вторник, 7 ноября 2017 г.

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

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

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

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

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

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

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

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

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

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

суббота, 21 октября 2017 г.

[prog.memories] С++ с 1985-го по 2017: краткая ретроспектива

В процессе подготовки доклада к GECon-2017 раскопал небольшой список основных, на мой взгляд, вех в развитии C++. Вроде как этот список впервые был написан для RSDN-а, ну и для того, чтобы искать его было проще, решил утащить в блог.

Любопытно, что если выбрать любую временную точку из истории развития C++, то в ней будет свое понятие "modern C++". Хотя у тех, кто оказался покусан Александреску и все еще находится в здравом уме и трезвой памяти, "modern C++" все-таки связан с тенденциями, которые начались в начале 2000-х именно с книги Андрея Александреску "Modern C++ Design" (хотя корни, по моему, уходят в 1992-1993, когда Алекс Степанов взялся за STL на C++). Ну и как по мне, только где-то с C++14 язык C++ стал достаточно удобным для того, чтобы описанные Александреску в 2001-ом году приемы могли бы использоваться массово. Но это мое личное мнение, никому его не навязываю.

Вот, собственно, моя краткая ретроспектива основных вех в развитии C++:

  • С++ вышел в свет в 1985-ом году, без множественного наследования, пространств имен, шаблонов и исключений;
  • в 1988-ом году в C++ добавилось множественное наследование, const-методы, protected-модификатор;
  • в 1990-ом в C++ добавили пространства имен, шаблоны и исключения. Где-то через год-полтора после этого Степанов выбрал C++ в качестве языка для своих экспериментов в области обобщенного программирования (используемые до этого Степановым Scheme и Ada оказались недостаточно(!) выразительными для этого);
  • в 1993-ом году про STL Степанова стало известно и начались работы по включению STL в стандарт C++;
  • в 1994-ом году выходит "Design and Evolution of C++", в которой Страуструп говорит о C++, как о мультпарадигменном языке программирования. И о том, что не следует относиться к C++ как к "чисто объектно-ориентированному" или "чисто процедурному" языку программирования;
  • где-то в 1994-1995-годах в C++ случайно открыли такую штуку, как template metaprogramming. Что дало старт теме программирования на C++ных шаблонах;
  • в 1998-ом году зафиксирован стандарт C++98, в котором оказался STL от Степанова (причем и сам STL был доработан, и C++ был "дотянут" до того, чтобы уметь поддерживать STL);
  • в 1999-ом году началось развитие Boost-а. В качестве полигона для новых идей для C++. Откуда затем в стандарт C++ придут shared_ptr и пр.;
  • в 2001-ом году вышла книга Александреску "Modern C++ Design", которая максимально популяризировала тему программирования на C++ных шаблонов (этой теме к тому времени уже было порядка 7 лет);
  • в 2003-ом году был зафиксирован корректирующий стандарт C++03. Приблизительно тогда же более-менее полные реализации C++98 стали доступны на большинстве платформ;
  • 2003-й год: в Boost попадает MPL -- фреймворк для метапрограммирования на шаблонах;
  • в 2003-ом году в свет выходит LLVM, проект, который затем попадет под крыло Apple и приведет к появлению clang;
  • 2004-2005-й годы: Boost становится двигателем новых библиотек в стиле Modern C++, изрядная часть Boost-а представляет из себя header-only библиотеки шаблонов;
  • в 2007-ом году появляется первая публичная версия компилятора clang. Который стал очень быстро развиваться и быстро стал основным компилятором для платформ от Apple, затем для FreeBSD и, позднее, для Android-а. Появление clang-а очень сильно подстегнуло развитие других мейнстримовых компиляторов, в первую очередь GCC и MSVC;
  • 2011-й год: стандарт C++11. Целая куча нововведений, расширяющих возможности шаблонов и метапрограммирования: variadic templates, constexpr, auto, decltype, ...;
  • 2014-й год: стандарт C++14. Еще больше возможностей для шаблонов и метапрограммирования, в частности, снятие ряда ограничений для constexpr, более простой auto для функций;
  • 2015-2016: появление библиотек вроде Boost.Hana;
  • 2017-й: зафиксирован драфт стандарта С++17, значительная его часть уже доступна в GCC и Clang, часть проектов уже активно использует C++17 в продакшене.

Если вы выделяете какие-то другие вехи в развитии C++, то не сочтите за труд поделиться в комментариях. Ну, например, появление такой штуки, как expression templates, библиотека MFC, начало использования C++ в разработке операционных систем Windows и BeOS, библиотека Qt, среда KDE, проект LLVM, решение о переходе на C++ в GCC и т.д.

четверг, 19 октября 2017 г.

[prog.c++] Буду выступать на GECon-2017

Компания EPAM проводит вторую большую ИТ-конференцию у нас в Гомеле: GECon-2017.

Я там собираюсь сделать доклад на тему "C++ -- монстр, которого никто не любит, но который всех переживет". Где собираюсь рассказать о том, как C++ стал мейнстримом, как он развивался, к чему пришел, где применяется, какое будущее его ждет и как C++ выглядит на фоне имеющихся конкурентов (в первую очередь C, D, Go, Rust).

