вторник, 1 января 2030 г.

О блоге

Более двадцати лет я занимался разработкой ПО, в основном как программист и тим-лид, а в последние пару лет как руководитель департамента разработки и внедрения ПО в компании Интервэйл (подробнее на LinkedIn). Поэтому в моем блоге много заметок о работе, в частности о программировании и компьютерах, а так же об управлении.

Так же я пишу о жизни вообще и о нескольких своих увлечениях: о фотографии (включая публикацию своих фотографий, некоторые есть и на ZeissImages), о спорте, особенно о дартсе, и, совсем коротко, о кино.

пятница, 28 ноября 2014 г.

[life.sport.darts] Осенний этап Кубка Гомеля -- 30 ноября 2014

Осенний этап Кубка Гомеля по дартс назначен на 30 ноября 2014 года. Место проведения турнира: СШ№66 г.Гомеля (ул.Макаенка, д.19).

Личное первенство. Игра в 501 single-in/double-out.

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

Победитель и призеры (1-3 места) награждаются памятными медалями.

Вся информация по турниру собрана на официальной странице.

PS. Это, вероятно, предпоследний турнир в Гомеле в 2014-м году. Далее будет еще Финал Кубка Гомеля в декабре, на который, судя по всему, будут допущены только те игроки, которые принимали участие в квалификациях и этапах Кубка Гомеля. Так что это крайняя возможность заработать право побороться за Кубок Гомеля 2014. Не пропустите! ;)

PPS. Этот пост специально будет висеть вверху блога до конца ноября 2014.

воскресенье, 23 ноября 2014 г.

[prog.c++] Классный трюк с использованием std::result_of в списке параметров шаблона

Подсмотрен здесь (а туда взят со stackoverflow):

template<
   typename T,
   typename F,
   typename R = typename std::result_of<F(T)>::type >
std::vector<R>
select(std::vector<T> const & c, F s)
{
   std::vector<R> v;
   std::transform(std::begin(c), std::end(c), std::back_inserter(v), s);
   return v;
}

Суть трюка в том, что он позволяет вывести тип R, который должен быть указан как тип значения для возвращаемого функцией std::vector-а.

пятница, 21 ноября 2014 г.

[prog.c++] Про использование C++ных шаблонов при разработке timertt-1.1

Библиотека таймерных нитей, timertt, создавалась специально таким образом, чтобы моменты срабатывания таймеров отслеживались отдельной нитью. Т.е. клиент библиотеки регистрирует таймер и не заботится о том, кто и как будет отслеживать время срабатывания. И о том, на каком контексте будет вызван обработчик таймера когда наступит время обработки сработавшего таймера.

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

Полагаю, что процесс преобразования timertt к поддержке как однопоточного, так и многопоточного варианта еще не закончен. Но какие-то работающие куски уже получены. Данный пост преследует цель попытаться лучше понять, что же именно было сделано, устраивает ли этот вариант и куда/как следует двигаться дальше. Ну и может быть кому-то будет интересно посмотреть на фокусы с шаблонами, на которые пришлось пойти, дабы минимизировать объем дублирующегося кода и избежать тупой копи-пасты.

четверг, 20 ноября 2014 г.

[prog.c++] Давно так плотно не работал с шаблонами, интересные впечатления

Очень давно так плотно не погружался в C++ные шаблоны, как в последние несколько дней. Вроде как ничего сложного, и нет такого, чтобы шаблон на шаблоне шаблоном погоняет. Тем не менее, что-то вроде вот такого:

template< typename ENGINE, typename CONSUMER >
class basic_methods_impl_mixin
   :  protected mixin_selector< typename ENGINE::thread_safety, CONSUMER >::type
   ,  public ENGINE::defaults_type
{

и вот такого:

template< typename THREAD_SAFETY, typename ERROR_LOGGER, typename ACTOR_EXCEPTION_HANDLER >
class timer_heap_manager_template
   : public details::manager_impl_template<
            details::timer_heap_engine<
                  THREAD_SAFETY,
                  ERROR_LOGGER,
                  ACTOR_EXCEPTION_HANDLER > > 
{

встречается регулярно :)

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

Еще одно впечатление: похоже, при написании кода шаблона самый лучший способ обращения к атрибутам и методам шаблонного класса -- это через this->. Т.е. во многих случаях можно писать так:

void
shutdown()
{
   typename base_type::lock_guard locker{ *this };

   if( m_thread && !m_shutdown )
   {
      m_shutdown = true;
      notify();
   }
}

Но, если часть атрибутов/методов со временем переезжает в базовый шаблонный класс, то для обращения к ним уже нужно использовать this:

void
shutdown()
{
   typename base_type::lock_guard locker{ *this };

   ifthis->m_thread && !this->m_shutdown )
   {
      this->m_shutdown = true;
      this->notify();
   }
}

