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

[prog.c++.thoughts] Какой вклад в сложность C++ вносит борьба с самой сложностью C++?

Давеча один из интересующихся SObjectizer-ом пользователей задал вопрос: а можно ли использовать в SO-5 в качестве обработчиков сообщений const-методы? Вопрос оказался, что называется, внезапным. Мы используем SO-5 в разработке софта уже лет семь (как раз где-то в конце 2010 первые стабильные версии SO-5 пошли в дело, если мне не изменяет склероз). Но ни разу нам не пришлось столкнуться с тем, чтобы обработчик сообщения следовало бы пометить модификатором const. Видимо, это потому, что обработчики сообщений в подавляющем большинстве случаев меняют состояние агента, поэтому не было смысла использовать const-методы в качестве обработчиков. А тут новый человек, другие задачи, другой взгляд на решение этих задач. И const-методы оказались нужны.

Ну, OK. Какие проблемы, будем делать.

Только вот как это сделать так, чтобы трудоемкость и сопровождаемость получившегося решения не зашкалила? Речь идет о том, чтобы преобразовать 6-7 методов вот такого формата:

templateclass RESULT, class ARG, class AGENT >
subscription_bind_t &
event(
   RESULT (AGENT::*pfn)( ARG ),
   thread_safety_t thread_safety = not_thread_safe );

которые живут в SO-5 давно, и используются повсеместно.

Нужно было получить методы, которые бы могли принять как RESULT(AGENT::*pfn)(ARG), так и RESULT(AGENT::*pfn)(ARG) const.

Простейший вариант, который приходит в голову -- это тупое дублирование:

четверг, 16 ноября 2017 г.

[prog.c++] Наткнулся на странное в VC++12

Разбавляя код SObjectizer-а шаблонной магией наткнулся на странную штуку в VC++12 (который из VS2013). Такое ощущение, что когда вложенность лямбд оказывается достаточно большой и очередная лямбда не захватывает контекст (т.е. может быть конвертирована в обычную функцию), то VC++ в качестве типа лямбды начинает использовать не анонимный класс с operator(), а указатель на функцию с соответствующей сигнатурой. Т.е. тупо конвертирует лямбду в функцию и дальше использует тип этой функции в качестве типа лямбды. Проявилось это вот на такой строчке. Там реально лямбда на лямбде и лямбдой погоняет :)

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

Еще захотелось, чтобы в C++ таки концепты завезли. Ибо набирать такое, наверное, прикольно, но уж больно муторно:

templatetypename Method_Pointer >
typename std::enable_if<
      details::is_agent_method_pointer<Method_Pointer>::value,
      subscription_bind_t & >::type
event(
   Method_Pointer pfn,
   thread_safety_t thread_safety = not_thread_safe );

Хотелось бы один раз определить концепт MethodAsHandler, а потом указывать этот концепт в декларации метода:

subscription_bind_t &
event(
   MethodAsPointer pfn,
   thread_safety_t thread_safety = not_thread_safe );

Осталось каких-то три годика подождать... :)

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

[prog.c++] Полку парсеров аргументов командной строки для современного C++ прибыло

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

Итак, первая библиотека -- Clara от автора отличного инструмента для TDD и BDD в C++, Catch/Catch2 (с недавних пор библиотека называется именно Catch2). Clara изначально развивалась внутри Catch, но при подготовке Catch2 ее выделили в отдельный проект. Получается, что Clara является более-менее проверенной библиотекой, все-таки Catch2 используется весьма активно. Плюс работает на разных платформах и с разными компиляторами. Но, с другой стороны, документация пока оставляет желать много лучшего.

Пример того, как может выглядеть работа с Clara я выдрал из unit-тестов для нее:

using namespace clara;
struct TestOpt {
    std::string processName;
    std::string fileName;
    int number = 0;
    int index = 0;
    bool flag = false;
    std::string firstPos;
    std::string secondPos;
    std::vector<std::string> unpositional;

    auto makeCli() -> Parser {
        return ExeName( processName )
          | Opt( fileName, "filename" )
              ["-o"]["--output"]
              ( "specifies output file" )
          | Opt( number, "an integral value" )
              ["-n"]
          | Opt( [&]( int i ) {
                    if (i < 0 || i > 10)
                        return ParserResult::runtimeError("index must be between 0 and 10");
                    else {
                        index = i;
                        return ParserResult::ok( ParseResultType::Matched );
                    }
                }, "index" )
              ["-i"]
              ( "An index, which is an integer between 0 and 10, inclusive" )
          | Opt( flag )
              ["-f"]
              ( "A flag" )
          | Arg( firstPos, "first arg" )
              ( "First position" )
          | Arg( secondPos, "second arg" )
              ( "Second position" );
    }
};

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

