четверг, 7 сентября 2017 г.

[prog.actors] Хотите решить задачу с помощью акторов? Спросите меня как! :)

После того, как мне довелось разным людям в разных местах рассказывать про Модель Акторов вообще и про SObjectizer в частности, сложилось впечатление, что продвижению Модели Акторов в массы препятствует две вещи:

  1. Отсутствие понимания того, что такое Модель Акторов вообще. Это, на самом-то деле, совсем не проблема. Очевидно, что всего на свете знать нельзя, а объяснить основные принципы работы Модели Акторов можно буквально на пальцах (что, кстати говоря, подтверждается практикой).
  2. Отсутствие понимания того, как эту самую Модель Акторов, принципы которой можно объяснить на пальцах, применить для решения практических задач.

Тут можно провести аналогию с молотком: понять как действовать молотком не сложно, не сложно и научить им бить. Гораздо сложнее разобраться куда же именно и с какой силой стукнуть молотком дабы получить нужный результат. Вот с Моделью Акторов, имхо, происходит тоже самое. Когда у человека есть конкретная задача, то не факт, что он может представить себе, как же ее решение будет выглядеть на акторах. Ибо понимать принцип -- это одно, а сделать декомпозицию конкретной задачи -- это другое.

В связи с этим предлагаю следующее: любой желающий может описать задачку или проблему, которую он бы хотел попробовать решить с помощью акторов. Ну или хотел бы увидеть, как ее можно было бы попробовать решить с помощью акторов. А я бы попробовал изложить свои соображения о том, можно ли это сделать, если можно, то как, какие будут достоинства и недостатки у этого решения и т.д. Условия задачи и предполагаемое решение (либо причины отсутствия такового) будут затем публиковаться в данном блоге.

Пример того, как это может выглядеть, у меня в блоге уже был: Интересная задачка на применимость модели акторов и акторных фреймворков.

Зачем это нужно мне? Очевидно, что мои цели исключительно корыстные ;) Прежде всего мне нужен материал, на основе которого можно было бы убедительно рассказывать людям о том, где применение Модели Акторов уместно, а где нет. Кстати говоря, неуместность применения Модели Акторов -- это актуальный вопрос. Бывает, что люди слушая про Модель Акторов теряют представление о том, что данная модель применима далеко не всегда. И хорошо бы уметь вовремя различать, где имеет смысл брать акторов, а где этого делать не нужно. Так же мне полезно прикидывать, насколько наш SObjectizer пригоден для решения тех или иных задач. Опыт показывает, что это сильно идет на пользу SObjectizer-у. А т.к. сам SObjectizer распространяется под BSD-лицензией (бездвоздме т.е. даром), то это пойдет на пользу и всем, кто воспользуется SObjectizer-ом.

Зачем это нужно вам? Ну мало ли :) Может вы хотите убедиться, какая все-таки гадость, эта ваша Модель Акторов, а вот вы молодец, когда придумали свое решение без использования акторов ;) Или может вы правда ломаете голову над чем-то и не прочь бы посоветоваться с умным человеком простофилей, который готов тратить свое время бесплатно. Ну или вам просто захотелось пообщаться ;)

В общем, если есть задачка и желание ее обсудить, то милости прошу. Описывайте свои задачки в комментариях к этой заметке (можно в G+), либо по почте eao197 на gmail тчк com, либо со мной можно связаться через FB, LinkedIn или Habrhabr.

PS. Запись специально повисит вверху до сентября. Но, если дело пойдет, можно будет заказать продление ;)

суббота, 19 августа 2017 г.

[prog.c++] Небольшое обновление библиотечки cpp_util

Есть у нас маленькая библиотека cpp_util, которая является небольшой коллекцией всяких мелких полезностей (часть из которых с развитием C++ теряет актуальность, но все-таки). Время от времени мы туда какие-то полезные мелочи добавляем.

Давеча была добавлена вспомогательная функция terminate_if_throws. Эта функция получает и вызывает переданную ей лямбда-функцию. Если лямбда бросает исключение, то автоматически вызывается std::terminate (поскольку сама terminate_if_throws помечена как noexcept).

Предназначена terminate_if_throws для того, чтобы в явном виде отмечать в коде куски, которые не должны бросать исключений в принципе. А если бросают, то нам не остается ничего другого, как прервать работу приложения, т.к. восстановиться мы уже не можем, а в каком состоянии остались наши данные -- не знаем.

Ну, например, допустим у нас есть какой-то архисложный класс some_complex_data и у него есть метод do_some_modification, который меняет состояние объекта. Мы хотим, чтобы do_some_modification обеспечивал строгую гарантию безопасности исключений: т.е. либо все изменения происходят успешно, либо нет никаких изменений.

Для этого в реализации do_some_modification будет, грубо говоря, три основных шага:

  1. Проверка всех необходимых условий. Если какое-то условие не выполняется, то может быть брошено исключение.
  2. Преаллоцация необходимых ресурсов для выполнения операции. Тут запросто может выскочить исключение, если каких-то ресурсов нет.
  3. Окончательная фиксация изменений.

Достаточно тяжело написать do_some_modification() так, чтобы выжить при возникновении исключений на третьем шаге. Гораздо проще, а потому и надежнее, сделать так, чтобы при возникновении исключения на третьем шаге тупо вызывать std::abort()/std::terminate(). Как раз для этого и предназначена terminate_if_throws: она явным образом выставляет в коде метку о том, что вот здесь мы никаких исключений не ждем в принципе, а если исключение таки случится, то пережить это мы не сможем:

#include <cpp_util_3/terminate_if_throws.hpp>
...
// We want to provide strong exception guarantee for that method.
void some_complex_data::do_some_modification(const params & p) {
  // Checks all necessary conditions first.
  // Some exceptions can be thrown here.
  check_condition_one(p);
  check_condition_two(p);
  ...
  // Preallocate some resources.
  // Exceptions are expected here. But this is not a problem
  // because there is no any actual state changes yet.
  auto r1 = preallocate_resource_one(p);
  auto r2 = preallocate_resource_two(p);
  ...
  // All preparations are done. We don't expect exceptions
  // in the following block of code. But if some exception is thrown
  // then we don't know how to repair from it.
  cpp_util_3::terminate_if_throws( [&] {
    do_state_change_action_one(...);
    do_state_change_action_two(...);
    ...
  } );
}

Так же в cpp_util был добавлен макрос CPP_UTIL_3_UNIX. Сейчас он определяется в cpp_util_3/detect_compiler.hpp если определен один из символов: unix, __unix или __unix__.

PS. В cpp_util вряд ли есть что-то уникальное. Наверняка в больших библиотеках, вроде Boost-а или Folly есть соответствующие аналоги. Но смысл cpp_util был как раз в том, чтобы в мелкие проекты не тянуть тяжелые зависимости масштаба Boost-а или Folly.

среда, 16 августа 2017 г.

[prog.bugs] Сделал, нашел и исправил любопытный баг в многопоточном коде :)

На днях сделал, а сегодня наткнулся и был вынужден искать любопытный баг, связанный с многопоточностью. Возможно, кому-то будет интересно, поэтому попробую рассказать.

Сценарий приблизительно такой:

  • нить №1 создает объект env;
  • на контексте нити №1 у объекта env вызывается метод start(). Внутри env.start() запускается цикл обработки событий Asio (т.е. вызывается asio::io_service::run()). По сути, env.start() вернет управление только когда завершится работа asio::io_service::run();
  • в одном из событий на контексте нити №1 создается нить №2. Ссылка на объект env передается в нить №2;
  • нить №2 какое-то время выполняет свои действия, после чего вызывает env.stop(). Внутри stop-а дается команда завершить цикл обработки событий Asio. Точнее говоря, внутри env.stop() выполняется ряд действий, одно из последних в котором -- это вызов asio::io_service::stop();
  • сразу после вызова env.stop() нить №2 завершает свою работу;
  • когда на нити №1 завершается env.start(), нить №1 разрушает объект env и дожидается завершения работы нити №2;
  • когда нить №2 завершается, завершается и работа нити №1.

Все это работало на реальном железе под Windows и gcc-5.2/vc-15.3. Но вот под Linux-ом внутри виртуалки начало падать. Не всегда, но довольно-таки регулярно.

Падало где-то между вызовом env.stop() на контексте нити №2 и сразу после возврата из env.start() на нити №1. Т.е. падало стабильно внутри нити №2 при вызове env.stop(), а нить №1 только что возвращалась из env.start().

Сразу стало очевидно, что это баг. Спустя какое-то время стало понятно, что баг происходит из-за того, что в нити №1 происходит возврат из env.start() и уничтожение env. А нить №2 все еще находится внутри env.stop(). Оставалось понять, как же так происходит, что ссылка на env внутри нити №2 перестает быть валидной прямо внутри вызова env.stop(), ведь вызов asio::io_service::stop() выполняется в самом конце и после этого вызова внутри env.stop() уже ничего не делается.