Посему лучше сразу все писать через this, и не заниматься правкой кода и борьбой с ошибками компиляции при рефакторинге. Но если все писать через this->, то тогда нужно отказываться от префикса m_, который за последние пятнадцать лет стал уже как родной :(

Еще несколько расстраивает многословность шаблонов при их специализации. Т.е. сейчас приходится писать:

templatetypename THREAD_SAFETY, typename CONSUMER >
struct mixin_selector
{
   // Специально пустой, т.к. без наличия конкретной специализации
   // должна возникать ошибка компиляции.
};

template<>
struct mixin_selector< thread_safety::unsafe, consumer_type::manager >
{
   using type = thread_unsafe_manager_mixin;
};

template<>
struct mixin_selector< thread_safety::safe, consumer_type::manager >
{
   using type = thread_safe_manager_mixin;
};

template<>
struct mixin_selector< thread_safety::safe, consumer_type::thread >
{
   using type = thread_mixin;
};

Тогда как хотелось бы обойтись без лишних пустых деклараций. Что-то вроде:

template for<thread_safety::unsafe, consumer_type::manager>
struct mixin_selector
{
   using type = thread_unsafe_manager_mixin;
};

template for<thread_safety::safe, consumer_type::manager>
struct mixin_selector
{
   using type = thread_safe_manager_mixin;
};

template for<thread_safety::safe, consumer_type::thread>
struct mixin_selector
{
   using type = thread_mixin;
};

Только вот не уверен, насколько это возможно. И видит ли в этом недостаток еще кто-нибудь кроме меня :)

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

Лично мне в этой связи часто вспоминается появление Java. Сейчас мало кто помнит, но шаблоны появились в C++ в конце 1980-х, самом начале 1990-х (при том, что официальной датой рождения C++ считается 1986-й год). Конечно, массовой поддержки шаблонов в C++ных компиляторах не было, да и там, где она была, были сильные различия в качестве ее реализации. Однако факты именно таковы: Степанов в 1993-м уже демонстрировал миру свои наработки по STL на базе C++ных шаблонов. Тогда как сама Java в Sun-е в это время существовала в виде наработок в рамках Green- и Oak-проектов.

А решение о выпуске ее в свет, да еще под именем Java, было принято в 1994-м. После чего началась рекламно-пропагандистская компания по подготовке мира к принятию Java. Сколько тогда было многообещающих статей о том, что Sun выкинул из C++ все самое плохое, оставил все самое лучшее, да еще и добавил сборщик мусора и реальную бинарную кроссплатформенность. А что получилось по факту? Как по мне, как сильно кастрированное и унылое говно. Гослинг со товарищи правильно сделали, что выбросили препроцессор и совместимость с C. Но вот то, что они не захотели сделать в Java шаблоны, да еще тогда, когда уже было видно, что шаблоны -- это одна из самых мощных возможностей C++... Иначе как ниасиляторством обозвать это нельзя.

Собственно, я вполне хорошо понимаю людей, которые набили себе шишек на "C with classes" и первых версиях Java (а то и C#) и ушли в OCaml-ы с Haskell-ем. Но, сдается мне, далеко не всегда у них были хорошие знания нормального C++, который с шаблонами и исключениями. Впрочем, это уже совсем другая тема для разговора.

[prog.flame] Очередной молодой программист вопрошает "Ну когда же все будет делаться нормально?"

Небезызвестный в Рунете персонаж выдал очередной поток сознания: "Объясните дураку". Что забавно: для того, чтобы зацепить читателя и проиллюстрировать свою мысль, он приводит в пример плейеры, которые нормально не могут сохранить точку остановки воспроизведения. Но зачем так далеко ходить за примерами? У него в ЖЖ настолько угребищный дизайн, что в больших обсуждениях комментарии очень быстро становятся нечитаемыми :)

Что же до основного вопроса, т.е. когда же софт будет разрабатываться нормально и когда появятся инструменты, которые упростят этот процесс, то ответ прост и неутешителен: никогда.

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

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

PS. Ну и несколько ссылок на свои старые заметки на эту тему:
Мои крамольные мысли о будущем языков программирования
О предсказании сроков написания программ
Опыт в программировании: переход от решения задачи к ее формализации.

среда, 19 ноября 2014 г.

[prog.c++.wow] Малообразованные C++ненависники доставляют

Не могу не утащить этот феерический фрагмент к себе (источник):

STL контейнеры были придуманы для приближения C++ к высотам и требованиям развитых высокоуровневых ЯП типа java/python/lisp и пр, с сохранением возможностей баловаться на низком C-шном уровне. Как известно, одной и то же жо...ой невозможно усидеть на двух противоположных поездах. Поэтому в C++ то крест спадает, то трусы сами надеваются. И программист, пишущий на C++, только и занят тем, что пытается одновременно и крест на шее удержать и трусы снять. Зрелище не очень так.

Особенно доставляет тот факт, что Степанов STL для C++ начал делать в начале 90-х годов. Т.е. тогда, когда Java не было в дикой природе, а Python делал лишь первые робкие шажочки. Что до Lisp-а, то первоначально свои идеи Степанов обкатывал на Scheme (т.е. на Lisp-е), затем на Ada. Но единственным языком, на котором Степанов, по его же признанию, смог воплотить в жизнь свои идеи полностью, оказался именно C++.

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

PS. Процитированный мной персонаж, smeeld, походу, сейчас является знаковой фигурой на RSDN-е (с вполне понятным направлением знака). Многие точки над ё вокруг этого товарища расставила вот эта тема (например, этот ее фрагмент).