В общем, к чему я это? А к двум вещам:

Во-первых, если кто-то еще не слышал про проведение GECon-2017 у нас в городе, то теперь вы знаете. Имеет смысл прийти и послушать. В прошлом году было более чем любопытно, и сама конференция была организована очень хорошо.

Во-вторых, если кто-то поделится какими-то интересными примерами использования C++ или интересными примерами того, как современный C++ упростил вам работу, то поделитесь, пожалуйста. Не сочтите за труд. У меня есть свои примеры, конечно же, но все равно чем больше таких примеров будет перед глазами, тем интереснее получится доклад. Ну вот, например, несколько лет назад было интересно узнать, что Dropbox стал активно использовать C++ для разработки мобильных приложений. Выгода здесь в том, что ядро приложение пишется всего один раз и остается общим для всех платформ, а уже GUI-часть навешивается своя для каждой платформы. Или другой пример: на одной из CppCon (вроде бы 2015-го года) был доклад о использовании C++ на больших вычислительных кластерах для расчета прогнозов погоды. Вот за такого рода примеры я буду сильно признателен.

Как, кстати говоря, и обратным примерам. Типа того, что вот была система на C++ и все просто замучались ее поддерживать, потом посадили 3-х вчерашних студентов и они все за месяц переписали на Go, после чего волосы у всех стали мягкими и шелковистыми, а стул нормализовался... ;) Серьезно, на C++ я давно смотрю без розовых очков и хочу сделать более-менее объективый доклад, а не агитку о том, как же все круто в C++ (на самом деле нет).

вторник, 17 октября 2017 г.

[prog.wtf] Городские легенды про "гения, из-за которого мы все просрали..."

Прочитал вот это: We fired our top talent. Best decision we ever made. Захотелось высказаться.

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

Ну что уж тут поделать, не нравятся мне задачи, где нужно комбинировать готовые компоненты. Есть такой недостаток. Зато у меня получается сделать что-то из ничего. Иногда в буквальном смысле. Иногда чужими руками (это я сейчас про тот же SObjectizer-5 и RESTinio).

Добавим сюда еще и неприятную предрасположенность к рефлексии и самокопанию, и получается, что довольно-таки часто мне приходится самому перед собой отчитываться о том, а не страдаю ли я откровенной херней? И не пора ли принять какое-то сильнодействующее лекарство от NIH syndrome...

Что меня в таких сеансах самокопания всегда спасало, так это то, что практически все велосипеды доводились не просто до работоспособного состояния. Они потом еще и работали. Иногда годами. Иногда серьезно развиваясь и видоизменяясь. Уж не знаю, чего это стоило тем коллегам, которым приходилось мои творения использовать, но факт оставался фактом: велосипеды быстро достигали работоспособного состояния и в таком состоянии старательно поддерживались.

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

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

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

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

PPS. Таки да, это явная и неприкрытая джинса. Проплачена мной :)

воскресенье, 15 октября 2017 г.

[prog.c++] Кратко о прошедшей конференции C++ CoreHard Autumn 2017

Если кратко, то было круто. За что огромное спасибо и организаторам, и коллегам-докладчикам, и посетителям!

У нас было три выступления, одно большое и два блица, так что сложилось ощущение, что вся конференция промелькнула как будто за пять минут: не успели прослушать один доклад, как нужно читать свой, не успели перевести дух -- нужно читать следующий, не успели отчитаться, бах! И все закончилось... Нам, правда, еще повезло, мы еще и на афтерпати попали, но и афтерпати промелькнуло еще быстрее.

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

Для конференции мы подготовили приз за самый оригинальный вопрос. Вот такой:

20171002-142120-DSCF8632

Чистой воды хэнд-мейд и самый что ни есть эксклюзив ;) Сделанный настоящим мастером Антоном Хандожко под заказ по индивидуальному эскизу. И, понятное дело, приз нашел своего обладателя:

20171014-183036-DSC_9751

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

В принципе, про Модель Акторов в рамках C++ CoreHard мы рассказали все, что хотели. И, если представится возможность выступить на C++ CoreHard в следующем году, то мы постараемся рассказать еще о чем-нибудь интересном. Например, о том, как C++ помогает в разработке и сопровождении кода. Время от времени я об этом писал в блоге в "рубрике" с общей темой "Шаблоны против копипасты". Полагаю, из этого можно будет сделать полноценный и интересный доклад. Или о том, чего стоит разработка своего встраиваемого HTTP-сервера на C++, который бы, с одной стороны, предоставлял простой и удобный интерфейс программисту, а, с другой, был бы сильно кастомизируемым и применимым, в том числе, и для больших нагрузок. Естественно, все это не без помощи современного C++ и шаблонной магии...

Ну и напоследок еще раз выскажу благодарность сообществу corehard.by, которое умудряется проводить такие крутые и нужные мероприятия. Ребята вы прям ну очень крутые, спасибо за то, что вы делаете!

пятница, 13 октября 2017 г.