Метод env.stop() выполнял следующие шаги:

  • захватывал замок объекта env;
  • проверял, запустил ли кто-нибудь процедуру shutdown;
  • если процедура shutdown еще не запущена, то:
    • выставлял признак запуска процедуры shutdown;
    • освобождал замок объекта env;
    • выполнял ряд действий по освобождению выделенных ресурсов (эти действия должны были выполняться при освобожденном захвате объекта env);
    • вновь захватывал замок объекта;
    • проверял, все ли ресурсы освобождены (освобождение может выполниться сразу, а может занять какое-то время). Если все ресурсы освобождены, то вызывал asio::io_service::stop(). Если не все ресурсы освобождены, то просто завершал свою работу, т.к. после освобождения последнего ресурса env.stop() вызвал бы кто-то другой;
  • если же процедура shutdown была запущена, то:
    • проверял, все ли ресурсы освобождены (освобождение может выполниться сразу, а может занять какое-то время). Если все ресурсы освобождены, то вызывал asio::io_service::stop()
  • освобождал замок объекта env.

Проблема оказалась вот в чем: когда нить №2 начинает освобождать ресурсы, то все ресурсы могут быть освобождены сразу же. Как только это случается, просыпается нить №1, которая сама дергает stop() на своем контексте. Когда stop() вызывается на нити №1, то обнаруживается, что процедура shutdown запущена, все ресурсы освобождены. Поэтому вызывается asio::io_service::stop(), это приводит к возврату из asio::io_service::run(), а следом и к возврату из env.start(). А значит и к разрушению env.

Но в это время нить №2 все еще внутри env.stop(). Она как раз завершила освобождение всех ресурсов и пытается вновь захватить замок объекта env. Но к этому моменту объекта env уже нет, а значит и нет его замка. Поэтому тут-то и и возникает сегфолт.

В общем-то, ничего особенного. Нить №1 контролирует время жизни объекта env, а нить №2 пользуется этим объектом, не имея возможности как-то повлиять на время его жизни. Поэтому-то когда нить №1 уничтожает объект env, у нити №2 остается повисшая ссылка.

Любопытным этот баг делает то, что я почему-то посчитал, что метод env.stop() будет являться атомарным. Что на самом деле оказалось не так. Внутри env.stop() было "вложенное" освобождение и повторный захват замка объекта env. Как раз это вложенное освобождение и позволило нити №1 вклиниться в работу и совершить свои черные деяния. При этом вероятность того, что нить №1 окажется свободной от каких-то своих действий для того, чтобы сразу же среагировать на освобождение всех ресурсов, да так быстро, что нить №2 не успеет повторно захватить замок объекта, была очень низка. Что и показывали успешно проходившие под Windows тесты. Но вот под Linux-ом в виртуалке эта вероятность материализовалась. Причем достаточно стабильно. Так что тут мне изрядно повезло.

Посему повторюсь: многопоточное программирование на голых нитях и мутексах -- это пот, боль и кровь сложно. Не нужно такими вещами заниматься. Оставьте это занятие опытным мазохистам ;)

вторник, 8 августа 2017 г.

[prog.flame] Очень, очень не хватает массового исхода на Rust! Доколе, блин? ;)

Когда читаешь вот такое: "Писать серверного демона на С++ — это означает выгребать корки по утрам.", то возникают смешанные чувства. С одной стороны, не понимаешь, то ли это ты такой монстр, которому написать C++приложение не падающее с сегфолтами не проблема, а вокруг одни криворукие джамшуты (что маловероятно), то ли ты читаешь мнение человека с руками из жопы (что вряд ли возможно). То ли это человеку в 2005-ом в универе преподавали C++ образца 1995-го, поэтому он нормального C++ в глаза вообще никогда видел, а озвучивает лишь фобии, давно признанные правдой в кругах почитателей функциональщины. Ну ведь если ты упорот таким говном как Erlang-ом, то ведь все остальное должно же быть в принципе еще хуже, иначе как же жить дальше ? ;)

С другой стороны, C++ оброс уже таким количеством городских легенд и на профильных форумах у C++ уже давно такая дрянная репутация, что волей-неволей появляется желание сказать: да когда ж вы все уже свалите, наконец, на свой Rust? Жить же станет лучше, жизнь станет веселее. И, что характерно, работы прибавиться и тем, кто останется на C++, и тем, кто начнет писать на Rust.

Кстати говоря, если ты хорошо знаешь C++, не страдаешь фобиями по поводу шаблонов, исключений, STL-я и прочих возможностей нормального C++, то у тебя демон на C++ будет написан ну совсем не так, как такой же демон, но на C.

Там дальше еще фееричное от любителей Rust-а: Раст сейчас находится примерно на том же уровне развития, как C++ в начале-середине 90х. Тогда тоже куча школоло с горящими глазами хвалила кресты, обещала похоронить C, мудрые аксакалы говорили что не нужен. 10 лет и всё, C++ в мейнстриме.

Феерично. Просто потому, что в начале, не говоря уже про середину 90-х, C++ уже был самым, что ни есть, мейнстримом. И выбор тогда стоял достаточно просто: для прикладных вещей, где не требовалась производительность, использовались языки, вроде Visual Basic или SmallTalk. Для всего остального приходилось брать либо C++, либо Object Pascal, либо Ada. Ибо нужно было сочетать и борьбу со сложностью, и борьбу с потреблением ресурсов. Ну а C использовался где-то на системном и околосистемном уровне, да и то в начале 90-х уже началось проникновение C++ в нишу системного программирования.

И никому, кроме системщиков и железячников, C не был нужен до тех пор, пока мощности компьютеров не выросли настолько, что большие приложения начали разрабатывать на языках вроде Python, Ruby и JavaScript. Плюс пришел Web, великий и всепроникающий. Вот тогда и возродился интерес к чистому C из-за того, что узкие места для Python-а и Ruby нужно было на чем-то "расшивать", а C здесь оказался естественным выбором. Произошло это достаточно давно, лет десять тому как минимум. Посему и выросло поколение, которое всерьез считает, что C++ не нужен, т.к. писать можно на Python/Ruby/JS/Erlang, а там, где их производительности не хватает, достаточно plain old С.

И вот эти самые люди сейчас радостно кричат Rust, Rust, Rust! Как будто Rust способен серьезно расширить ту самую нишу, в которой все еще теплится какая-то жизнь для C++, и которая с каждым годом скукоживается. Ну-ну, ну-ну. Хочется верить, что я очень сильно не прав.

Посему призываю активных и латентных Rust-оманов: покажите же нам, замшелым ретроградам-C++никам, кузькину мать! Даешь Rust в продакшен! Больше программ на Rust-е, хороших и разных! Больше рабочих мест для Rust-разработчиков! Зарплата Rust-омана должна быть на 20% выше, чем в среднем по отрасли! Каждому Rust-оману по бесплатному смузи и полугодовой абонемент в ближайший барбершоп (чтобы не значили эти слова)... ;)

PS. Для желающих воспринять этот текст слишком всерьез: обратите внимание на слово Humour в списке тегов. Ну и на смайлик в заголовке поста.

среда, 2 августа 2017 г.

[prog.flame] Не нужно отождествлять и увязывать друг с другом Actor Model и Reactive Manifesto

В последние годы модель акторов переживает очередную волну интереса со стороны разработчиков. С одной стороны -- это хорошо, т.к. при разумном применении в подходящих для этого нишах, модель акторов позволяет сократить сложность и стоимость разработки. С другой стороны, любой хайп, любая мода на что-нибудь, ведет к образованию большого количества "пены", которая скрывает суть вещей. Применительно к разработке софта это означает появление некоторого числа "евангелистов", проталкивающих модель акторов в массы, но при этом имеющих смутное представление о том, что же это такое.

Еще более усугубляет дело наличие материально заинтересованных "евангелистов", которым кровно необходимо продать свой продукт и/или услуги, построеные вокруг модели акторов. В частности, таковыми являются товарищи из небезызвестной в узких кругах конторы Lightbend. Эти товарищи стоят, в частности, за разработкой фреймворка Akka. Они же приложили руку и к небезызвестному в тех же кругах Reactive Manifesto. И все бы ничего, но продавцы услуг из Lightbend-а очень плотно увязывают Actor Model и Reactive Manifesto в своих маркетинговых материалах.

В частности, недавно они выпустили white-paper под названием "Modernization: The End Of The Heavyweight Era. Why Actor Model Matters for Could Infrastructure". В котором повествование идет приблизительно в таком ключе: разработка больших монолитных приложений на Java EE -- это отстой, прошлый век и в приличных домах Ландона и Парижу вспоминать о таком уже не комильфо. Сейчас, как-никак, XXI-ый век на дворе, поэтому есть Reactive Manifesto, а значит ваши волосы будут мягкими и пушистыми приложения должны быть отзывчивыми, эластичными и устойчивыми. Для чего вам всего лишь нужно использовать не просто модель акторов вообще, а конкретно Akka от Lightbend-а.

Причем описано все это вполне серьезно, красиво и убедительно. Можно только поучиться тому, как следует писать маркетиновые материалы, наполняя их технической шелухой для придания солидности. Однако, есть две проблемы.