Как и положено такого рода инструментам Clara автоматически генерирует usage-string, который можно отобразить пользователю.


Второй инструмент -- это clipp. Так же header-only, так же ему достаточно только стандартной библиотеки современного C++. Средства описания аргументов командной строки похожи на таковые в Clara, но, как мне показалось, здесь все более продвинуто:

#include <iostream>

#include "clipp.h"

using namespace clipp;
using std::cout;
using std::string;

int main(int argc, char* argv[]) { 
    bool rec = false, utf16 = false;
    string infile = "", fmt = "csv";

    auto cli = (
        value("input file", infile),
        option("-r""--recursive").set(rec).doc("convert files recursively"),
        option("-o") & value("output format", fmt),
        option("-utf16").set(utf16).doc("use UTF-16 encoding")
    );

    if(!parse(argc, argv, cli)) cout << make_man_page(cli, argv[0]);
    // ...
}

Если с аргументами что-то не так, то можно автоматически сгенерировать вот такой текст:

SYNOPSIS
    convert <input file> [-r] [-o <output format>] [-utf16]

OPTIONS
    -r, --recursive  convert files recursively
    -utf16           use UTF-16 encoding

Библиотека использует перегрузку операторов, что позволяет записывать вот такие-вот DSL-и:

auto cli = ( 
    option("-b")          % "activates b"   >> b,             
    option("-c""--noc") % "deactivates c" >> set(c,false),
    option("--hi")        % "says hi"       >> []{cout << "hi!\n";} );

Вообще, документация оставляет самые приятные впечатления. А вот по поводу кода на Reddit-е высказали замечания. С которыми автор библиотеки согласился и пояснил, что разработка еще не завершена и, например, под MSVC он пока особо ничего не проверял. Так что дока хорошая, а вот либа еще сырая. Хотя желание попробовать вызывает.

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

[life.books] Коротко о четырех книгах, которые я пытался прочитать в последнее время

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

Первая книга -- "Просто гениально" за авторством Уильяма Тейлора.

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

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

Вторая книга -- "Покер лжецов" Майкла Льюиса.

Просто оказалась не моя тема. В книге рассказывается о том, что происходило на Уолл-стрит с конца 1970-х и начала 1980-х. Рассказывается, в основном, о людях, но с сильной привязкой к финансовым механизмам, которые работали в то время. Но, поскольку в торговле ценными бумагами я вообще ничего не понимаю, то не понимаю и контекста, внутри которого происходят описываемые автором события. Без такого понимания читать просто не интересно. Но вообще книга написана живым, может быть местами грубоватым, языком. Так что если кто-то знаком с рынком облигаций, то ему книга может быть покажется интересной. Я же не дочитал и до половины.

Третья книга -- "«Do not disturb». Записки отельера". Автор Юнис Теймурханлы.

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

Наконец, четвертая книга -- "Ген директора" Владимира Моженкова.

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

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

[prog.c++] Пересмотр приоритетов для SO-5.5.20

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

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

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

Если же кому-то возможность использовать собственные нити вместо std::thread нужна уже сейчас или в обозримом будущем, то дайте нам знать. Если это реально кому-то нужно, то приоритет мы поднимем и такую функциональность реализуем. Но хочу, чтобы было понятно: в SO-5 сейчас и так уже достаточно фич. Просто так добавлять туда очередную фичу просто "шоб было" a) не интересно и b) не выгодно. Все-таки мы делаем SO-5 за свои и тратить свои ресурсы на то, что останется лежать мертвым грузом -- это сомнительная идея.

Так что если вам что-то нужно в SO-5, то найдите способ сказать нам об этом (например, в комментариях к этой заметке, а еще лучше по email: info at stiffstream dot com или eao197 at stiffstream dot com, или на мой gmail-овый ящик). То, что нужно реальным или потенциальным пользователям, мы будем добавлять в SO-5.