[prog.c++] Конференция C++ CoreHard Autumn 2017 уже завтра

Завтра, 14-го октября, в Минске пройдет очередная конференция для C++ разработчиков "C++ CoreHard Autumn 2017". Stiffstream там будет представлен сразу тремя выступлениями.

Во-первых, большим докладом "Actors for fun and profit", в котором я буду рассказывать о том, как понять, принесет ли Модель Акторов вашему проекту fun и profit, или же вы поимеете неприятности на свою голову. Кстати говоря, у нас заготовлен специальный памятный, очень аутентичный приз за лучший вопрос докладчику, так что не упустите свой шанс! ;)

Во-вторых, двумя блиц-докладами. Первый блиц-доклад будет посвящен restinio, нашему инструменту для встраивания http/websocket сервера в C++ приложение. Благо очередная версия, 0.3, в которую мы добавли поддержку websocket-ов и некоторые другие вкусности, уже стабилизировалась и мы активно верстаем документацию для того, чтобы сделать публичный релиз уже в начале следующей недели. Второй блиц-доклад будет про MxxRu::externals. Это та простая система управления зависимостями, которой мы успешно пользуемся уже более полутора лет. И которая отличается от conan-ов, hunter-ов, cppan-ов, vcpkg и пр. отсутствием надобности каких-либо централизованных репозиториев пакетов и какого-либо специального оформления этих самых пакетов.

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

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

среда, 11 октября 2017 г.

[prog.c++] Давайте говорить dependency manager for C++ вместо package manager for C++?

Why is the committee not in charge of a build system and a package manager?

Почитал эту тему на reddit-е. Показалось, что у некоторых красноглазых линуксоидов личностей, особенно из мира Linux и OSS, какая-то идиосинкразия на термин package manager. Они прям начинают исходить на говно при попытке обсудить package manager для отдельного языка программирования (не важно, будет ли это C++, Rust, Ruby или Haskell). Ибо package manager -- это неотъемлимая часть их теплого и уютненького дистрибутива Linux-а. А все, что вне этого есть ересь и блуд.

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

Ну чтож, мир нужно воспринимать как он есть. Посему, раз существуют подобные "седые горлопаны, мудрые как эпос"(с), то имеет смысл перестать в разговорах про управление зависимостями в C++ употреблять термин package manager. Предлагаю использовать термин dependency manager. И пусть никто не уйдет обиженным: C++ разработчики будут использовать dependency manager для того, чтобы разруливать зависимости для своего кода, а ярые защитники пакетных менеджеров затем будут опакечивать результаты работы C++ разработчиков посредством своих любимых package manager-ов.

суббота, 7 октября 2017 г.

[prog] Как же прав был Алан Перлис в своем афоризме про простоту и сложность...

Если кто не слышал (хотя есть ли такие?), то афоризм звучит так:

Простота не предшествует сложности, а следует за ней.

После месяца работы над подготовкой к релизу очередной версии restinio, в течении которого мы сломали множество копий в попытках ператрахнуть (с) интерфейс и сделать его понятным и удобным в использовании, в очередной раз убеждаешься, как же прав был старина Алан! Как же он был прав... :)

На данный момент минималистичный пример использования restinio, а именно http-сервер, запущенный на localhost:8080 и отвечающий "Hello, World" на все запросы, выглядит вот так:

#include <iostream>
#include <restinio/all.hpp>

int main()
{
   run(
      restinio::on_this_thread()
         .port(8080)
         .address("localhost")
         .request_handler([](auto req) {
            req->create_response().set_body("Hello, World!").done();
            return restinio::request_accepted();
         }));

   return 0;
}

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

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

воскресенье, 1 октября 2017 г.

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

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

Ветреная река (Wind River, 2017). Фильм получился атмосферным. Но вот меня эта атмосфера не зацепила. Как по мне, то довольно скучновато и, несмотря на трагизм происходящего, сопереживания к героям у меня лично особого не возникло.

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

Kingsman: Золотое кольцо (Kingsman: The Golden Circle, 2017). Если понравился первый Kingsman, то можно посмотреть и второй. Мне было скучновато, несмотря на обилие динамичных сцен. Ну и больше всего занимал вопрос: как в такое умудрились затащить этих пусть и немолодых, но звезд?

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

Бушвик (Bushwick, 2017). Тот случай, когда интересно следить за картинкой, но не за происходящем в фильме.

Книга Генри (The Book of Henry, 2017). Первая половина фильма интересна и смотрится. Но вот то, что начинает происходить во второй половине, после смерти одного из героев... Это просто какой-то эпический маразм.

Оно приходит ночью (It Comes at Night, 2017). Так и не понял, что же это было. Смело можно не смотреть.

вторник, 26 сентября 2017 г.

[prog.c++] Строю лисапед на тему аллокации объекта на уже имеющемся буфере

Столкнулся вот с какой ситуацией. Нужно создать объект типа Foo (при этом Foo принципиально не является Copyable и Moveable). Одним из полей объекта Foo должен быть объект типа Bar. Но фокус в том, что объект Bar должен создаваться не сразу при создании объекта Foo, а позже. При этом не факт, что Bar вообще может быть DefaultConstructible. Т.е. не получится написать в лоб:

class Foo
{
   ... // bla-bla-bla.
   Bar bar_;
public:
   Foo() = default;
   ... // bla-bla-bla.
};

Само собой напрашивается использование динамической памяти и unique_ptr:

class Foo
{
   ... // bla-bla-bla.
   std::unique_ptr<Bar> bar_;
public:
   Foo() = default;
   ...
   template<typename... Bar_Constructor_Args>
   void activate(Bar_Constructor_Args &&...args)
   {
      bar_ = std::make_unique<Bar>(std::forward<Bar_Constructor_Args>(args)...);
   }
   ... // bla-bla-bla.
};

Но здесь смущает то, что размер-то для Bar-а уже известен. И не хочется дергать динамическую память для того, чтобы сконструировать Bar в Foo::activate.

Поэтому напрашивается решение с буфером для хранения экземпляра Bar и использование placement new для конструирования нового экземпляра Bar внутри этого буфера. Что-то вроде:

class Foo
{
   ... // bla-bla-bla.
   alignas(Bar) std::array<charsizeof(Bar)> bar_buffer_;
   bool bar_created_{false};

public:
   Foo() = default;
   ~Foo()
   {
      if(bar_created_)
         reinterpret_cast<Bar *>(bar_buffer_.data())->~Bar();
   }
   ...
   template<typename... Bar_Constructor_Args>
   void activate(Bar_Constructor_Args &&...args)
   {
      new(bar_buffer_.data()) Bar(std::forward<Bar_Constructor_Args>(args)...);
      bar_created_ = true;
   }
   ... // bla-bla-bla.
};

Тут мы получаем те же фишки, что и при использовании unique_ptr, но без обращения к хипу. Но вот эта ручная работа с placement new... Это не есть хорошо.

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

class Foo
{
   ... // bla-bla-bla.
   buffer_allocated_object<Bar> bar_;
public:
   Foo() = default;
   ...
   template<typename... Bar_Constructor_Args>
   void activate(Bar_Constructor_Args &&...args)
   {
      bar_.allocate(std::forward<Bar_Constructor_Args>(args)...);
   }
   ... // bla-bla-bla.
};

В общем, к чему я это все? Может кто-то делал для себя что-то подобное или видел где-то что-то готовое на эту же тему? Поделитесь плиз, ссылками и/или опытом.

У меня сейчас самая первая, накиданная по-быстрому реализация. За некий образец брался std::unique_ptr (но без поддержки кастомных делетеров, естественно). Поэтому у меня сейчас метод get() и иже с ним объявлены как константные. Хотя, есть ощущение, что для buffer_allocated_object это неправильно. Нужно иметь две группы таких объектов: и для константного buffer_allocated_object, и для неконстантного.

Еще сейчас buffer_allocated_object не Swappable, не Copyable и не Moveable. Думаю, что он и должен оставаться не Copyable. А вот на счет Moveable и Swappable я что-то сомневаюсь. Может таки сделать их, если тип T является Moveable?

А еще есть идея добавить второй параметр для шаблона buffer_allocated_object. Который будет определять, должны ли методы get() и Ко проверять флаг allocated_. Если должны, то в run-time будут выполняться проверки и будет бросаться исключение при попытке разыменовать указатель на неаллоцированный объект. Что-то вроде:

class Foo
{
   ... // bla-bla-bla.
   buffer_allocated_object<Bar, checked_access> bar_;
public:
   Foo() = default;
   ...
   void do_something()
   {
      bar_->do_something(); // Exception if bar_ is not allocated yet.
   }
   ... // bla-bla-bla.
};

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

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

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

[prog.c++] Вопрос к читателям: интересна ли кому-нибудь статья о деталях реализации asio_thread_pool диспетчера?

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

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

Посему у меня к читателям блога пара вопросов:

  1. Будет ли интересен кому-нибудь такой рассказ вообще? Не хочется тратить несколько дней работы на то, что прочитает 10-15 человек.
  2. Если же статья все-таки кому-то интересна, то где бы вам было бы удобнее ее видеть: у меня в блоге или на Хабре? Место публикации важно потому, что, во-первых, от этого зависит стиль и подробность изложения, а так же, во-вторых, удобство комментирования и качество комментариев. В принципе, с точки зрения величины аудитории я бы нацеливался на Хабр. Но если у многих моих читателей к Хабру отношение негативное, то проще будет написать статью для блога.

В общем, прошу не счесть за труд и высказать свое мнение. Можно и пожелания по будущему тексту (например, на что сделать акцент, чтобы статья была интереснее для вас). Комментарии можно оставлять в блоге или в G+. Ну или там, где я еще размещу ссылки на этот пост (типа Facebook-а или LinkedIn-а).

На 2017.09.24 10:00 "за" проголосовало только четыре человека, этого мало для того, чтобы браться за написание статьи.

понедельник, 18 сентября 2017 г.

[prog.c++14] Мечты сбываются: задышала реализация диспетчера, которая может использовать пользовательские классы нитей