вторник, 1 августа 2017 г.

[life.cinema] Очередной кинообзор (2017/07)

Подошло время очередного кинообзора. В начале списка идут фильмы, которые понравились мне больше, в конце -- которые понравились меньше. Список на этот раз небольшой, т.к. в этом месяце возможностей смотреть кино в силу объективных причин было гораздо меньше, чем обычно.


Дюнкерк (Dunkirk, 2017). Снято, конечно, мастерски. Однако, сама история не цепляет. И к основным персонажам не проникаешься ни симпатией, ни сочувствием. Может разве что за исключением героя Тома Харди.


Большой (2017). Не могу сказать, что сюжет меня зацепил. Но снято прилично, интересно смотреть, как развиваются события на экране.


Четверо против банка (Vier gegen die Bank, 2016). Немецкая криминальная комедия. Немного примитивная, немного укуренная, немного пошлая, но в целом мне понравилось.


Уйти красиво (Going in Style, 2017). Старички подсобрались и выдали пусть и средненькую, но добротную криминальную почти что комедию. Посмотрел с удовольствием.


Взрывная блондинка (Atomic Blonde, 2017). Фильм должен понравиться фанатам Шарлиз Терон. Но т.к. я не фанат, то в паузах между экшен-сценами мне было откровенно скучно. В самих экшен-сценах, как по мне, было заметно, что несмотря на проделанную работу, Шарлиз Терон -- это не Джина Карано и не Синтия Ротрок (если кто помнит). Правда, ситуацию отчасти спасал саунд-трек из хитовых композиций конца 1980-х. Например, на фрагменте из Depeche Mode я был готов простить фильму все, но и Depeche Mode оказалось слишком мало :)


Выстрел в пустоту (Shot Caller, 2017). Сильно подзатянутая криминальна драма. Мне смотреть было интересно, поскольку от трейлера у меня сложились совсем другие ожидания от сюжета. Но в самом фильме все пошло совсем по-другому, поэтому хотелось досмотреть до финала. Тем не менее некоторую неспешность и затянутость нельзя не отметить.


2:22 (2017). Фильм красивый и снятый хорошо. Но не цепляющий.


Орбита 9 (Orbita 9, 2017). Ожидал большего. Посмотреть можно хотя бы потому, что европейцы снимают кино не так, как американцы, тем более фантастику. Но не шедевр, к сожалению.

Охранник (Security, 2016). Фильм откровенно разочаровал. Как туда вписались Антонио Бандерас и Бен Кингсли -- не понятно.

понедельник, 31 июля 2017 г.

[life.sport.darts] Послесловие к World Matchplay 2017

Ну и послесловие к завершившемуся вчера PDC-шному World Matchplay. Который, как мне кажется, войдет в историю. Ибо то, что сотворил там Фил "Power" Тейлор, -- это просто спорт как он есть.

воскресенье, 30 июля 2017 г.

[life.photo] Мысли на тему хорошей (для меня) travel-камеры

Перед поездкой в Питер был вынужден решать архиважный и архисложный для себя вопрос: с какой камерой и оптикой ехать? В наличии была большая, чОрная и тяжелая зеркалка от Nikon-а (куча мегапикселей, широченный динамический диапазон, рабочие ISO вплоть до 6400) и разнообразной оптикой (в основном большой, чОрной и тяжелой). И маленькая беззеркалка Fujifilm x30 с 12-тью мегапикселями, рабочими ISO не выше 400, никаким динамическим диапазоном, кроп-фактором x7 и встроенным зум-объективом 28-112mm в эквиваленте со светосилой 2.2-2.8 (которую так же нужно пересчитывать с учетом кроп-фактора).

Казалось бы, по техническим параметрам чОрная зеркалка должна была бы выиграть. Но, кроме качества изображения нужно было принимать в расчет еще несколько далеко немаловажных для путешествия факторов, как то: размер, вес и удобство использования. Причем путешествия не в дикую природу, где ты имеешь возможность остановиться в уединенном месте и снимать понравившийся тебе пейзаж без каких-либо помех часами. А в один из самых посещаемых в России городов, в некоторых местах которого количество туристов на квадратный метр прям таки зашкаливает.

четверг, 27 июля 2017 г.

[prog.c++] Принимаются заказы на тему следующей статьи для Хабра

Отпуск закончился и пришло время возвращаться к работе. В планах подготовка очередных релизов SObjectizer-5.5 и so_5_extra, а так же подготовка доклада для C++ CoreHard Autumn 2017. При этом есть возможность выделить 1.5-2 недели для написания очередной статьи для Хабра на тему акторов (вообще) и SObjectizer-а (в частности).

Посему принимаются заявки: о чем бы вам хотелось прочитать?

Сразу хочу предупредить, что статьи получаются не самые маленькие и довольно-таки скучные, хотя на их качество вроде пока никто не жаловался (примеры можно посмотреть здесь).

На данный момент есть одна тема, которая может считаться актуальной, т.к. она всплыла в комментариях к одной из предыдущих статей на Хабре. Речь о том, как же делать stage agents для того, чтобы использовать SEDA way. Немного на эту тему говорилось здесь, но напомню суть. Допустим, нам нужно выполнить операцию, которая состоит из шагов 1, 2 и 3. Мы можем создать актора для выполнения этой операции, который сам, последовательно, будет делать шаги 1, 2 и 3. Если нам нужно одновременно обслуживать 100500 таких операций, то и акторов в программе одновременно будет жить 100500. Что имеет свои негативные последствия. Либо мы можем сделать всего трех акторов: первый будет делать шаг 1, второй -- шаг 2, третий -- шаг 3. При этом каждый актор может одновременно выполнять свой шаг для 100500 параллельных операций. Как оказалось, такая краткая формулировка оставляет у читателей белые пятна в понимании, поэтому более подробный рассказ об особенностях реализации SEDA way на акторах в виде статьи может быть интересен кому-нибудь.

Но, если кто-то из читателей озвучит более интересную тему или задаст интересный/сложный вопрос, то тему можно будет и поменять.

Поэтому прошу оставлять свои соображения в комментариях, или же можно прислать их мне на почту: eao197 на stiffstream com.

среда, 26 июля 2017 г.

[life] Братская могила №142

Этот пост будет о личном, о части истории моей семьи. Хотя подобные истории совсем не уникальны для бывшего СССР.

Мой дед по отцовской линии погиб в Ленинграде зимой 1941-го года. Когда он ушел на фронт, моему отцу не было и трех лет. Соответственно, мой отец своего отца даже не помнил. А могилу смог посетить только в 1988-ом году.

В похоронке на деда было указано, что он захоронен на Пискаревском кладбище в траншее с таким-то номером. Когда отец в начале 60-х годов служил в Ленинграде, он пытался найти эту траншею, но тогда шла реконструкция кладбища и узнать где же именно находится захоронение не удалось. А в следующий раз отец смог приехать в Ленинград лишь 25 лет спустя, взяв с собой свою семью.

Тогда в архиве кладбища нашли информацию и сказали номер братской могилы. Мы нашли эту могилу. До недавних пор я думал, что помню этот номер.

Прошло еще 29 лет и вот уже я сам привез свою семью в Ленинград. И пришли на Пискаревское кладбище, на могилу к моему деду, которого даже мой отец совсем не помнил, ну а я родился через 32 года после его смерти.

И тут выяснилось, что я помню не тот номер. Приблизительное место помню, а точный номер -- нет.

Обратился в архив. И по мере ответов на вопросы архивариуса поймал себя вот на чем: дед призывался из под Твери, погиб он в Ленинграде, разыскивает его могилу внук из Гомеля. Воевала вся страна -- так нам рассказывали в детстве. Воевала вся страна, осознал я в тот момент, когда отвечал на вопросы архивариуса.

Информацию подняли достаточно быстро. Охотников Иван Андреевич 1909-го года рождения, умер 25-го декабря 1941-го года, захоронен в братской могиле №142.

На самом деле на написание этого поста подтолкнуло вот что: в обычной жизни может казаться, что Великая Отечественная Война -- это уже далекая история, больше 70 лет после Победы прошло. Но вот на Пискаревском кладбище понимаешь, что нет, не далекая. Что есть практически осязаемая ниточка от моего деда к моему отцу, от моего отца ко мне, от меня... На Пискаревке ты это ощущаешь. Там таких ниточек с полмиллиона.

Полмиллиона. И часть из них оборванны навсегда.

И было это совсем, совсем недавно.

пятница, 14 июля 2017 г.

[prog.c++14] so_5_extra-1.0.1 и so-5.5.19.3

Сегодня мы выкатили очередную версию so_5_extra -- 1.0.1, а накануне обновили SObjectizer до версии 5.5.19.3. Доступно все, как обычно, на SourceForge: здесь so_5_extra, здесь SObjectizer.

В so_5_extra-1.0.1 добавлена всего одна фича: это collecting_mbox.