Пока же в работах над версией 5.5.20 мы попробуем сосредоточиться на том, что может [не]существенно изменить подходы к использованию SO-5 и агентов. В частности это может быть:

  • более простая и тесная интеграция mchain-ов и агентов. Суть в том, чтобы сообщение, которое кто-то отсылает в mchain, могло поступать напрямую конкретному агенту. Тем самым mchain работал бы как естественный механизм overload control. Подробнее это описано здесь;
    • эта фича сейчас под вопросом до тех пор, пока не появятся какие-то дополнительные типы mchain-ов (например, в рамках so_5_extra). Когда будут разные типы mchain-ов, с разной логикой поведения, тогда будет лучше понятно, можно ли сделать универсальную интеграцию агента с разнотипными mchain-ами.
  • возможность каскадировать mbox-ы. Это выглядит актуальным в связи с появлением новых типов mbox-ов в so_5_extra. Например, может быть MPMC-mbox, одним из подписчиков которого будет collecting_mbox, а подписчиком collecting_mbox-а может быть round-robin mbox, подписчиком которого могут быть mchain-ы. Сейчас подписчиком mbox-а можно сделать только агента и это не позволяет выстраивать подобные цепочки без использования промежуточных агентов. Может быть такая возможность каскадирования mbox-ов поможет, например, в реализации реактивного программирования поверх SO-5;
    • разбирательство с текущим механизмом доставки сообщений показало, что эту функциональность сложно сделать без слома API. Делать такой слом в ветке 5.5 неразумно (следует сохранять совместимость в рамках релизов 5.5.*). А выделять версию 5.6 только ради одной фичи -- это преждевременно;
  • какое-то приближение к идее сессионных типов. Это когда сценарий взаимодействия между агентами описывается на уровне типов: сперва в одну сторону может пойти сообщение request, а обратно может прийти либо response, либо timeout, а затем может пойти только finish, но не request. Либо же это может быть какое-то приближение к типизированным mbox-ам и/или агентам. Опять же, прямо на уровне типов описывается, какие воздействия воспринимаются mbox-ом/агентом. Данная тема поднималась ранее;
  • какие-то инструменты для упрощения тестирования агентов. Этот вопрос задавали на последней C++ CoreHard. И он действительно актуален, т.к. тестирование агентов, которые общаются с внешним миром через асинхронные сообщения -- это не просто. Но в данной теме еще и конь не валялся.

В общем, пока вот такие приоритеты. Опять же повторюсь, если будут какие-то конкретные предложения (вроде идеи с мутабельными сообщениями или возможности проверить существование подписки), то мы их внимательно рассмотрим. И, с большой вероятностью, реализуем. Так что есть более чем реальная возможность повлиять на развитие SO-5.

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

[life] Ну, с юбилеем!

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

Вот такая вот история. Капиталист с грустью отмечает столетний юбилей Великой Октябрьской социалистической революции...

С праздником!

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

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

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

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

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

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

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

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

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

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

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

[prog.c++] timertt-1.2.1

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

Во-первых, мы добавили макрос TIMERTT_VERSION, которых хранит в себе версию библиотеки. В десятичном формате YXXXZZZ, где Y -- это мажорный номер версии, X и Z -- минорный номер и номер патча с лидирующими нулями. Т.е. версия 1.2.1 кодируется как 1002001, а версия 1.3.14 будет кодироваться как 1003014. Каюсь, такой макрос нужно было ввести еще в 1.2.0, но хорошая мысля, как говорится, приходит опосля...

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

auto t = tt.allocate();
tt.activate(t, std::chrono::seconds(30), []{...});
...
// Timer needs to be rescheduled.
// Deactivate it first.
tt.deactivate(t);
// Reactivate it.
tt.activate(t, std::chrono::seconds(45), []{...});

В случае, если использовался thread-safe механизм (например, такой, как timer_thread), то эта двойная операция обрабатывалась не очень эффективно -- требовалось два раза захватывать mutex.

Теперь же reschedule выполняет это все внутри себя захватив mutex всего один раз:

auto t = tt.allocate();
tt.activate(t, std::chrono::seconds(30), []{...});
...
// Timer needs to be rescheduled.
tt.reschedule(t, std::chrono::seconds(45), []{...});

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

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

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

Так что еще раз: при работе с reschedule нужно проявлять двойную осмотрительность.