В рамках работ над новой версией so_5_extra разрабатывается asio_thread_pool-диспетчер. Это диспетчер, в котором на пуле потоков запускаются методы asio::io_service::run(), и агенты, которые привязаны к такому диспетчеру, отрабатывают свои события на этих же рабочих потоках. По сути, диспетчеризацией событий для таких агентов занимается asio::io_service. Что должно дать возможность агентам просто и прозрачно выполнять и IO-операции, и обработку прикладных сообщений не задумываясь о том контексте, на котором они работают (ноги у этого всего растут вот отсюда).

Но самое интересное для меня другое: это был еще и эксперимент по созданию диспетчера, который может использовать не только std::thread для создания рабочих потоков, но и предоставленный пользователем класс нити, если этот класс частично мимикрирует под std::thread. Зачем такое может потребоваться?

Ну, например, если вы хотите использовать какую-то специфическую для ОС функциональность, недоступную через std::thread. Скажем, если вам нужно назначить собственные pthread_attr_t перед запуском рабочей нити. Это не такая уж и экзотическая ситуация. Например, если вы создаете большое количество нитей в приложении, то можете захотеть уменьшить размер стека для них, т.к. размер по умолчанию для вашей задачи может быть слишком большой.

Так вот, этот новый asio_thread_pool показал, что подобный фокус вполне себе работает. И выглядит это как-то вот так:

// Definition of traits to be used with asio_thread_pool.
struct my_disp_traits
{
   // Actual type of thread to be used.
   using thread_type = my_pthread_t;
};
...
   // Create dispatcher for ring of agents.
   auto disp = asio_tp::create_private_disp<my_disp_traits>(
         coop.environment(),
         "asio_tp",
         std::move(disp_params) );

Стоить определить в traits-ах для диспетчера имя класса, который следует использовать вместо std::thread, и asio_thread_pool начнет использовать данный класс.

Любопытные последствия могут быть у этого эксперимента. У меня уже давно есть мысль о том, чтобы добавить возможность такой кастомизации ко всем штатным диспетчерам SO-5. И, поскольку эксперимент оказался удачным, такая возможность может воплотиться в реальность в SO-5.5.20, работа над которой должна начаться вот прямо после фиксации so_5_extra-1.0.2. А это означает, что реализации штатных диспетчеров вынужденно станут шаблонными. Что заметно уменьшит количество .cpp-файлов в исходниках SObjectizer-а. Что будет означать еще одним большой шаг в сторону header-only версии SO-5. При этом header-only -- это вовсе не самоцель. Но чем больше будет в SO-5 кастомизаций через шаблоны, тем ближе к header-only окажется реализация.

Под катом простейшая реализация my_pthread_t, набросанная на коленке за 15 минут...

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

[prog.humour] Маленький фрагмент трудовых будней :)

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

Зачем я его показываю? Наверное, чтобы было понятно, что короткие идентификаторы -- это не про меня ;)

templatetypename Traits = default_traits_t >
inline private_dispatcher_handle_t
create_private_disp(
   //! SObjectizer Environment to work in.
   environment_t & env,
   //! Value for creating names of data sources for
   //! run-time monitoring.
   const std::string & data_sources_name_base,
   //! Parameters for the dispatcher.
   disp_params_t disp_params )
   {
      const auto io_svc_ptr = disp_params.io_service();
      if( !io_svc_ptr )
//FIXME: must be replaced by SO_5_THROW_EXCEPTION!
         throw std::invalid_argument( "io_service is not set in disp_params" );

      if( !disp_params.thread_count() )
         disp_params.thread_count( default_thread_pool_size() );

      using so_5::stats::activity_tracking_stuff::create_appropriate_disp;
      auto disp = create_appropriate_disp<
            // Type of result pointer.
            private_dispatcher_t,
            // Actual type of dispatcher without thread activity tracking.
            impl::real_private_dispatcher_t<
                  Traits,
                  impl::dispatcher_skeleton_without_thread_activity_tracking_t >,
            // Actual type of dispatcher with thread activity tracking.
            impl::real_private_dispatcher_t<
                  Traits,
                  impl::dispatcher_skeleton_with_thread_activity_tracking_t > >(
         // Args for create_appropriate_disp.
         env,
         disp_params,
         // Args for real_private_dispatcher_t constructors.
         env,
         data_sources_name_base,
         disp_params );

      return { disp.release() };
   }

PS. Код еще на альфа-стадии.

вторник, 12 сентября 2017 г.

[prog] А что, кстати говоря, с хайпом вокруг Haskell?

Сегодня довелось наткнуться на довольно старую презентацию Engineering Large Projects in Haskell: A Decade of FP at Galois и подумалось, что еще несколько лет назад хайп вокруг Haskell-я был достаточно заметным. А что сейчас? Поутих? Или это просто мне на глаза больше ничего подобного не попадается?

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

суббота, 9 сентября 2017 г.

[prog.c++] Упарываемся шаблонами: используем их даже для битовых операций