Смысл collecting_mbox в том, что иногда агенту X нужно получить строго N сообщений типа M от других агентов. Ну вот, например, стартует родительский агент-менеджер и создает пять нужных ему дочерних коопераций. Каждая дочерняя кооперация тратит какое-то время на свои начальные действия (например, загружается конфигурация, устанавливается подключение к БД, инициализируется внешнее устройство и т.д.), после чего отсылает родителю сигнал о том, что дочерняя кооперация готова. Соответственно, родительскому агенту нужно получить все пять сигналов о готовности дочерних коопераций, лишь после этого родительский агент сможет продолжать работать.

понедельник, 10 июля 2017 г.

[prog.c++14] Развернуть std::tuple в вызов конструктора базового класса

Давеча упоролся шаблонами настолько, что потребовалось сделать на C++ что-то вот такое (проще сперва показать на примере, а уже потом рассказывать словами):

templatetypename First_base, typename Second_base >
class Some_complex_template
   : public First_base // Boom #1
   , public Second_base // Boom #2
{
public :
   // Constructor.
   templatetypename... First_base_args, typename... Second_base_args >
   Some_complex_template(
      First_base_args &&...first_args, // Args for the first base class.
      Second_base_args &&...second_args ) // Args for the second base class.
      : First_base{ std::forward<First_base_args>(first_args)... }
      , Second_base{ std::forward<Second_base_args>(second_args)... }
      {}
   ...
};

Т.е. нужно было отнаследовать шаблонный класс Some_complex_template от двух других классов, которые задаются параметрами шаблона. А затем в конструктор Some_complex_template нужно было передать два независимых друг от друга набора параметров. Первый набор параметров должен уйти в конструктор первого базового класса, второй набор -- в конструктор второго базового класса.

Насколько я понимаю, C++ не позволяет написать фукнцию/метод с переменным количеством параметров вот так: f(First &&...first, Second &&...second), что логично, т.к. при вызове f(a1, a2, a3, a4, a5) невозможно понять, что из a(i) относится к first, а что к second.

Поэтому выход из ситуации сейчас ищется в использовании std::tuple вот в таком сценарии:

templatetypename First_base, typename Second_base >
class Some_complex_template
   : public First_base
   , public Second_base
{
public :
   // Constructor.
   templatetypename... First_base_args, typename... Second_base_args >
   Some_complex_template(
      std::tuple<First_base_args...> && first_args, // Args for the first base class.
      std::tuple<Second_base_args...> && second_args ) // Args for the second base class.
      : First_base{ /*some magic is required here!*/(first_args)... }
      , Second_base{ /*some magic is required here!*/(second_args)... }
      {}
   ...
};

Но вот тут возникает вопрос, как же распаковать содержимое std::tuple в вызов конструктора базового типа?

В принципе, вся эта магия с распаковкой std::tuple в вызов некоторой функции f хорошо проиллюстрирована на том же cppreferece.com в описании возможной реализации функции std::apply. Но есть нюанс: нам нужно сделать вызов конструктора базового типа, поэтому у нас нет возможности заводить вспомогательные функции, вроде apply_impl, в которые передается дополнительный аргумент std::index_sequence...

Или все-таки есть?

пятница, 7 июля 2017 г.

[prog.c++] Программирование на шаблонах плохо дружит с программированием на бумаге

Думаю, что старые читатели блога знают, что я большой поклонник программирования на бумаге. Этому здесь было посвящено несколько заметок, начиная с одной и самых старых. И я, по прежнему активно мараю бумагу перед тем, как сделать что-нибудь новое и/или важное.

Однако, если серьезно упороться C++ными шаблонами, то пользы от предварительного программирования на бумаге оказывается заметно меньше. Думаю, это потому, что C++ я знаю не очень хорошо, поэтому не могу на бумаге написать сложный код с шаблонами так, чтобы затем не пришлось его сильно переделывать из-за того, что компилятор на тему использования шаблонов думает совсем по-другому :( Из-за этого намного проще писать код прямо в Vim-е, пытаться его компилировать, сразу же исправлять те места, по которым у меня с компилятором (и со стандартном) обнаруживаются принципиальные расхождения. Затем все повторяется снова и снова, пока я не получаю то, что мне нужно, причем так, чтобы это компилировалось и VC++, и Gcc, и clang.

Однако, от того, что код начинает появляться прямо сидя за компьютером, зачастую я не знаю, как именно будет использовать какая-то новая сущность. Поэтому на начальном этапе настоящая беда и комментариями, и с самими наименованиями в программе. А для того, чтобы все это можно было со временем доработать напильником и довести до приличного состояния, код щедро размечается тегами FIXME. И выглядит это приблизительно вот так:

template<
   typename COLLECTING_MSG,
   typename TRAITS = runtime_size_traits_t,
   typename LOCK_TYPE = std::mutex >
//FIXME: should it be final or not?
class mbox_template_t final
   : public ::so_5::abstract_message_box_t
   , protected TRAITS::size_specific_base_type
   , protected ::so_5::details::lock_holder_detector< LOCK_TYPE >::type
   {
      using specific_base_type = typename TRAITS::size_specific_base_type;

      //FIXME: document this!
      using config_type = details::config_type< COLLECTING_MSG, TRAITS, LOCK_TYPE >;

      //FIXME: document this!
      using messages_collected_builder_t = typename
            details::collected_bunch_type_selector< config_type >::builder_type;
   public :
      //FIXME: document this!
      using messages_collected_t = typename 
            details::collected_bunch_type_selector< config_type >::message_type;

   private :
      //FIXME: document this!
      using messages_collected_subscription_type = typename
         std::conditional<
               is_mutable_message< COLLECTING_MSG >::value,
               mutable_msg< messages_collected_t >,
               messages_collected_t >
            ::type;

   public :
      //FIXME: what about message tracing traits?
      templatetypename... ARGS >
      mbox_template_t(
         mbox_id_t mbox_id,
         ARGS &&... args )
         :  specific_base_type( mbox_id, std::forward<ARGS>(args)... )
         {
            details::check_mutability_validity_for_target_mbox<config_type>(
                  this->m_target );
         }

PS. Ну и еще наброшу на тему C++ных шаблонов. Когда ими реально упарываешься, то начинаешь совсем по другому относиться к спорам на тему того, что вместо C++ можно использовать plain old C или Rust. Программирование на шаблонах C++ -- это, блин, совсем другой подход к программированию. Ни Rust, ни, тем более, C, тут и рядом не стояли. Т.е. сделать одно и тоже можно будет на разных языках, но это будут совершенно разные решения, абсолютно не похожие на друг друга.

вторник, 4 июля 2017 г.

[prog.c++] Новая большая статья про SObjectizer на Хабре

Мы сделали разбор штатного примера machine_control из дистрибутива SObjectizer в виде большой статьи на Хабре: Имитируем управление устройствами с помощью акторов. По ходу дела была мысль вместо одной статьи сделать две, а может и три поменьше. Но по опыту выходит, что каждую следующую читают меньше, чем предыдущие, поэтому решено было ограничиться всего одной.

Статья получилась большой, материала в ней много. Но старались сделать доступной и понятной. Если что, то с удовольствием ответим на вопросы в комментариях.

Темы для следующих статей принимаются :)

PS. Интересное обсуждение завязалось в комментариях к одной из предыдущих статей. При использовании агентов в виде конечных автоматов могут возникать некоторые не очевидные моменты, с которыми, однако нужно считаться. Поскольку эти моменты появились не просто так.

суббота, 1 июля 2017 г.

[life.cinema] Очередной кинообзор (2017/06)

Незаметно пролетел июнь и пришло время очередного кинообзора. Традиционно в начале списка идут фильмы, которые понравились больше. Ну а замыкают список те фильмы, которые не понравились.


Таинственный город Z (The Lost City of Z, 2016). Хороший фильм, мне понравился. Может быть в каких-то моментах он был слегка нудноват и не динамичен. Но это итоговое впечатление не испортило. Тем не менее, подозреваю, что сама по себе история про увлеченного путешественника будет интересна не всем.

Меч короля Артура (King Arthur: Legend of the Sword, 2017). Качественно снятое фэнтези. Гай Ричи не подкачал. Перед просмотром у меня было скептическое отношение из-за негативных отзывов о фильме, которые доводилось читать. Но для своего жанра получилось весьма достойно.

Мумия (The Mummy, 2017). Хороший развлекательный аттракцион. Правда, я лично не понял двух вещей. Во-первых, зачем там Том Круз в главной роли. Ни он в фильм ничего не привнес, ни фильм ему ничего не дал. От участия Круза ждешь чего-то большего, но здесь ничего "эдакого" не было. Во-вторых, зачем нужно было сюда вплетать еще и историю Джекила и Хайда. В общем, сходить и развлечься можно, но не более того.

Киллер поневоле (Un petit boulot, 2016). Понравилось. Вот просто понравилось и все.

Эксперимент "Офис" (The Belko Experiment, 2017). Вообще такого рода фильмы мне нравятся. Здесь же явно ощущалось, что бюджет у картины был более чем скромным, однако несколько постаревших актеров, чей звездный час уже давно в прошлом, смогли удержать фильм на вполне себе достойном уровне.

Жмот (Radin!, 2016). Любопытный фильм. Как комедия он меня не сильно зацепил (хотя и придраться не к чему). Но зато показалось, что Дэни Бун очень хочет сменить амплуа комика на амплуа серьезного драматического актера. И что таланта для этого у него вполне может хватить.

Ограбление в Хаттон-Гарден (The Hatton Garden Job, 2017). Сразу скажу, что снято так себе. Но есть в нем какой-то английский стиль, который добавляет фильму какого-то непонятного очарования и заставляет смотреть даже не смотря на неторопливое развитие событий и отсутствие каких-то эффектных сцен.

Его собачье дело (Once Upon a Time in Venice, 2017). Не шедевр, но посмотреть вполне можно, особенно если шутки "пониже пояса" не смущают.

Возьми меня штурмом (Raid dingue, 2016). Типичная французская комедия. Сильно предсказуемая, местами смешная. Мне особенно понравилось то, что Дэни Бун вообще не кривлялся, и то, как Иван Атталь сыграл эксцентричного злодея.

Калифорнийский дорожный патруль (CHIPS, 2017). Обычная американская комедия. Не шедевр, но и не полный шлак. Можно посмотреть, но и ждать чего-то выдающегося не стоит.

Трансформеры: Последний рыцарь (Transformers: The Last Knight, 2017). Все деньги на экране и это видно, к визуальной составляющей вообще никаких претензий. Но вот все остальное находится где-то по ту сторону добра и зла. Такое ощущение, что Майкла Бэйя настолько утомила вся эта история с трансформерами, что он сам для себя решил: "Ну раз вы просите, то я вам сниму такое, что вы сами не будете рады, что упросили меня." Вот он и снял так, что смотреть это могут разве что дети до 10 лет.

Еще я тут пересмотрел фильм "Эверест" от 2015-го года. Как-то в этот раз он зашел гораздо лучше. Может быть потому, что в этот раз я уже не обращал внимания на то, как снято. Зато следил за тем, о чем рассказывает кино. В итоге поменял свое мнение о фильме на гораздо более лучшее. Достойное кино, как по мне.

вторник, 27 июня 2017 г.

[prog.c++] Практически динамически-типизированное программирование

Давеча, занимаясь примером для демонстрации Asio-инфраструктуры для SObjectizer из нового проекта so_5_extra, написал фрагмент C++кода, в котором практически не фигурировали имена конкретных типов. Буквально возникло впечатление, что программирую на каком-то динамически-типизированном языке (правда, с излишне многословным синтаксисом). Кому интересно посмотреть немного C++14-того хардкора милости прошу под кат.

понедельник, 26 июня 2017 г.

[prog.c++] so_5_extra-1.0.0 и so-5.5.19.2

Мы выпустили первую версию своего нового проекта поверх SObjectizer -- so_5_extra версии 1.0.0.

В этой версии в so_5_extra доступны:

  • so_5::extra::env_infrastructures::asio::simple_not_mtsafe -- реализация однопоточной инфраструктуры SObjectizer-а на базе Asio. Т.е. с использованием этой инфраструктуры и Asio, и SObjectizer смогут работать на одной и той же рабочей нити;
  • so_5::extra::mboxes::round_robin -- специальный mbox, который доставляет сообщения поочередно каждому из N агентов, подписанных на это сообщение;
  • so_5::extra::shutdowner -- небольшой инструмент для упрощения операции завершения работы в больших приложениях.

Исходники можно взять либо из репозитория, либо загрузить из соответствующего раздела.

Документацию по проекту можно найти в Wiki. Если из документации чего-то не понятно или что-то в ней не описано, то не сочтите за труд, дайте нам знать. Улучшим, расширим и углубим :)

