Захотелось мне ужасного: сделать неявное преобразование из строки к перечислению в Scala. Почему ужасного? Да потому что чем больше неявных преобразований в реальном, большом проекте, тем тяжелее понять что же происходит на самом деле без отладчика. А отладчик, мало того что доступен далеко не всегда и если есть возможность обойтись без него, то она явно предпочтительнее. Но это все философия, далеко не все будут с ней согласны.
Post Category → Development
Поговорим об акторах
Лично мне очень нравится концепция акторов. Что интересно, познакомился я с ней куда раньше повальной моды на функциональщину, в году так 2003, когда начал плотно работать с библиотекой ACE (это та, которая The ADAPTIVE Communication Environment). Ну а сейчас акторами никого не удивишь, все про них только и говорят. И это хорошо, так как данная модель сильно упрощает отладку и разработку, при относительно не большой просадке по производительности и памяти.
В последнее время я присматриваюсь у относительно экзотическим языкам программирования, таким как Rust и Scala, а для обоих языков модель акторов является родной. При этом, на данный момент, Rust ничего не может предложить сравнимого с библиотекой AKKA, хотя даже в текущем своем состоянии его представление модели акторов не безынтересно. Continue reading
Задачи в Rust. Подводные камни.
Более-менее глубокой информации о задачах в Rust не много, поэтому, для того что бы разобраться в том, как же это работает, приходится экспериментировать “на кошках”. Посидев некоторое время за исходниками Rust, я открыл для себя много нового.
Самое неожиданное: на данный момент планировщиков задач аж два! Один старый, более функциональный, правда, на данный момент находящийся в довольно непотребном состоянии и новый, который еще не дописан. Подобная ситуация приводит к тому, что поведение задач, запущенных при помощи методов
Ну а после “оптимистичного” введения, небольшой рассказ о планировщиках Rust и том, как с ними работать на данный момент.
Старый планировщик
Если зайти на сайт Rust, то в разделе посвященном модулю Task можно найти информацию о ряде доступных типах планировщиков, заявленное поведение которых расходится с реальным: Continue reading
Работа с TrustedBSD на Mac OS X.
Моя первая, кривенькая и косенькая статья не только вышла в рамках RSDN Magazine #3 за 2012 год, но еще и появилась на сайте
Взаимодействие между задачами в Rust
Модель памяти Rust, в общем случае, не допускает совместного обращения к одной и той же памяти (shared model) предлагая вместо этого обмениваться сообщениями (mailbox model). При этом существует возможность работать с общей памятью в режимах “только для чтения” и “один писатель много читателей”. На данный момент в Rust существует несколько способов организации взаимодействия между задачами:
- Низкоуровневые каналы и порты из модуля core::comm;
- Высокоуровневая абстракция над каналами и портами std::comm;
- Каналы предназначенные для передачи бинарных данных из std::flatpipes;
- Новая инфраструктура для обмена сообщениями core::pipes.
Continue reading
Инициализация константных переменных
В Rust мне сразу понравилась возможность присваивать переменным результат оператора
let someFlag = false;
let value = if someFlag {
-1
} else {
1
};
io::println(fmt!("value == %d", value))
}
В случае же с C++, для того что бы инициализировать константную переменную приходилось вводить дополнительную функцию или, что чаще, плевать на константность и обходиться обычной переменной. Ну а с C++11 данная проблема решается куда проще
bool someFlag = false;
const auto value = [&]{
if(someFlag)
return -1;
else
return 1;
} ();
std::cout << "value == " << value << std::endl;
return 0;
}
Ну и конечно не забываем про то, что это C++11
P.S. подсмотренно у Саттера.
Как получить индекс в цикле for
Периодически Scala сильно удивляет как в хорошем смысле этого слова, так и в плохом. С циклом
for(index = 0; index < ....) {...}
std::cout << index; // и тут мы получили количество итераций
Да, обломс… Единственный найденный мной рабочий вариант выглядит следующим образом, что на мой взгляд очень и очень печально:
var index = 0
while (index < strings.length) {
index += 1
}
Но, зато наткнулся на куда более приятную возможность: получение индекса в процессе обработки коллекции без дополнительных приседаний:
for ((string, index) <- strings.zipWithIndex) {
println("Index: " + index + ", string: " + string)
}
Доступ к элементам кортежа
Да, несмотря на все плюсы модели памяти Rust, все же приходится признать – это для очень упорных программистов. В крайнем случае у меня создалось именно такое ощущение в процессе выяснения в “рассылке” как же мне написать функции доступа к элементам кортежа. В сам язык встроена следующая возможность:
...
let (l, r) = foo();
io::println(fmt!("Left value = %u, write value = %u", l, r));
В то же время, мне хотелось получить доступ к элементу кортежа без дополнительных “приседаний”, где-то так:
io::println(fmt!("Left value = %u, write value = %u",
pair._1(), pair._2()));
Effective C++11
Скотт Майерс выложил предварительное содержание будущей книги Effective C++11. Мне нравится, по большому счету, чтение самого содержания + немного размышлений довольно хорошо помогает понять C++11.
Moving from C++98 to C++11
- Prefer auto to explicit type declarations.
- Remember that auto + { expr } ⇒ std::initializer_list.
- Prefer nullptr to 0 and NULL.
- Prefer enum classes to enums.
- Prefer alias templates to typedefs.
- Declare overriding functions override.
- Distinguish () and {} when creating objects.
- Prefer emplacement to insertion.
- Declare functions noexcept whenever possible.
- Make const member functions thread-safe.
- Avoid std::enable_if in function signatures.
- Handle iterators where copying means moving.
Rvalue References, Move Semantics, and Perfect Forwarding
- Distinguish universal references from rvalue references.
- Avoid overloading on universal references.
- Pass and return rvalue references via std::move, universal references via std::forward.
- Assume that move operations are not present, not cheap, and not used.
- Be aware of perfect forwarding failure cases.
- Understand reference collapsing.
Secrets of the C++11 Threading API
- Thread construction may throw.
- Destroying a joinable thread calls terminate.
- Arguments passed to std::thread, std::async, and std::call_once are unconditionally copied.
- std::async is really two different functions with somewhat different APIs.
- Futures from std::async are special.
- void futures can be used for interthread communication.
- To poll a future, use wait_for with a zero timeout.
- Native handles let you go beyond the C++11 API.
- Clock adjustments affect _until functions.
Lambda Expressions
- Prefer lambdas to std::bind.
- Prefer lambdas to variadic arguments for threading functions
- Beware default captures in member functions.
Smart Pointers
- Use std::make_shared whenever possible.
- Prefer pass-by-ref-to-const to pass-by-value for std::shared_ptrs.
Miscellaneous
- Pass by value when you’ll copy your parameter.
- Keep abreast of standardization developments.
Монитор от Герба Саттера
В недавно вышедшем выступлении Герба Саттера посвященном многопоточности он привел интересный пример примитива для синхронного выполнения последовательностей операций. Сам Майерс окрестил детище “монитором”, по аналогии с мониторами из мира Java и C#, хотя на мой взгляд, сходства между ними не так уж и много. Суть задумки в том, что бы синхронизировать работу с каким-либо объектом, который изначально не поддерживает синхронизации. При этом, обеспечив синхронность не только на уровне одной операции, но и на уровне “трансзакции”.
vector<future<void>> v;
for(int i=0; i<5; ++i)
{
v.push_back(async([&,i]{
{
// необходимо сделать атомарно.
s += to_string(i) + " " + to_string(i);
s += "\n";
}
{
// так же необходимо сделать атомарно.
cout << s;
}
}));
}
В принципе, в приведенном выше примере вполне можно воспользоваться комбинацией mutex + lock_guard, но так не интересно, не красиво, да и о чем было бы рассказывать в течении полутора часов?
Решение предложенно действительно элегантное: Continue reading