Меня тут давеча на LOR-е типа обвинили в том, что боюсь и шаблонов, и возни с битами и байтами. Юмор этой ситуации заключался в том, что как раз незадолго до этого мы у себя в RESTinio по мере подготовке к релизу очередной публичной версии как раз проводили рефакторинг операций над битами и байтами.

Дело в том, что в коде RESTinio со временем появился ряд операций, в которых требовалось извлечь сколько-то битов из какого-то значения. В принципе, это все элементарные вещи вроде (bs >> 18) & 0x3f. Однако, когда таких элементарных вещей нужно записать несколько штук подряд, да еще в разных местах, да еще с преобразованием результата к разным типам, то лично у меня в голове начинает звучать тревожный звоночек: слишком много хардкодинга и копипасты. А поскольку по поводу копипасты и ее опасности у меня есть собственный пунктик, то в итоге операции с битами и байтами мы упрятали во вспомогательную шаблонную функцию. Там, где у нас было что-то подобное:

result.push_back( alphabet_char( static_cast<char>((bs >> 18) & 0x3f) ) );
result.push_back( alphabet_char( static_cast<char>((bs >> 12) & 0x3f) ) );
result.push_back( alphabet_char( static_cast<char>((bs >> 6) & 0x3f) ) );
result.push_back( alphabet_char( static_cast<char>(bs & 0x3f) ) );

появилось что-то вот такое:

template<unsigned int SHIFT>
char
sixbits_char( uint_type_t bs )
{
   return ::restinio::impl::bitops::n_bits_from< char, SHIFT, 6 >(bs);
}
...
result.push_back( alphabet_char( sixbits_char<18>(bs) ) );
result.push_back( alphabet_char( sixbits_char<12>(bs) ) );
result.push_back( alphabet_char( sixbits_char<6>(bs) ) );
result.push_back( alphabet_char( sixbits_char<0>(bs) ) );

Где ключевую роль играет тривиальная шаблонная функция n_bits_from:

template<
   typename T,
   unsigned SHIFT,
   unsigned BITS_TO_EXTRACT = details::bits_count<T>::count,
   typename F = unsigned int >
T
n_bits_from( F value )
{
   return static_cast<T>(value >> SHIFT) & details::mask<T>(BITS_TO_EXTRACT);
}

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

  • во-первых, замыливания глаза при повторении однотипных операций. Когда приходится записывать подряд штук 5-6 сдвигов с последующими "логическими И", то очень легко где-то ошибиться и записать не то смещение или не ту битовую маску. Такие ошибки, к сожалению, не так просто заметить и они могут жить в коде очень долго, особенно, если код недостаточно покрыт тестами;
  • во-вторых, неявных приведений типов, которые в C++ могут приводить к неожиданным результатам. Например, можно легко попытаться получить из char-а значение unsigned int, забыв про промежуточный каст в unsigned char. И, если в char-е установлен старший бит, то получить нежданчик. Особенно это круто в ситуации, когда сперва 8 бит извлекаются из int-а в char, а затем этот char используется в качестве индекса в массиве (т.е. может произойти расширение из char в size_t, который беззнаковый).

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

Ну а теперь полная реализация (ее текущий вариант, не факт, что хороший и окончательный):

пятница, 8 сентября 2017 г.

[prog.c++] json_dto-0.2

Мы сегодня обновили свою небольшую библиотеку json_dto, которая служит хоть и тонкой, но очень полезной оберткой над такой замечательной штукой, как rapidjson. Мы сделали json_dto где-то года полтора назад, взяв идеи из Boost.Serialization, для уменьшения объема писанины при работе с JSON в C++. Например, json_dto позволяет писать вот так:

class User {
public :
   /* ... */

   template<typename JSON_IO>
   void json_io(JSON_IO & io) {
      io & json_dto::mandatory("id", _id)
         & json_dto::mandatory("name", _name)
         & json_dto::mandatory("birthday", _birthday)
         & json_dto::mandatory("phone", _phone);
   }
};

вместо того, чтобы писать вот такие простыни (взято отсюда как пример несложного кода):

вторник, 5 сентября 2017 г.

[prog.thoughts] Сложные инструменты уже не нужны в современных условиях?

Размышляя время от времени над феноменом языка Go, над тем, что пишут на Хабре или обсуждают на профильных ресурсах (типа LOR-а или RSDN-а), закрадывается мысль, что в современных условиях сложные инструменты мало кому нужны. И я не могу понять, это объективная реальность такова или же это я вошел в пору конфликта "отцов и детей", но уже со стороны "отцов".

Но вот взять тот же C++, который сейчас повсеместно ругают за сложность. Забывая при этом, что сложность эта возникла не просто так, а как адаптация языка к тем прикладным нишам, в которых он активно используется. Сложная система шаблонов в C++ появилась не просто так же. Специализация шаблонов, к примеру, является следствием того, что обобщенные структуры и/или алгоритмы бывает нужно адаптировать к конкретным условиям. Ну это же объективная реальность такая: ты либо борешься со сложностью свой прикладной задачи посредством мощного инструмента, либо же тупо тратишь больше времени и усилий, обходясь намного более примитивными средствами. Скажем, там, где можно сделать один шаблон и применить его для 5 разнотипных наборов данных, можно же обойтись и без шаблона, посредством копипасты.