Проект header-only. Если захочется собрать тесты и примеры самостоятельно, то придется воспользоваться Ruby и Mxx_ru. Зависимости так же подтягиваются через MxxRu::externals. Но в секции Files есть архивы с именами вида so_5_extra-1.0.0-full.tar.xz, в которых уже все зависимости присутствуют. Поэтому можно брать *-full.tar.xz архив, распаковывать, прописывать в INCLUDE путь к so_5_extra-1.0.0/dev и пробовать.

Работоспособность проверялась под Linux-ом (gcc 5.4 и 7.1, clang 3.7 и 4.8) и Windows (gcc 5.2-7.1, VC++ 14.0 и 15.0). На всякий случай выставлять -Werror при работе с so_5_extra не советуем, т.к. и gcc, и clang очень сильно ругаются на потроха Asio.

В планах у нас добавление еще нескольких фич в so_5_extra. Следующие версии будут выходить по мере добавления новых фич. В том числе в планах и simple_mtsafe-инфраструктура для Asio, но приоритет у этой задачи не самый высокий. Если кому-то нужна thread-safe реализация Asio-инфраструктуры для SO-5, то дайте знать. Постараемся повысить приоритет.

Обращаем внимание, что so_5_extra распространяется под двойной лицензией: GNU Affero GPL для OpenSource применения, и коммерческая лицензия для использования в закрытых проектах. Если кому-то интересна коммерческая лицензия, то пишите на info at stiffstream dot com, там цена вопроса порядка $40 за одного разработчика в год.

Попутно мы сделали SObjectizer-5.5.19.2, в который вошло несколько фич, необходимых для реализации so_5_extra. Дистрибутивы SObjectizer лежат там же, где и обычно.

пятница, 23 июня 2017 г.

[prog.c++.flame] Взять бы, да и закрыть Boost...

Иногда мне кажется, что для того, чтобы дать хороший и мощный пинок для дальнейшего развития C++, нужно взять и закрыть Boost.

Вот просто взять и закрыть. Что вошло в Boost, то пусть там и остается.

А вот всем остальным нужно учиться жить без Boost-а.

Поскольку языку программирования нужна не одна мегабиблиотека, в которую пытаются впихнуть все. А нормальный. Повторяю, нормальный, Карл!, способ подтаскивания в свой проект сторонних библиотек. Но пока есть Boost, и пока попадание в Boost является целью для многих разработчиков библиотек, эволюция и селекция нормальных способов управления зависимостями в C++ идет черепашьими темпами.

Да и смысл Boost-а в настоящее время от меня ускользает. 17 лет назад Boost был полигоном для обкатки того, что хотелось бы включить в стандарт. Boost.Thread, Boost.SharedPtr, Boost.Optional и все такое. Ну OK, тогда это было актуально, когда работа над стандартами C++ велась не слишком быстро.

Но сейчас-то? Какой смысл в Boost-е кроме как в централизованной сборке относительно небольшого количества C++ных библиотек, которые совместно тестируется перед релизом? Ну кроме протекционизма?

Кстати о протекционизме. Вот есть в Boost-е библиотеки Boost.Test, Boost.Log, Boost.Format. Для которых существуют не менее достойные, мягко говоря, аналоги в виде Catch, Spdlog и Fmtlib. С какой стати старые и неудобные монстры, вроде Boost.Test и Boost.Log, должны иметь преференции в современном мире перед теми библиотеками, которые в Boost не входят? Ведь не так уж и редко от сторонних аналогов люди отказываются потому, что дальше Boost-а и не заглядывают. Но даже если и заглядывают, то иногда предпочитают Boost, потому что Boost к проекту уже подключен, а подтаскивание еще одной библиотеки в C++ный проект -- это боль для многих.

В общем, Карфаген должен быть разрушен Boost сделал свое дело, Boost должен уйти.

PS. Пост написан под влиянием боли, которая появляется, когда в зависимостях кросс-платформенного проекта оказывается достаточно свежая версия Boost-а.

четверг, 22 июня 2017 г.

[prog.c++] Если разрабатывать REST-сервисы на C++, то как должен выглядеть "инструмент мечты"?

Допустим, вам нужно разработать RESTful сервис на C++. Не будем сейчас вступать в религиозные споры на тему того, зачем, когда и кому в здравом уме это может потребоваться. Ну мало ли ;) Может нужно выставить REST API для старого легаси-кода, которому 20 лет и который никто с C++ переписывать не будет. Может нужна высокая эффективность и низкое потребление ресурсов. Может нужно сделать это для военки на Эльбрусах какой-то экзотической платформы, для которой нормального качества компиляторы есть только для C и C++. Может еще что-то, включая упоротость и мазохизм :)

Итак, вы стоите перед выбором, как же сделать REST API на C++. В какую сторону вы будете смотреть? Напишете все сами или постараетесь взять что-то готовое?

Если что-то готовое, то выберете что-то уже известное и опробованное, или же составите список требований и будете выбирать из существующих решений на предмет соответствия вашему списку?

Если будете выбирать согласно своих требований, то что вы в свой список требований включите обязательно? Что будет однозначно положительными факторами в пользу стороннего решения? А что однозначно отрицательным?

Может вы уже используете что-то и вас в этом что-то сильно не устраивает? Что?