Вообще, в timertt с самого начала существует рекомендация: вместо реактивации таймеров лучше создавать таймеры заново. Т.е. сперва вы вызваете deactivate для старого таймера, затем создаете новый таймер и вызываете для него activate. С учетом того, что timertt может поддерживать темп активации в несколько миллионов таймеров в секунду, мы считаем, что это приемлемый подход. Однако, если кому-то приходится сталкиваться с ситуациями, когда прямо внутри deactivate нужно точно знать, что таймер полностью деактивирован (может быть даже он успел сработать прямо внутри deactivate), то дайте знать. Будем думать, как это побороть. Но не бесплатно. Ну, в том смысле, что это не сможет не сказаться на скорости работы timertt. Хотя, если кто-то возьмется проспонсировать такую доработку, мы тоже не откажемся ;)

среда, 1 ноября 2017 г.

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

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

Джунгли (Jungle, 2017). Не являюсь большим любителем приключенческих фильмов, да и давно не попадалось ничего достойного на эту тему. Но вот "Джунгли" посмотрел с удовольствием. Пожалуй, первый фильм, в котором Дэниэл Рэдклифф у меня не ассоциировался с Гарри Потером.

Авантюристы (The Adventurers, 2017). Простенько, незамысловато, но для развлекательного боевичка -- как раз то, что нужно. Отличный фильм, чтобы отключить мозги и расслабиться.

Сделано в Америке (American Made, 2017). Не фанат байопиков. Так что этот фильм, хоть и сделан качественно, но не зацепил. Единственно, что меня действительно впечатлило, так это то, с какой иронией в американском фильме показаны махинации ЦРУ в Центральной Америке в 1980-е годы. Вот уж, действительно: самое ужасное в советской пропаганде было то, что она говорила правду. Забавно получать подтверждение этому из продукции Голливуда.

Зеленая комната (Green Room, 2015). Более чем скромненько, но, на удивление, атмосферненько и почти что смотрибельно. Мне не хватило логики в поведении злодеев и кровищи в происходящих разборках. Но, если другого выбора нет, то можно глянуть.

Овердрайв (Overdrive, 2017). Фильм явно на аудиторию лет на 30 помладше меня: благородные и остроумные грабители, лихие парни, красивые девушки, крутые тачки, погони по горным серпантинам. При полном отсутствии реалистичности происходящего. Подросткам, наверное, понравится.

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

Обет молчания (Acts of Vengeance, 2017). Сильно так себе. Не покидало ощущение, что стареющий Антонио Бандерас пытается проделать тот же фокус, который получился у Лиама Нисона: стать брутальной звездой боевиков на шестом десятке. Только у Нисона с первой "Заложницей" это получилось на отлично, а вот у Бандероса с его последними фильмами как-то не очень.

Планета обезьян: Война (War for the Planet of the Apes, 2017). Наконец-то засмотрел. Потерянное время. Снято красиво, конечно же. Но какой же бред! Можно посмотреть только для того, чтобы постоянно стебаться с происходящего на экране.


Отдельно хочу пройтись по двум фильмам, которые не стал включать в общий обзор.

Бегущий по лезвию 2049 (Blade Runner 2049). Как по мне, так это фильм, который не следовало снимать. И не следовало бы смотреть, если первый Blade Runner оставил сильные впечатления. Первый фильм, в свое время, сильно разрывал шаблоны, во-первых, своей визуальной составляющей (которая по тем временам была фантастикой, да и сейчас еще вполне себе смотрится). Во-вторых, сюжетом, который в те времена буквально пленял, т.к. компьютеры и роботы -- это был манящее далекое будущее. В-третьих, отлично показанными характерными героями (особенно мне понравился фрик-ботаник Дж.Ф.Себастиан со своими домашними роботами). Ну и, в-четвертых, конечно же, волшебный саундрек Вангелиса. В новом фильме ничего этого нет. По крайней мере для меня.

Тачки 3 (Cars 3, 2017). Обычно я не включаю в обзоры мультфильмы. Но этот меня приятно поразил. Отличный, добрый мультик. И, как мне показалось, вовсе не детский. В общем, не ожидал, был приятно удивлен.

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

[prog.c++] Какой код на С++11/14 произвел на вас самое большое впечатление?

В процессе подготовки доклада к GECon-2017 столкнулся с необходимостью привести небольшой, но яркий пример отличия кода на C++11/14 от кода на C++98. Пока в качестве примера я хочу привести код, который наполняет std::vector несколькими значениями, копирует оттуда те элементы, которые удовлетворяют некоторому условию в другой вектор, затем результирующий вектор сортируется и отображается через std::cout. В коде на С++98 это будет сделано через функторы и итераторы. В коде на C++11/14 это уже будет с initializer lists, лямбдами, туплами и range-for.

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