Мне часто вспоминается первое знакомство с C++ в далеком 1991-ом году. Язык был гораздо сложнее Паскаля и Бейсика. Но зато он позволял сделать свои классы Set и Matrix, которые бы ничем не отличались бы от встроенных в язык типов. А уж когда в C++ завезли шаблоны, то тут вообще такие бескрайние просторы открылись, что просто дух захватывало. Можно было сделать Matrix<T>, где T мог быть, скажем, Complex<U>, да и сам U мог быть не просто float-ом или double, а каким-то собственным FixedSizeFloat...

Конечно, C++ здесь не очень показательный пример, т.к. за его мощность нужно было платить унаследованными от C граблями. Но можно посмотреть и на другие знаковые языки 1980-х и 1990-х годов. Ada, например. Или Eiffel. Да взять ту же Java, которая вышла в свет в 1995-ом как очень примитивный язык. Который был вынужден со временем усложниться и заиметь таки генерики. С C# затем это так же произошло, но гораздо быстрее.

Т.е. в 1980-х и 1990-х, да даже в начале 2000-х, растущая сложность инструментария воспринималась как само собой разумеющееся. Думаю, что это происходило потому, что программистов было мало, сложность программ росла очень быстро, потребность в софте росла еще быстрее. Получалось, что когда программистов мало, а задача сложная, то решать ее методом грубой силы не получится, просто нет ресурсов. Значит решать ее можно было посредством более мощных, а значит и более сложных, инструментов.

Но потом что-то изменилось. Возможно, двумя последними языками из знакомых мне, которые пошли по пути создания сложного, но мощного инструмента, были D и Scala. А вот то, что стало появляться затем, образует уже иную тенденцию. Go -- это вообще какой-то крайний случай. А вот языки, вроде, Ceylon, Kotlin и даже Rust, как мне кажется, идут по пути снижения сложности, но при этом предоставления пользователям достаточной мощности. Хотя "достаточной" -- это относительное понятие. Например, нет в Rust-е нормального ООП и кому-то Rust может казаться достаточно мощным, а кому-то -- нет.

Думается, что все это таки объективно. Подавляющему большинству разработчиков сейчас не нужны сложные инструменты. Ибо изменился "ландшафт" программирования. Сложных задач в процентном отношении становится меньше. Больше становится рутины, в которой основная сложность не в самом программировании, а в организации процесса разработки: от общения с заказчиком и формализации требований до интеграционного тестирования и запуска в эксплуатацию. Программист -- это винтик, который должен быть быстрообучаемым и легкозаменяемым. Чего сложно достигнуть, если программист будет использовать C++ или Scala, а не Go или Kotlin.

пятница, 1 сентября 2017 г.

[prog.c++] RESTinio в конкурсе HighLoad Cup-2017 от Mail.ru

Мы у себя в "СтифСтриме" занимаемся разработкой небольшого C++ного фреймворка RESTinio (последняя публичная версия лежит здесь). Цель в том, чтобы дать C++ разработчикам легковесный, простой в использовании, но мощный и производительный инструмент для разработки RESTful сервисов на C++. А тут Mail.ru объявляет конкурс HighLoad Cup. Естественно, захотелось попробовать RESTinio в чужом бенчмарке, в котором мы ничего не контролируем. В общем, Коля Гродзицкий, который отвечает за разработку RESTinio, и занялся подготовкой конкурсного решения для HighLoad Cup-а.

В качестве технологического стека использовался C++14/17 (в объеме, поддерживавшемся в gcc-6.2), RESTinio, Asio, Node.js http-parser, RapidJSON и json_dto. Отдельного экстрима доставило то, что в этот же момент в RESTinio вливалется поддержка WebSocket-ов. Так что решение приходилось делать на не стабильной версии RESTinio, нестабильность которой увеличивалась за счет выявленных в процесс работы над конкурсным решением недостатков самого RESTinio. В общем, Николаю досталось :)

В итоге Колино решение вошло в финал с 45-го места. И в финале оно оказалось на 44-ом месте (на 2017.09.04).

С учетом того, что в финал, как мне представляется, вошло всего несколько решений, использующих готовые, более-менее полноценные реализации HTTP-серверов, не заточенных под эту конкретную задачу, результат не самый плохой. Как раз рядом с Колиным решением оказалось еще пару решений на Go и fasthttp (а производительность у RESTinio и fasthttp была очень близка, когда мы такие замеры производили в последний раз). Вероятно, четвертый-пятый десяток ТОПа -- это максимум, на который могут претендовать решения, построенные на универсальных инструментах и не оптимизированные на самом низком уровне под конкретные условия.

Но для нас более важным оказалось другое: RESTinio получило первое более-менее серьезное боевое крещение. И мы сами обнаружили целый ряд мест, в которых нужно не только активно дорабатывать напильником, но и вообще довольно глубоко копать в будущих версиях, чтобы дать пользователям RESTinio удобный и настраиваемый под их нужды инструмент. Тем более, что выбор таковых под C++ совсем невелик. В этом плане участие в конкурсе оправдало себя на все 146%.

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