Понимаю, что вопросов много. Но почему бы не пофантазировать и не пообщаться о том, как оно должно было бы быть в идеальном мире C++? :)

понедельник, 19 июня 2017 г.

[prog] Проблема нумерации: so_5_extra-5.5.19-1.0 или so_5_extra-1.0-5.5.19 или...?

В очередной раз столкнулся с одной из неразрешимых проблем в программировании: выборе названия :) Точнее, не названия, а системы нумерации, но это даже сложнее :(

У нас на финальную стадию подготовки к релизу вышла дополнительная библиотека для SObjectizer-а под названием so_5_extra. Сейчас в нее входят три компонента: однопоточный вариант SObjectizer Environment на базе Asio (немного об этом здесь), готовая реализация round-robin mbox-а и специальная штука под названием shutdowner, которая предназначена для обработки некоторых сценариев завершения больших приложений. Со временем состав so_5_extra будет еще расширяться, но для первого релиза нам показалось достаточно этих трех. Все это уже работает и протестировано, но нужно еще написать какую-то вводно-обзорную документацию.

Неожиданной сложностью оказался выбор системы нумерации версий для so_5_extra. Традиционный semver, вроде so_5_extra-1.0.0, здесь не очень подходит потому, что версии so_5_extra планируется плотно синхронизировать с версиями SObjectizer-а. Поскольку so_5_extra в своей реализации очень активно использует знание того, что и как выполняется в конкретной версии SObjectizer. Поэтому версия so_5_extra-1.0.0, которая была сделана для SObjectizer-5.5.19, не обязательно будет работать с SObjectizer-5.5.18 или SObjectizer-5.5.20. В обратном направлении, кстати, тоже работает. SObjectizer так же допиливается под нужды so_5_extra. Например, вместе с первой версией so_5_extra мы опубликуем и SObjectizer-5.5.19.2, в который добавлено несколько фич для работы so_5_extra.

Поэтому хочется иметь какую-то систему нумерации, в которой номера версий so_5_extra были каким-то боком увязаны друг с другом. Но, при этом, чтобы допускалась некоторая свобода. Например, может быть so_5_extra-1.0.0, которая выпущена для SO-5.5.19.2, но затем появляется SO-5.5.19.3, из-за которой менять номер для so_5_extra не нужно. С другой стороны, может быть so_5_extra-1.0.1, ради которой не нужно менять номер версии SO-5.5.19.2.

В результате я сейчас всерьез рассматриваю следующие варианты системы нумерации:

  1. so_5_extra-5.5.19-1.0. С тем, чтобы впоследствии можно было использовать номера вида so_5_extra-5.5.19-1.0.12, so_5_extra-5.5.19-1.2.39, so_5_extra-5.5.20-1.0 и т.д. Тут, правда, возникает вопрос: как между собой соотносятся, скажем so_5_extra-5.5.19-1.2.39 и so_5_extra-5.5.20-1.0.0? На который у меня пока ответа нет;
  2. so_5_extra-1.0-5.5.19. С последующим развитием в сторону so_5_extra-1.2.39-5.5.19, so_5_extra-2.0-5.5.19 и т.д.
  3. Просто тупо совместить нумерацию so-5 и so_5_extra. Т.е. если есть so-5.5.19.2, то и есть so_5_extra-5.5.19.2. Если есть so_5_extra-5.5.20.34, то есть и so-5.5.20.34. Хотя этот вариант мне нравится меньше всего.

Посему вопрос к читателям: что вас меньше испугает и запутает? so_5_extra-5.5.19-1.0.0 или so_5_extra-1.0.0-5.5.19?

понедельник, 12 июня 2017 г.

[business.book] Кратко о книге "Великие по собственному выбору" Джима Коллинза и Мортена Хансена

Закончил читать книгу "Великие по собственному выбору". Один из авторов которой, Джим Коллинз, отметился серией бестселлеров "Построенные навечно" (мне не понравилось), "От хорошего к великому" (не читал), "Как гибнут великие" (хотел бы прочесть, но нигде не продают ее электронную версию). Теперь вот следующая книга в этой серии, на этот раз в соавторстве с Мортеном Хансеном.

Первые страниц 200 занимает обсуждение результата анализа нескольких компаний, разделенных на пары. Первой компанией в паре была компания, показавшая до 2002-го года результат, в 10 раз превосходящий средний по своей отрасли. Второй в паре была компания, которая очень похожа по размерам, истории и целям, но которая не смогла добиться успеха в тех же самых временных рамках.

Авторы книги утверждают, что они обнаружили несколько общих черт, которые присущи выдающимся компаниям (которые в книге называются 10-кратники). Несколько общих принципов, которые, по мнению авторов книги, привели компании 10-кратники к тем самым выдающимся результатам. Соответственно, на первых 200-х страницах обсуждаются эти принципы, то по отдельности, то вместе.

Завершающие 100 страниц -- это рассказ о том, как само исследование проводилось. Эту часть книги я не читал, только слегка просмотрел. Как по мне, завершающая треть книги может быть интересна только тем, кто сам собирается провести какое-то подобное исследование.

Мои впечатления следующие:

  • первые 2/3 книги читаются легко и интересно, много примеров из разных областей. Воды не много, хотя и не обошлось без типичной англосаксонской манеры неоднократно повторять одно и то же на разные лады. Так что если ничего из Коллинза раньше не читали, смело можно взять и познакомиться с творчеством автора;
  • некоторые моменты, о которых лично я имел несколько другое мнение, заставляют задуматься о том, а насколько вообще авторы книги понимают специфику определенных прикладных областей и правильно ли они интерпретируют обнаруженные ими в процессе исследования факты. Например, рассказ о том, как Microsoft относилась к разработке OS/2 для IBM и как Microsoft "подстраховалась" от рыночной неудачи OS/2 продолжая работу над своей ОС Windows. Если читатель более-менее в курсе истории отношений Microsoft-а и IBM, а так же в курсе истории развития OS/2 и причин неудачи OS/2 на рынке, то у него могут возникнуть возражения против точки зрения авторов книги (у меня, например, возникли). Соответственно, ловишь себя на мысли, что есть есть несостыковочка здесь, то может она не одна такая? И насколько тогда можно доверять выводам авторов?
  • у меня сложилось ощущение, что ключевыми факторами, на которых авторы книги пытались заострить внимание читателей, являются: здравый смысл, последовательность и дисциплина. Что очень сильно, на мой взгляд, пересекается с ключевыми факторами из "Живой компании" Ари де Гиуса. А раз так, то смело можно обойтись чтением "Живой компании" вместо "Великие по собственному выбору".

Так что, если вы пока еще не прочитали "Живую компанию" и, например, "Управление жизненным циклом корпорации" Ицхака Адизеса, то лучше сперва прочесть эти две книги, а не "Великие по собственному выбору". Ну а если это все уже прочитано, но хочется прочитать что-нибудь именно из Коллинза, то тогда можно и "Великие по собственному выбору" осилить. Хуже точно не будет.

PS. Кажется, читая про Zappos и про Twitter, встречал упоминания о Джиме Коллинзе как о "гуру" от бизнеса и менеджмента, репутация которого базируется на успехе книг "Построенные навечно" и "От хорошего к великому". Но вот я прочел уже вторую его книгу и мне сложно понять, почему Коллинз считается таковым "гуру".

суббота, 10 июня 2017 г.

[prog.c++] В очередной раз про Rust. О том, почему до сих пор меня Rust не прельстил

На C++ я программирую уже давно. Лет 25 точно. Не могу сказать, что знаю C++ хорошо, но, наверное, хорошо знаю, как использовать C++ так, чтобы не было мучительно больно. Тем не менее, не могу не признать, что C++ уже весьма стар, сложен и противоречив. Хоть программировать на нем в последнее время становится удобнее, но есть серьезные родовые травмы, которые будут постоянно отравлять жизнь C++ разработчикам. Посему лично я был бы не прочь со временем сменить C++ на что-то более современное, удобное и безопасное.

На мой взгляд, из того, что сейчас существует для разработки из нативных языков без GC, самой реальной и серьезной альтернативой для C++ является язык Rust. Так же мне кажется, что Rust -- это всерьез и надолго. Как много Rust переманит именно C-шников и C++-ников -- это отдельный вопрос, думаю, что изрядное количество Rust-программистов будут составлять те, кто пришел в Rust не из мира C/C++, а из мира безопасных языков с GC и, вероятно, из мира функциональщины.

Сам я пока в Rust всерьез не вложился и причины тому вовсе не какие-то идеологические, а весьма прозаические. Мы с коллегами пытаемся создать бизнес вокруг инструментария для программистов. Что именно начнет приносить нам деньги -- продажа продуктов, продажа коммерческих лицензий, обучение, консалтинг, заказная разработка или что-то другое -- это выяснится со временем. Поскольку у нас большой опыт в C++, то сейчас мы концентрируемся именно на инструментарии для C++. Ибо здесь есть рынок: по некоторым оценкам количество C++ разработчиков в мире насчитывает более 3 миллионов человек, накоплена огромная кодовая база на C++, которая никогда не будет переписана на других языках, да и новые проекты на C++, пусть не часто, но начинают (и выбор C++ в качестве языка реализации нового проекта в современных условиях вполне имеет смысл). Так что в области C++ есть и какой-никакой рынок, и нам есть что на нем предложить.

А вот для Rust-а такого рынка, насколько я могу судить, пока еще нет. Вроде как есть какой-то совсем небольшой спрос на Rust-разработчиков, но вряд ли сейчас найдется много желающих покупать коммерческие библиотеки для Rust-а, или коммерческие лицензии на такие библиотеки. Да и вряд ли кто-то будет готов платить за консультации по Rust-у или за обучение Rust-у разработчиков, переходящих на Rust с других языков программирования. Думаю, что не ошибусь, если скажу, что не смотря на весь хайп вокруг Rust-а, реальное присутствие Rust-а в коммерческой разработке ПО в мире находится где-то на уровне статпогрешности. Посему достаточно емкого рынка инструментария для Rust-а еще просто нет. Может через 3-5 лет такой рынок сложится и достигнет достаточного объема, чтобы на нем можно было бы зарабатывать. Но сейчас это не так.

Ну а поскольку самый дефицитный ресурс -- это время, то я пока не вижу смысла тратить этот ресурс на Rust, если его можно потратить на что-то с более реальными перспективами продажи. И это, наверное, главная причина, почему мы до сих пор не вложились в Rust. Хотя из поля зрения его и не выпускаем.

Но сегодня я бы хотел затронуть еще один аспект. Он касается того, насколько сильно придется перестраивать мозги старому C++нику, вроде меня, при переходе с C++ на Rust. И будет ли такая перестройка того стоить.

среда, 7 июня 2017 г.

[prog.c++] Жалел ли кто-нибудь об отсутствии Erlang-style супервизоров в SObjectizer?

Вопрос к тем читателям блога, которые в какой-то мере интересуются SObjectizer-ом: возникало ли у вас при знакомстве с SObjectizer-ом сожаление о том, что в SObjectizer-е нет механизма супервизоров, как в Erlang-е?

Вопрос вызван тем, что сейчас дополнительный проект so_5_extra, который мы хотели бы зарелизить в этом месяце, наполняется функциональностью. Наш интерес состоит в том, чтобы добавить в so_5_extra вещи, которые бы были востребованы потенциальными и актуальными пользователями SObjectizer-а. Если кому-то в SObjectizer не хватает Erlang-овых супервизоров, то мы могли бы их реализовать в so_5_extra вне зависимости от моих персональных взглядов на этот счет.

Ну а если никому не нужны, то "раз врач сказал в морг, значит в морг" :) Все-таки работа с небезопасным C++ сильно отличается от работы с безопасным Erlang-ом.