К сожалению, с хорошими примерами происходит как с анекдотами: когда очень нужно, то хрен вспомнишь. А когда вспоминаешь, то вокруг уже никого нет.

Посему обращаюсь к читателям: есть ли у вас самые яркие впечатления от знакомства с C++11/14? Вот увидели какой-то код на C++11/14 и поняли, что C++ стал другим и то, что в С++98 требовало кучи приседаний, в C++11/14 записывается легко и непринужденно. Если есть, то не сочтите за труд, поделитесь, пожалуйста.

Upd. Под катом варианты, которые есть на данный момент.

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

[prog.c++] Библиотека timertt обновилась до версии 1.2.0

Мы обновили свою легковесную библиотеку для работы с отложенными и периодическими таймерами (wallclock-таймеры не поддерживаются в принципе). В этой версии добавлены две важные фичи:

1. Раньше действие для таймера всегда имело тип std::function<void()>, что было гибко и удобно, но имело скрытые накладные расходы, связанные с std::function (по сути, std::function тут выступал как умный указатель для лямбд и функторов). Если от этих скрытых расходов хочется избавиться, то можно задать свой собственный тип. Например:

class operation_canceler {
    operation_manager & manager_;
    operation_id id_;
public:
    operation_canceler(operation_manager & manager, operation_id id)
        : manager_{manager}, id_{id}
    {}
    void operator()() const noexcept
    {
        manager_.cancel(id_);
    }
};
...
// Define type of timer thread which was use operation_canceler as
// a timer action type.
using my_timer_wheel_thread = timertt::timer_wheel_thread_template<
    operation_canceler,
    timertt::default_error_logger,
    timertt::default_actor_exception_handler >;
...
// Create and use this timer thread.
my_timer_wheel_thread tt;
tt.start();
...
tt.activate( std::chrono::milliseconds(750), operation_canceler{manager, current_id});

Тип должен быть Moveable и MoveConstructible. Соответственно, теперь когда создается объект-таймер, там резервируется место для пользовательского объекта-функтора. А при активации таймера пользовательский функтор мувится в этот зарезервированный кусок памяти. Тем самым не происходит дополнительных аллокаций памяти (как это временами может происходить с std::function).

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

void do_something_complex() {
    timertt::default_timer_wheel_thread tt;
    tt.start();
    ...
    timertt::default_timer_wheel_thread::scoped_timer_object timer;
    // Activate
    tt.activate(timer, std::chrono::milliseconds(250), ...);
    ...
    // Timer can be deactivated in usual way.
    tt.deactivate(timer);
    ...
    tt.shutdown_and_join();
}

Правда, в этой версии мы несколько сломали совместимость на уровне исходного кода. Поэтому номер версии 1.2.0, а не 1.1.4.

Пару слов о происхождении и назначении библиотеки. Когда-то мы долго и с удовольствием использовали большую библиотеку ACE. В том числе и тамошние таймеры (реализация которых была добротной и продвинутой). Но по мере перехода на C++11 мы постепенно отказывались от ACE и в один прекрасный момент оказалось, что из ACE нам нужны только таймеры. Чтобы не таскать дистрибутив ACE только ради таймеров, мы сделали свою легковесную header-only либу, которая базируется только на штатных возможностях C++11.

У нас timertt в работе уже года три. Проблем не замечено. Работает стабильно, может поддерживать изрядное количество таймеров (десятки и сотни миллионов). Реализует три разных таймерных механизма: wheel, heap и list, каждый из которых хорош в своей ситуации.

Предыдущие версии timertt могли работать и с компиляторами, которые не очень хорошо поддерживали C++11 (в частности, VS2013). Начиная с 1.2.0 мы на такие компиляторы уже не оглядываемся. Нужно что-то более-менее нормальное (gcc 4.8-7.2, clang 3.5-5.0, vs2015/2017). Однако, основная часть кода пока еще под все возможности C++ (вроде noexcept и constexpr там, где это разумно) еще не адаптирована. Сделаем это со временем.

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

Архивы с исходниками библиотеки можно найти здесь. Сами исходники живут здесь. Документация здесь.

суббота, 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 генерируют точно такой же код, как и для вручную записанных битовых операций.

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