От себя лично добавлю, что я не понял логики организаторов турнира в финале. Как по мне, так нужно было либо делать один рейтинговый обстрел (но объемом в 4-5 раз больше, чем в отборочном туре, чтобы нивелировать разные факторы вроде фазы Луны), либо же делать N обстрелов с одинаковыми данными и высчитывать среднее, либо делать N обстрелов с разными данными и суммировать время. А так получается N попыток и в зачет идет лучшее. Что выглядит особенно непонятно в ситуациях, когда какое-то из решений на отдельных обстрелах уходит по времени в бесконечность или вообще падает.

Очевидно, что Колиному решению на RESTinio это бы никак не помогло. Но зато не было бы вопросов по поводу отдельных решений из ТОП-10, которые в некоторых обстрелах показывают времена в районе 150K и более секунд. Или вообще падают. Но находятся на верхних строчках.

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

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

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

Мегрэ расставляет сети (Maigret Sets a Trap, 2016), Мертвец детектива Мегрэ (Maigret's Dead Man, 2016) и Мегрэ: Ночь на перекрёстке (Maigret: Night at the Crossroads, 2017). Вот все хорошо. От картинки так вообще получаешь огромное удовольствие. Но к чему невозможно привыкнуть, так это к худому комиссару Мегрэ. Как въелось с детства, что Мегрэ был грузным и боролся с перееданием, так и не отпускает до сих пор :)

Тайна 7 сестер (Seven Sisters, 2017). Если не придираться к сюжету, то очень даже неплохо. Руми Рапас, сыгравшая сразу семерых персонажей, вызывает уважение.

Дикая история (El bar, 2016). Обажаю такие, по хорошему укуренные, истории. Поэтому мне очень понравилось. Но фильм для ценителей жанра.

Валериан и город тысячи планет (Valerian and the City of a Thousand Planets, 2017). Очень красочный и очень-очень детский фильм. Рассчитан на аудиторию 8-10 лет, как мне показалось. Так что взрослым можно смотреть только за компанию с детьми.

Послание от Кинга (Message from the King, 2016). Неплохо. Не шедевр, но неплохо. Нормальный такой криминальный фильм без претензий на глобальность и масштабность.

Телохранитель киллера (The Hitman's Bodyguard, 2017). Как по мне, так трейлеры фильма оказались круче, чем сам фильм. Экшен-сцены хороши, но все, что между ними, навевает смертельную скуку. Ну и для фильма такого уровня неожиданно было увидеть не очень уж качественно нарисованные на компьютеры взрывы. Так что фильм несколько разочаровал.

Тёмная башня (The Dart Tower, 2017). Оригинальные произведения Стивена Кинга я не читал, так что для меня это все совершенно новая история. Как по мне, так слишком простенько и недорого. Идрис Эльба и Мэттью МакКонахи, конечно, хороши. Но их не хватает, чтобы сказать, что получилось крутое и зрелищное кино.

6 дней (6 Days, 2017). Не плохая, в общем-то история, но как-то слишком уж скучно и схематично рассказана. Как будто смотришь не художественный фильм, а какую-то полудокументальную реконструкцию.

Стена (The Wall, 2017). Противоречивые ощущение. Сначала история захватывает. Потом становится скучно. Потом главный герой начинает подбешивать. Ближе к концу в фильме почти что разочаровываешься. Но финал откровенно доставляет.

Первое убийство (First Kill, 2017). Как-то ни о чем. Можно и не смотреть.

четверг, 24 августа 2017 г.

[business.book] Дочитал книгу Константина Бакшта "Как загубить собственный бизнес. Вредные советы предпринимателям"

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

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

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

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

Однако, подобные недостатки не отменяют того факта, что книга явно относится к категории must read.

Точнее так: тем, кого устраивает роль наемного работника, данная книга ни к чему. А вот тем, кто задумывается над открытием своего дела (какие бы побуждения за этим не стояли), прочесть ее нужно обязательно. Желательно перед открытием своего дела. Но можно и после :)

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

Признаюсь, что это была, пожалуй, первая книга, которая заставила посмотреть на выражение "работать на себя" под совсем другим углом. И в этом главная ценность книги "Как загубить собственный бизнес. Вредные советы предпринимателям" лично для меня. Многократно перевешивающая все недостатки. Эта та вещь, которая заставляет сильно и много думать. Не то, чтобы я ощутил какое-то озарение из разряда "Ах, знал бы я это пару лет назад!". И не то, что я захотел что-то переделать или пожалел о том, что было сделано ранее. Но зато появилась отчетливость восприятия каких-то вещей и лучшее понимание факторов, которые влияют на мое поведение и на мои решения. Ради этого книгу стоило читать.

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

В общем, если задумался о том, чтобы "поработать на себя", то прочти "Как загубить собственный бизнес. Вредные советы предпринимателям". Наверняка поможет. В частности, отказаться от этой затеи ;)

суббота, 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). Фильм откровенно разочаровал. Как туда вписались Антонио Бандерас и Бен Кингсли -- не понятно.