PS. Здесь можно высказать любое соображение касательно того, чего не хватает в SObjectizer. Или что хотелось бы видеть сделанным по-другому.

PPS. Сейчас в so_5_extra уже есть однопоточная инфраструктура, позволяющая запустить SO-5 с использованием Asio в качестве бэкэнда. В варианте simple_not_mtsafe. Кстати, нужна ли кому-нибудь такая же инфраструктура в варианте simple_mtsafe или инфраструктура, которая позволяет использовать Asio-бэкэнд при работе Asio на пуле потоков? Так же в so_5_extra уже есть реализация round-robin mbox-а. Пока мы закрываем ряд юридических вопросов вокруг so_5_extra, есть возможность включить в so_5_extra еще что-нибудь полезное. Например, те же самые супервизоры, если они кому-то нужны.

понедельник, 5 июня 2017 г.

[prog.thoughts] Внезапное осознание момента в модели publish/subscribe и топиков с wildcard-ами в именах.

Внезапно поймал себя вот на чем: есть publish/subscribe, которая доступна посредством разнообразных MQ-шных брокеров (т.к. IBM WebSphere MQ, RabbitMQ или mosquitto). Там можно подписываться на топики, в именах которых есть wildcard-ы (спецсимволы вроде +, * и #). Например, кто-то публикует сообщение в топике с именем /temperature_sensors/floor/1/n001/value. А приходит это сообщение к тем, кто подписался на топики /temperature_sensor/floor/+/+/value и /temperature_sensors/floor/#.

Так вот, внезапно осознал, что в 90% случаев (а может и в 99.9%) при использовании wildcard-ов нужно иметь точное имя топика, в котором сообщение опубликовано. Т.е. в 90% случаев нужно не только уметь подписаться на /temperature_sensors/floor/+/+/value, но нужно еще обязательно вместе с сообщением получить и имя топика (будь то temperature_sensors/floor/1/n001/value или temperature_sensors/floor/10/n100_10_03/value). Без знания точного имени нет пользы от самого лишь сообщения из топика.

PS. Понятно, что я старый тормоз. Но когда пользуешься готовыми MQ-брокерами, в которых имя топика-источника сообщения есть всегда, то об этом не задумываешься. А вот когда размышляешь о чем-то таком собственном, то вдруг осознаешь, что этой "мелочи" раньше даже и не замечал.

PPS. А вообще это пост на тему о том, как полезно использовать "проектирование на бумаге" с очень ранними прикидками того, как то, что ты хочешь сделать, должно будет использоваться. На реальных или более-менее похожих на реальные примерах.

пятница, 2 июня 2017 г.

[prog.thoughts] Пара докладов про управление зависимостями в C++ и небольшое послесловие к ним

Надеюсь, что тем, кто интересуется проблемой управления зависимостями в С++ проектах, могут быть интересны эти два доклада. Первый хорош тем, что показывает спектр проблем, с которыми приходится иметь дело в больших и кроссплатформенных C++ проектах.

Второй доклад хорош тем, что он дает небольшой, но веселый и бодрый обзор некоторых из существующих пакетных менеджеров для C++:

Мне же, однако, подходы на базе централизованых хранилищ пакетов, будь то conan.io (в ближайшем будущем conan-central), hunter или cppan.org не очень нравятся. Точнее говоря, я не верю, что это окажется достаточно жизнеспособным из-за разных причин. Во-первых, в C++ все очень разобщено, поэтому слабо верится в то, что народ массово начнет концентироваться вокруг какого-то одного центрального репозитория C++ных проектов. Во-вторых, кто и с какой радости должен будет оплачивать сей банкет? Хостинг 100500 непонятных проектов и кучи версий для них -- это накладные расходы, которые кто-то должен оплачивать. Что-то в мире C++ мне не видится организации, которая взяла бы на себя бремя этих накладных расходов. Тем более, что почти что все уже живет на ресурсах типа GitHub, BitBucket или SourceForge.

Кроме того, те же Conan и Hunter работают по принципу организации центрального хранилища загруженных пакетов на машине разработчика. Т.е., если разработчик однажды скачал себе Boost-1.64 для проекта A, а затем захотел использовать Boost-1.64 для проекта B, то пакетный менеджер будет переиспользовать уже загруженный ранее Boost.

Как по мне, то этот подход хорош для быстрой разработки какой-нибудь мелкой программулины. Ну, например, прочитал я на Хабре какую-то статью и захотелось мне проверить один из ее тезисов. Или задали на LOR-е вопрос, для поиска ответа на который нужно написать программку на 50 строк. В этих случаях хочется приложить минимум усилий: где-то просто перечислить список нужных мне внешних зависимостей (например, таких, как Boost, ICU, zlib) и потратить минимум времени на подгрузку и сборку этих зависимостей. Поскольку, если я пишу программу на 50 строк "на выброс", то мне вряд ли захочется ждать, пока Boost еще раз скачается и скомпилируется. Поэтому переиспользование того, что было загружено ранее, в таких условиях -- это вполне себе разумный подход.

С другой стороны, когда я делаю какой-то внутренний проект (или же работаю с какой-то приватной веткой), то у меня могут возникать ситуации, когда мне нужно взять не просто библиотеку X, а конкретный ее слепок. Скажем, библиотека X разрабатывается моим коллегой и он добавил в X новый нужный мне функционал, но еще не зафиксировал ее стабильную версию. Поэтому я на свой страх и риск хочу взять X от ревизии (коммита) R1. С большой долей вероятности я могу наткнуться в X@R1 на какую-то проблему. Из-за чего мне потребуется перейти на X от ревизии R2 (может быть даже эту ревизию я создам сам для того, чтобы исправить проблему, а потом вернуть правки в основную ветку разработки X).

В этих случаях, как мне кажется, плохо работают обе составляющие централизованного подхода (положенного в основу canon/hunter/cppan):

  1. Центральное хранилище опубликованных пакетов. Поскольку для работы с X@R1 кто-то должен быть создать соответствующий пакет и опубликовать его в этом хранилище. Потом это же нужно будет проделать с X@R2 и т.д. При том, что вряд ли хорошо засирать центральное хранилище промежуточными пакетами. Как и делать собственное зекало этого центрального хранилища для того, чтобы засирать его локально.
  2. Централизованное хранилище загруженных на локальную машину пакетов. Поскольку если X@R1 и X@R2 мне нужны только для проекта Y, да и то в какой-то временной ветке Y, то не есть хорошо сохранять X@R1 и X@R2 там же, где хранятся нормальные стабильные версии чего-то вроде Boost-а или ICU.

Имхо, для таких случаев хорошо работает подход, который мы используем в своем MxxRu::externals:

  • во-первых, MxxRu::externals сам забирает исходники стороннего компонента оттуда, где эти исходники лежат. Например, лежит Boost в tar.bz2 на SourceForge -- оттуда его и заберем. А исходники spdlog от конкретного коммита есть в git-е на GitHub-е -- ну значит вытянем их через git с GitHub-а. Таким образом для того, чтобы сделать свой пакет доступным для пользователей, не нужно предпринимать каких-то серьезных усилий. Можно даже свой тарбол не делать, если кроме GitHub-а ни на что мозгов не хватает, то достаточно просто теги в своем репозитории создавать, GitHub сам для них тарбол доступным для загрузки сделает. Ну а если нужно забирать какие-то промежуточные версии собственных компонентов из закрытых репозиториев внутри своей организации, то тут вообще никаких проблем нет, ничего дополнительно поднимать не нужно (речь про локальный инстанс того же Conan-а);
  • во-вторых, загруженные зависимости кладутся только в локальный рабочий каталог. Поэтому, если моему проекту Y потребовался сперва X@R1, а затем X@R2, то исходники X@R1 и X@R2 никуда за пределы рабочего каталога Y не уйдут. И, соответственно, когда я удалю этот рабочий каталог, то автоматически исчезнет и весь "мусор" , который был с этим связан.

Однако, поскольку мир сходит с ума и куча народа с удовольствием жрут тот же самый CMake, то есть серьезные подозрения, что CMake в качестве системы сборки и пакетные менеджеры на его основе (вроде Conan-а и Hunter-а) таки станут де-факто стандартом в мире C++. Ну что тут поделать, количество разума -- величина постоянная, а население-то растет.

ЗЫ. Отдельно хочется помянуть незлым тихим словом красноглазых Linux-оидов, которые всерьез считают, что для управления C++зависимостями нужно пользоваться штатным пакетным менеджером вашего любимого дистрибутива Linux-а. Этих сторойными рядами прямо направляю в жопу. Ибо ваше мнение не интересно. Ну вот от слова совсем. По крайней мере пока не поймете, что "кроссплатформенность" -- это "переносимость между разными платформами", а не "переносимость между разными дистрибутивами Linux-а".

четверг, 1 июня 2017 г.

[prog] Очередная статья про SObjectizer для Хабра: будет ли интересен разбор примера machine_control?

Обдумываю тему следующей статьи на Хабре о SObjectizer-е. Появилась идея подробно описать штатный пример из дистрибутива SObjectizer-а под названием machine_control. Этот пример имитирует управление промышленным оборудованием: контролирует температуру неких двигателей, включает вентиляторы для охлаждения или вообще выключает двигатели при перегреве. Пару лет назад я об этом примере уже писал в блоге, но там не было детального разбора агентов, их принципов работы и взаимосвязей. Оттуда же и вот эта картинка:

Собственно, почему может быть интересно подробнее описать этот пример? Потому, что в нем задействованы почти все самые важные фичи SObjectizer-а. Включая и возможность создания агентов-шаблонов. И хотя пример остается все-таки абстрактным (имитация она и есть имитация), но он совсем не игрушечный, в отличии от ping-pong-а или обедающих философов. Хотя бы чуть-чуть, но приоткрывающий завесу над тем, как на SObjectizer выглядит более-менее приближенный к реальности код.

С другой стороны, именно это и смущает. Ведь для понимания происходящего читателю придется прикладывать гораздо больше усилий, что может быть непосильной задачей для изрядной части аудитории Хабра (при всем моем уважении к размеру этой самой аудитории). Поэтому велик риск вложиться в написание очень длинной статьи, а на выходе получить не более тысячи ее просмотров.

В общем, если кому-то интересна такая статья, то дайте об этом знать: либо в комментариях, либо через +1 в G+, либо через лайки в FB и LinkedIn (где я размещу ссылки на этот пост).

Если не интересно, то об этом так же можно (и даже нужно) заявить прямо. А еще лучше сказать, статья на какую тему вокруг SObjectizer-а вам была бы интересна.

среда, 31 мая 2017 г.

[life.cinema] Очередной кинообзор (2017/05)

Подошло время очередного кинообзора. Довольно необычного. Во-первых, в нем неожиданно много фильмов оказалось. Во-вторых, за исключением последних двух картин, о которых будет отдельный разговор, посмотреть можно практически все. Первые четыре фильма просто отличные. Остальные -- разной степени достойности, но откровенного шлака нет. Что-то может понравиться больше, что-то меньше. Но во второй половине списка фильмы практически одинаковые, порядок их перечисления особой роли не играет. Ну а про новых "Чужих" и последний "Форсаж" я внизу скажу отдельно.

Золото (Gold, 2017). Очень хорошо сделанная драма. Но главное -- это досмотреть до самого финала. Тогда некую неторопливость развития событий можно будет полностью простить.

Пираты Карибского моря: Мертвецы не рассказывают сказки (Pirates of the Caribbean: Dead Men Tell No Tales, 2017). Прекрасный образец того, как нужно делать развлекательный киноаттракцион: выключаешь мозги, откидываешься в кресле и понеслась! Ну и да, все деньги на экране, и это видно.

Перестрелка (Free Fire, 2016). Фильм специфический, наверняка понравится не всем. Но я получил большое удовольствие от просмотра.

Берлинский синдром (Berlin Syndrome, 2017). Ну очень неторопливо. Иногда хочется включить его на 1.25 или 1.5 скорости. Однако, именно неторопливость помноженная на предельную обыденность и составляет в итоге уникальную, тягостную, но сильную атмосферу этой картины.

Три для до весны (2017). Понравилось, как снято. Для российского кинематографа более чем достойно. Хотя в сюжетной линии с главным злодеем НКВД-шником авторы перестарались.

Без тормозов (À fond, 2016). Незамысловато, местами туповато, но смешно и весело.

Столик №19 (Table 19, 2017). Хорошая смесь специфического юмора, из-за которого нужно внимательно ловить каждую реплику, и сентиментальной драмы. Сдобренной отличной актерской игрой и работой оператора.

Похищение (Kidnap, 2017). Добротно. Хороший напряженный триллер.

Ирис (Iris, 2016). В целом неплохо. Но правда не понял, за счет чего: сюжета или присутствия красивой актрисы в главной роли ;)

Семейное ограбление (Mes trésors, 2017). Нормальная французская комедия. Не шедевр, но и потраченного времени не жалко.

Комик (The Comedian, 2016). Фильм можно смотреть, если нет неприятия стендапа вообще и жесткого пошлого юмора в частности.

Время псов (The Hunter's Prayer, 2017). Странная картина. Вроде бы актеров подобрали. Вроде как динамику создали. Вроде как и постреляли вдоволь. А вот не торкает особо.

Мелкие преступления (Small Crimes, 2017). Ждал большего. Фильм оставил впечатление какого-то сумбура, в котором события активно переплетаются, но непонятно почему и из-за чего. Хотя нужно начислить дополнительные очки за отсутствие хэппи-энда.

Пробуждение Зодиака (Awakening the Zodiac, 2017). Довольно посредственно. Хотя посмотреть можно, только не нужно ждать ничего, хотя бы отдаленно напоминающего замечательный "Зодиак" Дэвида Финчера от 2007-го года.

Лжец, Великий и Ужасный (The Wizard of Lies, 2017). Пример того, как авторы взяли потенциально очень крутую тему, но не смогли донести до зрителя ощущение того, что главный герой фильма повинен в создании самой большой финансовой пирамиде в истории.

Кухня. Последняя битва (2017). Средней руки российская комедия. Местами смешная. С отличной картинкой. Белорусской аудитории отдельно сильно понравится маленький проезд по "Коленьке".


Ну а эти две картины находятся просто вне всякой критики, т.к. днище было пробито с огромной силой и на невероятную глубину.

Чужой: Завет (Alien: Covenant, 2017). Это так плохо и маразматично, что я даже не могу увязать этот фильм с первыми тремя "Чужими". Ну вот есть уникальная вселенная "Чужих", показанная в первых трех фильмах. Ее даже не смогли испортить странной четвертой частью. А "Прометей" и "Чужой: Завет", особенно "Чужой: Завет", ну никак у меня не увязываются со знаменитой серией. Это какое-то совсем другое кино, совсем про других монстров и совсем про других людей. Можно было бы о "Чужом: Завет" порассуждать именно как о совершенно другой истории. Но происходящее на экране настолько тупо и неинтересно, что нет смысла это делать.

Форсаж 8 (The Fate of the Furious, 2017). Это какой-то красочный киноаттракцион, но непонятно для какой аудитории. Может быть для 10-летних детей. Как это можно всерьез смотреть взрослым людям просто не представляю.