среда, 1 июля 2020 г.

[prog.flame] Может уже и пришло время закапывать C++, но пока он позволяет в коде выписывать ABNF грамматики...

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

Иногда складывается впечатление, что эти стенания повсюду. Особенно на профильных ресурсах, типа Reddit, LOR, RSDN (может и на Хабре, но до Хабра сейчас просто уже руки не доходят).

Финальным гвоздем стали очередные вбросы в Telegram канале C++ Russia от известного своим праздным звездежом хаскелиста Александра Гранина, у которого, походу, второй натурой стал троллинг C++а и C++ников. С традиционным уже участием на C++ Russia с докладом про функциональщину.

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

В тоже самое время этот самый современный C++, в который, как искренне верят неасиляторы, напихали всякой ненужной чуйни, лично мне позволяет в декларативном виде записывать в коде ABNF грамматики из RFC по HTTP/1.1.

Причем это даже не самый еще современный C++, а всего лишь C++14.

И что-то мне подсказывает, что сделать подобное на каких-то "типа альтернативах" C++ будет не так уж и просто. Особенно с учетом накладных расходов в run-time. Если вообще возможно.

Ну а вот и сам упомянутый фрагмент. Который отвечает за парсинг содержимого HTTP-заголовка Transfer-Encoding согласно спецификации.

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

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

Фильмы

В погоне за ветром (Ride Like a Girl, 2019). Отлично снятый байопик. Добрый, жизнеутверждающий. Мне понравился.

Идеальный дворец Фердинанда Шеваля (L'incroyable histoire du facteur Cheval, 2018). Странный фильм про странного человека. Снят хорошо. Но смотреть тяжело. История рассказана трогательная, хотя и очень печальная.

Афера в Майами (Wasp Network, 2019). Просто удивительно, как из такой интересной и многослойной истории получилось такое унылое и затянутое непонятно что. Хорошо хоть, что снято красиво.

Отверженные (Les misérables, 2019). У фильма высокие оценки, но мне было смотреть не интересно. Ни сюжет не зацепил, ни актеры. А операторская работа так вообще раздражала. Сложилось ощущение, что фильм ценен разве что за остросоциальную тему, но вовсе не как художественное произведение.

Сериалы

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

Обвиняемый (Presunto culpable, 2018). Начал смотреть из-за исключительно красивого видеоряда в трейлере. Ну и досматривал тоже исключительно из-за работы оператора. Сам по себе сериал туфта, все события из 13 серий можно было бы запросто уместить в 3-4 и получилось бы динамичнее и напряженнее. Но вот работа оператора -- это что-то. В некоторых местах картинку хотелось останавливать и печатать, чтобы на стену повесить. Компоновка кадров, работа со светом и даже разумное использование светосильной оптики вызывает мое почтение.

Ночной администратор (The Night Manager, 2015). Актеры хорошие, на них приятно смотреть. А вот сам фильм не зацепил.

среда, 24 июня 2020 г.

[prog.c++] Максимальное значение времени, которое можно передать в std::condition_variable::wait_for

Метод std::condition_variable::wait_for в качестве одного из параметров получает время ожидания срабатывания condition_variable. На cppreference.com этот параметр называется rel_time. И про его значение сказано следующее:

an object of type std::chrono::duration representing the maximum time to spend waiting. Note that rel_time must be small enough not to overflow when added to std::chrono::steady_clock::now().

Ok. Значит значение rel_time не должно быть таким, чтобы вызвать переполнение, когда его суммируют с std::chrono::steady_clock::now(). Но каким же оно тогда может быть?

В принципе, можно предположить, что максимально допустимое значение для rel_time может быть получено таким нехитрым способом:

std::chrono::steady_clock::time_point::max() - std::chrono::steady_clock::now()

Т.е. оно не должно быть больше разницы между самой большой меткой времени, которую можно выразить в std::chrono::steady_clock, и текущей меткой времени.

Вроде бы так. Но не все так просто, ведь мы же в мире C++.

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

Так вот под Windows и VC++19 (и под FreeBSD и clang) получается вполне себе ожидаемый результат:

wait_time: 9222236437359ms, days: 1
wait for: 1001ms

А вот под Linux-ом и GCC/clang меня ждет неожиданное открытие:

wait_time: 9223171751593ms, days: 1
wait for: 0ms
wait_time: 9136771751593ms, days: 1001
wait for: 0ms
wait_time: 9050371751593ms, days: 2001
wait for: 0ms
wait_time: 8963971751593ms, days: 3001
wait for: 0ms
wait_time: 8877571751593ms, days: 4001
wait for: 0ms
wait_time: 8791171751593ms, days: 5001
wait for: 0ms
... skipped
wait_time: 7840771751592ms, days: 16001
wait for: 0ms
wait_time: 7754371751592ms, days: 17001
wait for: 0ms
wait_time: 7667971751592ms, days: 18001
wait for: 0ms
wait_time: 7581571751592ms, days: 19001
wait for: 1000ms

Т.е. мало того, что нельзя просто взять разность между (time_point::max() - time_point::now()) и для безопасности отнять оттуда какую-то разумную дельту (вроде 24 часов). Эту самую дельту нужно сделать весьма большой.

Почему так -- я хз. Вероятно какие-то фокусы трансформации вызова condition_variable::wait_for в POSIX-овый вызов. Но вот на практике довелось в это стукнуться.

PS. Зачем может потребоваться задавать большие значения для rel_time? Бывают случаи, когда либо пользователь задает время ожидания какого-то события, либо же нужно ждать "когда рак на горе свиснет". И если мы хотим время ожидания представить в программе всего одним значением std::chrono::duration, которое можно будет просто без дополнительных проверок отдать в condition_variable::wait_for (или не делать отдельно вызовы condition_variable::wait и condition_variable::wait_for), то нужно понимать, какая именно величина будет означать "когда рак на горе свиснет".

понедельник, 22 июня 2020 г.

[prog.c++] Релиз SObjectizer-5.7.1 и so5extra-1.4.1

Нашлось время и ресурсы запилить новые версии SObjectizer и so5extra.

В SObjectizer-5.7.1 реализовано несколько полезных нововведений и исправлено несколько косяков, которые пока еще не проявлялись.

Одно из самых важных изменений в SObjectizer-5.7.1 -- это возможность назначать лимиты для сообщений по умолчанию:

class demo final : public so_5::agent_t
{
public:
   demo(context_t ctx)
      : so_5::agent_t{ctx
         + limit_then_drop<msg_A>(100u)
         + limit_then_abort<msg_B>(10u)
         // That limit will be used for all other messages.
         + limit_then_drop<any_unspecified_message>(50u)
         }
   {}

   void so_define_agent() override
   {
      // Explicitly defined limit will be used.
      so_subscribe_self().event([](mhood_t<msg_A> cmd) {...});

      // A new limit object will be created for msg_C here.
      so_subscribe_self().event([](mhood_t<msg_C> cmd) {...});
   }
};

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

Второе полезное нововведение в SO-5.7.1 -- это шаблонная функция make_agent_ref, которая позволяет гарантировать нужное время жизни указателя на агента, если этот указатель приходится отдавать в какой-нибудь коллбэк:

class io_performer final : public so_5::agent_t
{
   asio::io::tcp::socket connection_;
   std::array<std::byte, 4096> input_buffer_;

   void handle_incoming_data(std::size_t bytes_transferred) {...}

   void on_some_event(mhood_t<some_msg> cmd)
   {
      // It's time to read some data.
      connection_.async_read_some(asio::buffer(input_buffer_),
         [self=so_5::make_agent_ref(this)]
         (const asio::error_code & ec, std::size_t bytes_transferred)
         {
            if(!ec)
               self->handle_incoming_data(bytes_transferred);
         });
   }
   ...
};

Полный перечень нововведений и изменений в SO-5.7.1 можно найти здесь.

В so5extra-1.4.1 был добавлен новый тип диспетчера: asio_one_thread.

Этот диспетчер использует отдельную рабочую нить, на которой запускается io_context::run и на которой обслуживаются как I/O-операции, так и события привязанных к этому диспетчеру агентов.

По своей логике работы asio_one_thread очень похож на asio_thread_pool диспетчер, но имеет более простую реализацию. И он более удобен в использовании, когда все I/O-операции нужно "прибить" к одному контексту.

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


Хочу поблагодарить всех, кто выбрал SObjectizer/so5extra. Ваш интерес к этим проектам является самым важным стимулом для дальнейшего развития SObjectizer-а. А ваша обратная связь позволяет добавлять в SObjectizer/so5extra функциоальность, до который мы сами бы и не дошли. Что делает SObjectizer/so5extra полезными еще большему кругу разработчиков.

суббота, 20 июня 2020 г.

[prog.thoughts] Применимость вложенных классов в C++ и серьезность проблемы с forward declaration для них

На RSDN-е угораздило пообщаться с персонажем, насколько уверенном в собственном непогрешимом мнении, что просто караул. К счастью, в Интернетах такие на 100% правые люди встречаются гораздо чаще, чем в реальной жизни. А то работать было бы тяжело.

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

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

понедельник, 15 июня 2020 г.

[prog.flame] Тут опять поднимается волна хайпа вокруг Rust-а, а мне интересно, насколько просто будет на Rust-е...

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

Данный пост навеян двумя факторами:

  • после поверхностного знакомства с Rust-ом при написании кода на C++ я зачастую задумываюсь о том, а как такие же вещи можно было бы сделать на Rust-е и насколько бы сложно было бы объяснить Rust-овому компилятору, что я сам понимаю, что делаю;
  • прозвучавшей на днях в этих наших Интернетиках статьей в которой кто-то выступил от имени всего Microsoft-а: "Microsoft: Rust Is the Industry’s ‘Best Chance’ at Safe Systems Programming".

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

Вкратце суть такова: есть класс-родитель, который владеет неким объектом child. При этом у класса-родителя есть метод replace_child:

class child;
using child_unique_ptr = std::unique_ptr<child>;

class parent
{
   child_unique_ptr m_child;

public:
   void replace_child(child_unique_ptr new_child)
   {
      m_child = new_child;
   }
   ...
};

Класс child -- это интерфейс, который имеет несколько реализаций. Что-то вроде:

class child
{
   parent & m_parent;

public:
   ... // some pure virtual methods.
};

class first_stage : public child {...};
class second_stage : public child {...};
class third_stage : public child {...};
...

А фокус в том, что объекты-дети заменяют у родителя сами себя. Т.е. есть код вида:

class first_stage : public child
{
   ...
   void on_some_event() override
   {
      ...
      m_parent.replace_child(std::make_unique<second_stage>(...));
   }
};

Обратить внимание нужно на то, что только parent владеет указателем на child. И вызов replace_child синхронный. Поэтому, когда child вызывает replace_child у своего parent-а, то parent внутри replace_child-а уничтожает того child-а, который и сделал вызов replace_child.

Это ведет к тому, что значение this после возврата из replace_child будет невалидным. И, если в first_stage::on_some_event после возврата из replace_child будут происходить какие-то обращения к this (например, вызовы каких-то нестатических методов и/или чтение/изменение полей объекта), то возникнет UB.

В общем-то, примененное мной решение явно опасное и неоднозначное. Да и вообще программист из меня так себе, поэтому пишу код как умею. Отсюда и такие спорные решения.

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

А можно было бы в Rust-е применить такой же прием, не прибегая к unsafe?

Мои поверхностные знания Rust-е не позволяют дать точный ответ. Есть смутные подозрения, что без unsafe не обойтись.

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

четверг, 11 июня 2020 г.

[prog.thoughts] Пару слов благодарности в адрес статической типизации

У нас тут подходит к концу четвертая неделя внезапного спурта по реализации неожиданно свалившегося на нас проекта. Написано уже порядка 12KLOC на C++17. И уже недели две с половиной лично я временами ловлю себя на том, что сегодня уже мало чего помню о том, что именно, почему и как было сделано три дня назад.

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

Тут еще нужно сказать, что C++ в принципе не тот язык, на котором можно писать быстро и качественно. Вне зависимости от цены.

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

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

Но это достаточно тривиальные примеры. Давеча довелось столкнуться с несколько другой ситуацией. В первой реализации одного механизма, в котором ограничивалось количество байт для чтения/записи, ограничение (квота) задавалась просто типом std::size_t. Но затем первая реализация начала эволюционировать в сторону усложнения и возникла потребность интерпретировать нулевое значение квоты специальным образом. При этом пропала возможность использовать в коде простое сравнение quote < bytes_to_read.

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

В случае статически-типизированного языка это делается вообще элементарно. Для хранения квоты начинает использоваться не std::size_t, а свой собственный тип. Соответственно, компилятор просто-напросто заставит тебя поправить все места использования квот в коде.

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

Так что статическая типизация в условиях гонки за сроками очень сильно повышает коэффициент спокойного сна.


Еще, если не сильно увлекаться auto, аннотации типов в коде очень облегчают чтение как своего старого, так и чужого кода. Вообще удивительно, но многословность C++ и все вот эти длинные template, typename, nodiscard, namespace, unique_ptr и пр. при чтении кода изрядно замыленным глазом начинают работать в плюс языку. А вот то, что записывается какими-то короткими сочетаниями букв, бывает, что и проскакивает незамеченными.

Доходит до того, что в условиях гонки за сроками и постепенно накапливающейся усталости написать что-то вроде:

std::size_t m_bytes_written{ 0u };

оказывается выгоднее, чем более компактный аналог:

std::size_t m_bytes_written{};

Ну и еще раз хочется сказать спасибо людям, благодаря которым появился C++11 и развился до C++17 (и продолжает развиваться дальше). Потому что та же самая возможность инициализировать члены класса/структуры прямо по месту их описания, а не в отдельно написанного конструкторе (как это было в C++98), так же очень и очень сильно упрощает жизнь и делает код надежнее.


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

Тесты -- это очень важно и очень полезно. Хорошие тесты в дополнение к статической типизации поднимают коэффициент спокойного сна еще выше.

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

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

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

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

Но тесты обходятся недешево.

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

воскресенье, 31 мая 2020 г.

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

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

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

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

2020/04

Тайлер Рейк: Операция по спасению (Extraction, 2020). Очень бодренько, много экшена, интересно следить за происходящим. А с учетом, что это творение Netflix-а, так прямо удивительно хорошо.

Расправь крылья (Donne moi des ailes, 2019). На удивление приятный фильм для семейного просмотра. Местами с очень красивыми съемками.

Охота (The Hunt, 2020). Мне зашло хорошо. Интересная смесь, которая так и норовит скатиться в абсолютный треш, но не скатывается и балансирует на тонкой грани между "вау" и "фу" :)

Морские паразиты (Sea Fever, 2019). Общее впечатление: собрались неплохие, в общем-то, в прошлом актеры, чтобы сделать недорогой околофантастический околотриллер. Но чего-то сильно не хватило. Возможно, бюджета. Возможно, хорошей идеи для фильма.

Тот, кто меня слышит (The Parts You Lose, 2018). Решился посмотреть потому, что в описании к фильму было указано и "боевик", и "триллер". На самом деле там не оказалось ни того, ни другого. А была довольно скучная и затянутая околокриминальная драма.

Спутник (2020). Первую половину фильма авторы успешно нагнетали-нагнетали... Но в конце, как мне показалось, бездарно жидко обделались, ибо понять что, зачем и почему вытворяли герои фильма мне не удалось.

2020/05

Приговор (Une intime conviction, 2018). Как по мне, так отличное европейское кино. Понравилось.

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

Не в себе (Unsane, 2018). Очень даже неплохо. Визуальная составляющая, правда, меня регулярно раздражала. Но в целом очень даже неплохо.

Магия зверя (Seules les bêtes, 2019). Первая треть фильма вызывала уныние и желание завершить просмотр. Но потом разные линии повествования стали закручиваться в общую сюжетную канву так, что в итоге мне даже понравилось.

Аванпост (2019). Был очень приятно удивлен. Несмотря на мои собственные претензии к развязке фильма по сравнению с бондарчуковскими "Притяжением" и "Вторжением" это смотрится как вполне себе нормальная фантастика для детей и подростков.

Последнее слово (The Last Word, 2017). Редко смотрю мелодрамы и не являюсь их любителем, но вот этот фильм почему-то зашел. Хотя он какой-то незамысловатый и вполне себе предсказуемый, без каких-то интересных сюжетных ходов. Тем не менее посмотрел с удовольствием.

Просто помиловать (Just Mercy, 2019). Добротно сделанный фильм. Но очень уж на специфическую для США тему. Поэтому на меня сильного впечатления не произвел.

Помнить (Remember, 2015). Не смотря на хороших актеров и важную тему, меня не впечатлило. Сюжет попахивает плагиатом с "Помни" (Memento, 2000г.) (https://www.kinopoisk.ru/film/335/), а развязка хоть и неожиданная, но воспринимается как слишком фантастическая и притянутая за уши.

суббота, 30 мая 2020 г.

[prog.c++] Насколько дорого дергать раз в секунду некий метод у целой кучи объектов?

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

Скорее всего каждая активность будет реализована неким объектом (внутри которого будет какой-то конечный автомат, а снаружи каждый объект будет иметь некий общий интерфейс).

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

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

Для оценки была сделана небольшая программа, в которой создается несколько рабочих нитей, на каждой из которых создается множество объектов handler-ов, а затем с темпом ~1s у этих handler-ов вызывается метод on_next_turn.

Реализация on_next_turn у handler-ов не совсем пустая. А что-то типа вот такого:

void
actual_handler_one_t::on_next_turn( trigger_t & trigger )
{
   const auto now = std::chrono::steady_clock::now();
   if( now > m_border )
   {
      trigger.triggered();
      m_border = now + m_step;
   }
}

Т.е. в каждом вызове on_next_turn вызывается еще и метод now из steady_clock.

Так же в этом тесте я постарался сделать так, чтобы компилятор не повыбрасывал код, который компилятору мог бы показаться неимеющим наблюдаемых эффектов. Поэтому интерфейсы и реализации объектов handler-ов были разнесены по разным файлам, сделана пара разных реализаций handler-ов (в каждой из которых дергается steady_clock::now()), введен абстрактный тип trigger_t, у которого handler-ы время от времени вызывают triggered. Что позволяет думать, что проведенный замер таки показывает именно то, что мне нужно.

А показывает он вот что: на i7-6600U с Kubuntu 18.04 и GCC-8.4 при запуске с тремя рабочими нитями и 50K объектами handler-ами на каждой из рабочих нитей, среднее время цикла вызова on_next_turn для всех 50K handler-ов составляет 7-8ms. Худшие из увиденных результатов -- 10ms.

При этом 50K объектов -- это где-то раза в 3 больше, чем нужно на данный момент.

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

воскресенье, 24 мая 2020 г.

[prog.c++] Особенность привязки агентов к диспетчеру, о которой полезно знать в SObjectizer

Мы позиционируем SObjectizer как инструмент для упрощения разработки сложных многопоточных приложений. Упрощение, в основном, за счет того, что SObjectizer-овские агенты, как и акторы в "классической" Модели Акторов, обладают собственным приватным состоянием. И никто, кроме самого актора, не может поменять это состояние извне.

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

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

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

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

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

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

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

Вполне логично сделать так, чтобы work_manager владел очередью запросов, т.е. эта очередь была бы частью его приватного состояния.

Однако, со временем мы можем прийти к тому, что work_manager будет обрастать все большей и большей функциональностью. Так, мы можем решить, что время нахождения заявки в очереди нужно ограничивать. И у work_manager-а может появится периодическое сообщение, при получении которого work_manager начнет выбрасывать те заявки, которые ждут слишком долго. Потом окажется, что отправитель заявки может захотеть проверять статус заявки и work_manager-у придется начать обрабатывать новое сообщение get_operation_status. Затем может оказаться, что отправитель запроса может захотеть отменить заявку. И work_manager-у придется начать обрабатывать еще одно новое сообщение revoke_operation. А потом мы можем захотеть, чтобы work_manager начал собирать статистику о том, сколько заявки ждут в очереди. И т.д., и т.п.

По мере усложнения агента work_manager мы можем обнаружить, что объем и сложность кода work_manager постоянно растет и мы ничего не можем с этим поделать, т.к. только агенту принадлежит очередь заявок. И со временем нам придется столкнуться с тем, что work_manager вырос в объеме до 3-4-5K строк кода, и вынужден обрабатывать несколько десятков разнообразных сообщений.

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

И вот добавление поддержки get_operation_status в более-менее сложный work_manager, а потом изъятие get_operation_status из work_manager, -- это трата времени, да еще и чревато возникновением каких-то ошибок.

В таких ситуациях упростить себе жизнь можно за счет того, чтобы сделать очередь заявок разделяемым объектом. К которому могли бы иметь доступ сразу несколько агентов.

Тогда мы могли бы иметь work_manager-а, который наполняет очередь и следит за отправкой запросов на обработку. И могли бы иметь агента operation_status_monitor, который бы обратывал get_operation_status.

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

Но ведь разделяемые данные -- это плохо?

Да, плохо. Особенно когда сущности, которым требуются общие данные, работают на разных рабочих контекстах.

А вот в SObjectizer-е мы можем директивно заставить агентов работать на общем контексте.

Так, мы запросто можем создать one_thread диспетчер и привязать к нему как work_manager, так и operation_status_monitor, так и любого другого агента, которому потребуется доступ к очереди заявок.

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


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

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

Но, повторюсь, нужно такое изредка.

пятница, 22 мая 2020 г.

[work.sandness] Отказался от предложения Яндекс.Практикум поучаствовать в разработке нового обучающего курса по C++

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

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

Но при этом всем я не знаю толком C++.

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

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

Да и, по большому счету, накопилась изрядная усталость от C++. Если бы не тот большой объем наработок, которые наша маленькая компания успела сделать за последние годы именно на C++, которые, к моему удивлению и радости, постепенно получают признание, то я бы лично рискнул бы сменить C++ на что-то другое. На тот же Rust, скорее всего. Или же на C#.

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

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

Но вот обучать кого-то C++... Это уже нет. Староват-с

среда, 20 мая 2020 г.

[prog.c++] Собственные ощущения от выбора C++17 в 2018, 2019 и 2020 годах

Интересно, как меняется собственное ощущение от применимости C++17 с течением времени.

Два года назад, в 2018-ом, когда мы начинали делать демо-проект Shrimp, то выбор в пользу C++17 был достаточно рискованным. Типа, ну это же изначально эксперимент, не для продакшена, да и C++17 в условиях, максимально приближенных к боевым, надо осваивать.

Год назад, в 2019-ом, когда принималось решение делать SObjectizer-5.6 уже исключительно с расчетом на C++17, было ощущение, что это не самое лучше решение и часть потенциальных пользователей от SObjectizer-а это отвернет. Но зато оно оправдано с точки зрения перспективы. Все равно популяризация SObjectizer-а идет медленно, поэтому когда информация о SObjectizer-е разойдется более-менее широко, то C++17 уже не будет проблемой. Т.е. уже в 2019-ом выбор C++17 был не то, чтобы однозначно хорошим решением, но уж точно не рискованным.

Сейчас, в 2020-ом, мы начинаем делать прототип для нового решения сразу на C++17. И это уже воспринимается как само собой разумеющийся выбор. Благо у заказчика всего одна целевая платформа и на ней по дефолту доступен gcc-8.

Ну и еще раз повторю сказанное раньше неоднократно: попрограммировав немного на C++14 уже сложно возвращаться на C++11, хотя 14-е плюсы от 11-ых не сильно отличаются. Тоже самое ощущение и после программирования на C++17: на 14-й стандарт уже некоторая ломка :)

Какой из этого вывод?

Наверное, их два.

Во-первых, пора уже переставать считать C++11 современным C++ ;)

Во-вторых, времена меняются, сидеть по 10-15 лет на древних компиляторах C++, наверное, можно. Но тут нужно отдавать себе трезвый ответ на вопрос "А зачем мне это все нужно и оправдано ли это?"

пятница, 15 мая 2020 г.

[prog.c++] Любопытное про производительность разных диспетчеров в SObjectizer

Занимаюсь сейчас разработкой нового диспетчера для SObjectizer в составе so5extra. Это asio_one_thread диспетчер. Т.е. создается отдельный экземпляр asio::io_context и на отдельной нити для этого экземпляра запускается цикл обработки событий (т.е. дергается метод io_context::run()). Вся обработка событий агентов, привязанных к такому диспетчеру, выполняется только средствами Asio. По сути, собщения на агентов на этом диспетчере доставляются через asio::post.

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

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

Когда pinger и ponger работают на контекте одной нити (т.е. привязаны к одному io_context-у):

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5_extra.disp.asio_one_thread.ping_pong -r 2700000
Configuration: separate dispatchers: no, requests: 2700000

real    0m1,056s
user    0m1,042s
sys     0m0,005s

Т.е. несколько меньше 3M ping-pong/sec, что весьма неплохо.

На штатном one_thread-диспетчере из SObjectizer-а выходит чуть больше 3M msg/sec:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5.ping_pong -r 2700000
Configuration: active objects: no, requests: 2700000

real    0m0,768s
user    0m0,750s
sys     0m0,008s

Самая веселуха начинается когда агентов разносишь на разные нити. В случае штатного one_thread-диспетчера получается:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5.ping_pong -r 2700000 -a
Configuration: active objects: yes, requests: 2700000

real    0m3,479s
user    0m5,092s
sys     0m1,795s

Т.е. порядка 776K ping-pong/sec.

А вот у нового asio_one_thread-диспетчера лучший результат вот такой:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5_extra.disp.asio_one_thread.ping_pong -r 2700000 -s
Configuration: separate dispatchers: yes, requests: 2700000

real    0m18,394s
user    0m11,465s
sys     0m14,112s

Порядка 146K ping-pong/sec или почти в пять раз хуже.

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

пятница, 8 мая 2020 г.

[prog.c++] Теперь и SObjectizer, и RESTinio перечислены в списке Awesome C++

В общем-то мелочь (на самом деле нет), а очень приятно: теперь и SObjectizer, и RESTinio перечислены в важном для C++сообщества списке Awesome C++.

Для нас, как разработчиков SObjectizer и RESTinio, это важно потому, что в мире C++ люди нередко начинают поиск подходящих для себя инструментов именно с Awesome C++. В этом смысле Awesome C++ -- это нечто вроде "желтых страниц". И теперь оба наших основных OpenSource продукта на этих "желтых страницах" перечислены.

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


Как обычно добавлю, что мы очень внимательно относимся ко всем пожеланиям и предложениям наших пользователей. Поэтому если вам чего-то не хватает в SObjectizer/so5extra, RESTinio или json_dto, то дайте нам знать. Постараемся учесть ваше мнение.

Так же, если у кого-то есть сложности в освоении и/или использовании SObjectizer и RESTinio, то не стесняйтесь, спрашивайте. Обязательно поможем. И еще с большим энтузиазмом поможем вам, если вы решите заказать у нас разработку, в которой будут использованы SObjectizer или RESTinio ;)

четверг, 7 мая 2020 г.

[prog.c++.fantasy] А что, если кто-то возьмет и форкнет C++?

И будет развивать свой диалект независимо от комитета. Причем вменяемый диалект, с преферансом и куртизанками прямо сейчас, а не после принятия C++26 или C++29?

Звучит вроде бы дико. Но вот мне давеча попалась ссылка на экспериментальный язык Circle. Это форк C++17 с дополнительными возможностями по метапрограммированию, но не только. Поскольку сейчас в C++ мне больше всего не хватает паттерн-матчинга для удобной и надежной работы с optional, variant и expected, то я пробежался по описанию того, что в Circle сделано в плане поддержки паттерн-матчинга.

И знаете что? Выглядит впечатляюще.

Вот пример:

#include <cstdio>

int main() {

  struct foo_t {
    int x, y, z;
  };
  foo_t obj { 345 };

  int Z = 6;
  @match(obj) {
    // Test an expression against the initializer.
    [_, _, 3]    => printf(".z is 3\n");  // structured binding
    [  .z: 4]    => printf(".z is 4\n");  // designated binding

    // Is Z a test/expression or a binding? If the clause fails, it's got to
    // be a test.
    [_, _, Z]    => printf("Z must be a binding\n");
    _            => printf("Z must be an expression\n");
  };

  return 0;
}

И вот какая мысль внезапно посетила мою голову: а вот что, если автора Circle возьмет "под крыло" какой-нибудь крупный игрок на рынке? Типа Facebook-а, Amazon-а, Bloomberg-а или IBM... И проект превратится из усилий энтузиаста-одиночки во вполне себе живой OpenSource продукт с солидной финансовой поддержкой за плечами.

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

Но вот если форк обещает совместимость с C++17 (т.е. старые кодовые базы на него перетащить не проблема) и, при этом, более оперативное и гладкое развитие в будущем? Развитие под управлением "мягкого диктатора", а не в результате компромиссов комитета? Когда вещи, типа метапрограммирования, паттерн-матчинга и дешевых исключений в нем появляются с темпом в 6-9 месяцев, а не спустя 4-5-6 лет обсуждений в комитете?

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

воскресенье, 3 мая 2020 г.

[prog.c++] По поводу стенаний на счет сложности выразительного C++ кода

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

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

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

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

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

Можно сказать, что на собственной шкуре проверил историю, которую когда-то рассказывал Максим Янченко (aka jazzer с RSDN), про быструю и дешевую реализацию парсеров на Boost.Spirit.


Какова мораль?

Переусложнить можно все что угодно.

Но фичи C++ действительно дают возможность писать меньше, а результат получать качественно.

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

пятница, 1 мая 2020 г.

[prog.c++] К сожалению, не всегда есть возможность применять SObjectizer, а хотелось бы...

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

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

Ну а раз это старый чужой код, то SObjectizer-а там нет и в помине.

И вот на этом фоне временами становится очевидно, насколько бы проще оказалось бы доработка/сопровождение, если бы SObjectizer или что-то подобное в проекте использовалось бы.

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

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

Но ни акторов, ни CSP в проекте не было, запросы к ресурсу шли из разных рабочих нитей, причем сам ресурc не был отдельной нитью, это был объект, методы которого можно было дергать из разных тредов одновременно. Поэтому пришлось делать систему очередей на mutex-ах и куче condition_variables. Что оказалось сильно сложнее, чем если бы использовались SObjectizer-овские агенты.

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

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

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

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


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

Поэтому еще раз предлагаю тем, кто пишет код на C++, посмотреть в сторону SObjectizer-а.

Это бесплатный и свободный инструмент с большой историей. С поддержкой. С возможностью доработать под ваши нужды.

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

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


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

Ну и да, если вам нужна помощь в разработке на C++, то вы можете обратиться к нам. Даже если это не связано, ни с SObjectizer, ни с RESTinio.

среда, 29 апреля 2020 г.

[prog.c++] Ну и о наболевшем...

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

На языке C++ невозможно налабать что-то полезное за 5 минут. Как из-за особенностей самого языка, так и состояния его экосистемы.

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

среда, 22 апреля 2020 г.

[prog.thoughts] Извините, что притащил к вам Егора Бугаенко, но...

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

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


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

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

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


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

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

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

И, пожалуй, единственное, ради чего стоит потратить время и посмотреть данное интервью -- это получить представление о том, во что нельзя превращаться.


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

четверг, 16 апреля 2020 г.

[prog.c++] Послесловие к релизу RESTinio-0.6.6

На неделе была выпущена очередная версия нашей библиотеки для встраивания в C++ приложения асинхронного HTTP/Websocket сервера: RESTinio-0.6.6.

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

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

Взлетит или не взлетит эта тема не знаю. Хотелось бы, чтобы взлетела. Т.к. C++ требует большого внимания со стороны программиста и не прощает ошибок, а REST API (и не только), как оказалось, на C++ таки вполне себе пишут. Ну а раз пишут, то пусть C++ компилятор помогает разработчику избегать глупых ошибок и опечаток.

Ну а если не взлетит... Ну не взлетит, что тут поделать.

В общем, по моему мнению RESTinio стал еще лучше и мощнее. Так что если кто-то сомневается пробовать или не пробовать, то имеет смысл попробовать ;)


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

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

пятница, 3 апреля 2020 г.

[software.thoughts] Интересно и толково про проблемы OpenSource

На RSDN-е всплыла интересная ссылка: https://youtu.be/YDBE7OM7-mM?t=35578. Это доклад Андрея Ситника на HolyJS 2019 Piter. Доклад как бы из двух частей: первая часть про OpenSource, а вторая, якобы, про Web. Но мне показалось, то обе части доклада посвящены одной и той же проблеме. И то, что автор доклада рассказывает про ситуацию c front-end-ом, можно без особых проблем перенести на любую другую область разработки софта.

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

Одна из основных мыслей, высказанных Андреем Ситником, а именно "популярность != надежности/качеству", может показаться провотиворечащей реальности. Но здесь я, скорее, склонен с автором согласиться. С той лишь поправкой, что для популярного инструмента гораздо легче найти в Интернете рецепты для простых задач и решения для простых, наиболее часто встречающихся проблем. Так что если вам с помощью популярного инструмента нужно решать какие-то типовые и несложные задачи, то вероятность сделать это "малой кровью" все-таки больше, чем если выбирать мало известный инструмент.

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

Еще очень важная вещь, которая проистекает из рассказанного Андреем Ситником -- это необходимость помощи в раскрутке малоизвестных OpenSource проектов. Может показаться мелочью, но каждая звездочка на GitHub-е, каждый лайк в соцсетях, каждый дополнительный +1 на Reddit-е или HackerNews, не говоря уже о ретвитах/репостах оказывают огромную помощь разработчикам малоизвестных OpenSource проектов. Т.к. отсутствие этих мелких признаков внимания очень сильно снижают мотивацию разработчиков. Поэтому если вам на глаза попадается новость о каком-то OpenSource проекте, который показался вам интересным или просто симпатичным, то не сочтите за труд, лайкните эту новость. А если вы еще и сделаете ее репост где-нибудь от своего имени, то реально сделаете большое дело.

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

Но, с другой стороны, у разработки OpenSource нет нормальной экономической модели. Т.к. если не брать 1% топовых открытых проектов, которые либо спонсируются (прямо или косвенно) различными компаниями, либо смогли выйти на окупаемость за счет модели двойного лицензирования или продажи техподдержки, то подавляющее количество открытых проектов -- это либо в прямом смысле one-man show, либо результат работы совсем небольших коллективов. И жизнеспособность таких небольших проектов в условиях кризиса является далеко не праздным вопросом.

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

Не факт, что такая тенденция уже сформировалась. И даже если сформировалась, то ощутимые последствия станут заметны в относительно отдаленной перспективе (лет через 10-15).

В общем, еще раз порекомендую посмотреть доклад. Он будет интересен даже тем, кто далек от разработки front-end-а. Т.к. рассказывает о более важных и общих вещах.

Я же напоследок дам еще одну интересную ссылку: Seven Stages of Open Software. ИМХО, имеет смысл с этими стадиями ознакомиться перед тем, как ввязываться в открытие своего кода и подумать, а до какой стадии ты сам хотел бы дойти.

четверг, 2 апреля 2020 г.

[life.cinema] Очередной кинообзор (2020/02-2020/03)

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

Холоп (2019). Мне зашел очень неплохо, посмотрел с удовольствием.

Джентльмены (The Gentlemen, 2019). Смотреть можно спокойно. Фильм добротный. А вот получить удовольствие от просмотра... Тут не знаю. Я ждал от Гая Ричи большего. Получилось разве что добротно.

21 мост (21 Bridges, 2019). Весьма смотрибельно. Динамично, в меру разговоров, в меру экшена. Хотя финал оказался немного предсказуем, но в целом это впечатления не испортило.

Калашников (2020). Мне зашло. Не смотря на то, что фильм простой и прямолинейный, без каких-либо изысков. Да и попадание одной или двух сцен в фильм лично у меня вызывает вопросы. Но зато хорошо передана фантастическая увлеченность главного героя своим делом.

Пушки Акимбо (Guns Akimbo, 2019). Так плохо, что даже хорошо. Мне понравилось, но я и не ждал ничего кроме лютого трэша от данного произведения. И, как по мне, именно в качестве трешового ремейка "Адреналина", но не с крутым уголовником в главной роли, а с хипстером-задротом, и следует это кино воспринимать. Ну и, соответственно, воздержаться от просмотра, если подобная смесь вас не интересует.

Под водой (Underwater, 2020). Вот вообще ни разу не шедевр, но смотреть было интересно, не смотря даже на то, что подводный монстр в конце был как-то не к месту (имхо). Может быть потому, что нормальной фантастики на тему более-менее качественно снятых катастроф на подводных объектах давно не было.

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

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

Правосудие Спенсера (Spenser Confidential, 2020). Как-то все посредствененько. Есть ощущение, что это не полноценный самодостаточный фильм, а попытка забросить пробный шар на что-то вроде серии фильмов с одними и теми же персонажами. Спасает разве что наличие пары-тройки известных актеров, которые придают происходящему хоть какой-то вес и солидность.

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

Джуманджи: Новый уровень (Jumanji: The Next Level, 2019). Ну так себе. Первый фильм был неплох, а этот явная попытка заработать на успехе первого фильма не утруждая себя поиском чего-то нового и интересного.

Убийства по открыткам (The Postcard Killings, 2020). Начало было интригующим и обещающим интересное продолжение. Но где-то с середины фильма начался такой лютый маразм, что никаких хороших впечатлений не осталось вовсе.

Ангелы Чарли (Charlie's Angels, 2019). Мог бы получится недорогой аттракцион из категории "Телохранитель киллера" или "Гемини", если бы не желание девочек снять про девочек и для девочек с режиссером-девочкой. И да, для подобной фигни сексизм в критике вполне уместен.

Герой (2019). Редкая бредятина и Александр Петров, как обычно, местами сильно выбешивает. Но картинка класcная. Местами офигенно классная. Настолько, что очень обидно становится, что к такой картинке прилагается такая муть в довесок.

Звериная ярость (Primal, 2019). Николас Кейдж, к сожалению, в нормальных фильмах уже не снимается. И "Звериная ярость" лишь подтверждает это. Так что смело можно проходить мимо.

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

суббота, 28 марта 2020 г.

[prog.actors] Почему я не считаю упомянутых в статье на Хабре акторов настоящими акторами

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

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

Это вообще достаточно сложная проблема, с которой я регулярно сталкивался будучи тимлидом. Делаешь code review подчиненного и явно ощущаешь, что реализация переусложнена, что можно проще. Говоришь "здесь что-то слишком сложно, должно быть проще", а на встречный вопрос "А как проще?" сходу ответ дать не можешь. Потому что для того, чтобы дать такой ответ, нужно самому сесть и плотно поработать над этой задачей. А это значит, что ты мало того, что будешь вынужден переделать работу, которую должен сделать твой подчиненный, но еще и не успеешь сделать что-то другое, что ты никому не можешь делегировать.

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

Но данный пост я решил написать на другую тему, связанную с этой же статьей.

Дело в том, что в статье автор повсюду упоминает акторов и, как мне показалось, пребывает в уверенности, что использует "акторный подход". Хотя я (пока еще) убежден, что это не так. И в данном посте попробую объяснить, почему мне кажется, что использованные в статье "акторы" на самом деле "акторами" не являются.

среда, 18 марта 2020 г.

[prog.flame] Ай да молодцы в Dropbox! Взяли и переписали свой sync engine на Rust. Ну молодцы же, да?

Речь про вот этот пост: Rewriting the heart of our sync engine. Dropbox переписал часть своей системы, отвечающей за синхронизацию, с Python-а на Rust и выкатил на публику победную реляцию об этом достижении.

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

Не нравится C++, а Rust-а еще нет не свете? Но ведь есть Ada, есть Eiffel...

Но нет. Сперва нужно написать на Python.

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

А потом выбросить результаты всех трудов на помоечку (с) и с криками восторга и другими проявлениями щенячей радости переписать все на Rust.

Ну и для тех, кто типа умеет читать между строк: это пост не про то, что C++ хорош, а Rust плох. Rust хорош. И для подобных задач его выбор ничуть не хуже, а может и лучше, чем выбор C++. Он про то, что выбор по принципу "пользуем то, на что надрачиваем, а ничего другого просто не знаем", в конце-концов приводит к потере времени и денег.

Да и еще не могу удержаться и не пройтись по любимой теме:

Almost all of our code runs on a single thread (the “Control thread”) and uses Rust’s futures library for scheduling many concurrent actions on this single thread. We offload work to other threads only as needed: network IO to an event loop, computationally expensive work like hashing to a thread pool, and filesystem IO to a dedicated thread. This drastically reduces the scope and complexity developers must consider when adding new features.

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

четверг, 12 марта 2020 г.

[prog.c++.tiredness] Написал мета-функцию transform и чё-та приуныл...

Сегодня с утра потребовалось реализовать мета-функцию transform для списка типов в C++. Т.е. в transform передается мета-функция трансформатор и список типов, а на выходе получается преобразованный список типов. Типа такого:

using T = transform_t<std::decay, type_list<int, int&, const string&>>;
// T == type_list<int, int, std::string>

Задался целью сделать этот transform самостоятельно, опираясь только на детали реализации других мета-функций, которые написал где-то в начали осени 2019-го по следам прочитанных в Интернете материалов.

Убил часа полтора. Тупил невероятно. Но сделал. Не заглядывая в Интернет.

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

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

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

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

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

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

Чего и пожелаю всем читателям, которые ощущают себя в подобной ситуации.

вторник, 10 марта 2020 г.

[prog.c++.bicycle] I'm working on the first draft of experimental type-safe router for RESTinio

The express-like router is available in RESTinio for several years. I think it is a very easy to use tool, that is familiar to many developers. But it has some congenital defects I wanted to repair for a long time.

The congenital defects of express-like router

The propensity to errors

The first defect is that express-like router is error-prone and isn't friendly for repeated cases. Let's look at one of the examples from RESTinio, express_router:

пятница, 6 марта 2020 г.

[prog.c++] Реинкарнация procxx под именем procyy

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

https://github.com/eao197/procyy

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

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

Нет и планов добавить procyy в conan, vcpkg, hunter или какой-нибудь другой менеджер зависимостей. Лично мне это не нужно, а если кому-то нужно, тот пусть и берет на себя все эти заботы. Простите.

PS. Кидаться в меня помидорами по поводу качества кода нет смысла, программист из меня так себе. Получилось как получилось. Конструктивные соображения по поводу улучшения кода продуктивнее оформлять в виде issues.

среда, 4 марта 2020 г.

[prog.c++.actors] Обновленная версия презентации "Actor Model and C++: what, why and how?"

Обновил свою презентацию более чем трехлетней давности. Поскольку что-то в ней уже устарело за это время. А что-то все еще актуально. Так же добавил в презентацию ссылки на малоизвестные широкой публике разработки actor-zeta и rotor. Ну не CAF-ом же единым, в конце-концов...

Так же эта презентация доступна на SlideShare, а PDF-ку отдельно можно скачать с SourceForge.

Я сделал пост на Reddit-е и у себя в LinkedIn. Если кто-то сочтет возможным опубликовать ссылку на SlideShare еще на каких-то ресурсах, то это будет здорово.

[prog.c++.actors] Довелось еще раз посмотреть в сторону CAF

Решился намедни обновить свою старую уже презентацию Actor Model and C++: what, why and how?, поскольку за прошедшее время многое уже изменилось. И, наверное, впервые с 2016-го года еще раз посмотрел в сторону главной на данный момент реализации модели акторов для C++, библиотеке CAF (она же C++ Actor Framework).

Несколько прифигел.

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

Во-вторых, похоже, уже нет шансов хоть как-то побороться с CAF-ом за популярность в C++ мире. Больше двух тысяч звезд на github-е против шести десяков (именно десятков) у SObjectizer-а. На HackerNews посты про CAF собирают под сотню поинтов, тогда как аналогичные для SObjectizer-а -- в лучшем случае 2-3 поинта.

Что называется, почувствуйте разницу.

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

Собственно, вывод из этого простой: никогда SObjectizer не станет популярным и широко востребованным инструментом среди C++ников.

Следовательно, нет смысла пытаться двигаться в этом направлении. Значит SObjectizer будет развиваться дальше без оглядки на то, чтобы кому-то понравится. Или чтобы добавить в SObjectizer что-то, что позволит ему "конкурировать" с чем-то еще.

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

Ну, например, мы используем SObjectizer в тестах RESTinio для проверки поведения RESTinio в условиях многопоточности. И этот сценарий использования SObjectizer-а выглядит вполне себе естественным. Поскольку SObjectizer относительно небольшой. Тогда как применение для этих целей CAF-а мне сложно представить... Наверное, можно. Но, блин, как из пушки по воробьям.

Так что постараемся сохранить небольшой размер и сложность SObjectizer-а. А создание комбайна на все случаи жизни оставим разработчикам CAF-а.

В-третьих, в очередной раз пришел в выводу, что у меня лично, видимо, совсем другие представления о разработке софта, нежели у авторов CAF-а. Поэтому SObjectizer получается именно таким, какой он есть: многословный и несколько старомодный, базирующийся на ООП из конца 1980-х и 1990-х. Ибо я на собственном опыте неоднократно убеждался, что в таком стиле можно писать софт, который развивается в течении многих лет, который вбирает в себя все больше и больше функциональности, разрастаясь в объеме в разы, если не на порядок. И который, при этом, все еще остается обозримым и поддающимся сопровождению.

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

При этом я ни в коем случае не говорю о том, что подход разработчиков CAF-а плох. Возможно, он намного лучше моего. И у множества людей, выбравших CAF, нет никаких проблем. Может это как раз передовой подход, который либо уже стал мейнстримом в C++, либо станет в ближайшее время. А я этого не понимаю в силу своей замшелости и зашоренности.

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

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

Кстати говоря, а что читатели блога думают о CAF-е и о том, как выглядит разработка на акторах с использованием CAF-а?

Заодно, если кому-то чего-то не хватает в SObjectizer-е, или же что-то мешает в SObjectizer-е, то об этом можно сказать прямо сейчас. Например, кому-то мешало использование RTTI в SObjectizer-е. Эта проблема все еще актуальна для кого-нибудь? Имеет ли смысл думать над SObjectizer-ом, который бы не был завязан на RTTI?


Надеюсь, что обновленную версию "Actor Model and C++: what, why and how?" получится опубликовать сегодня во второй половине дня.

понедельник, 2 марта 2020 г.

[prog.c++] Есть желание реинкарнировать библиотеку procxx

Тут такое дело... Жила-была себе небольшая, простая в реализации и симпатичная C++11 библиотека procxx для запуска дочерних процессов в Unix-ах. Мы нашли её года четыре назад и несколько раз за это время использовали то тут, то там. И даже отослали автору какие-то PR.

Давеча потребовалось использовать procxx еще раз и в ее реализации обнаружились некоторые фатальные недостатки. Для устранения которых потребовалось существенно ператрахнуть (с) потроха procxx. И вот теперь, когда новая реализация procxx задышала, возник вопрос: а что с этим делать дальше?

Проект procxx выглядит заброшеным. В репозиторий несколько лет ничего не коммитили, на issue нет реакции. Сам автор, судя по его мизерной активности на github-е, переключился на Rust. Так что, в принципе, можно было бы сделать pull-request для procxx, но смысла в этом я лично не вижу. Тем более, что подобный вопрос я открыл в качестве issue, но никакой реакции пока не последовало (вполне ожидаемо).

Тем не менее, выбрасывать procxx "на помоечку" (с) не хочется. Ну реально простая и удобная библиотека без каких-либо серьезных наворотов в реализации (по крайней мере до того, пока я не запустил туда свои шаловливые ручонки). Осваивается влет, буквально берешь и пользуешься.

Поэтому есть желание реинкарнировать procxx.

Но т.к. автор ничего на эту тему не сказал, то мне стремно использовать procxx в названии моего форка. Было желание назвать обновленную версию procxxrv (от procxx-revisited) или procxx-ng (от procxx-new-generation). Но с такими названиями получается, что я как бы пытаюсь заработать очки на популярности старой procxx. Что не есть хорошо.

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

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

суббота, 29 февраля 2020 г.

[prog.flame] Наглядное подтверждение изречения про два типа языков программирования...

...которое, если я не ошибаюсь, звучит как-то так: "Есть всего два типа языков программирования: те, которые все ругают, и те, которыми никто не пользуется". И вот статья про язык Gо под названием "I want off Mr. Golang's Wild Ride" наглядно показывает, что Go принадлежит к языкам первого типа.

Вообще, как мне думается, данная статья является еще одним проявлением давнего вселенского плача под названием "worse is better". Может из молодежи кто-то не в курсе, но лет 15 назад вокруг этого самого "worse is better" в этих наших интернетиках были большие срачи. Хотя те, 15-летней давности срачи были всего лишь очередной волной подобных срачей после самой формулировки этого принципа в начале 1990-х.

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

Собственно, упомянутая статья наглядно это демонстрирует на примере косяков языка Go и его стандартной библиотеки. Да, косяки есть. Да и вообще сам по себе Go не столько прост или, правильнее сказать, примитивен. Это откровенно "тупорылый" язык.

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

Потому что в этой части рынка ничего сложнее нынешнего Go и первых версий Java/C#, в которых еще и генериков не было, просто не приживается.

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

Тут мне хочется отпрыгнуть в сторону и рассказать о двух ярко выраженных типах программистов, с которыми доводилось сталкиваться.

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

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

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

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

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

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

Ну и следствием из этого является то, что языки вроде Go, Java или даже чистого Си, находят гораздо более широкое применение, нежели что-то вроде Modula-2, Eiffel, Ada или Rust. Просто Go и Java гораздо ближе к народу, под которым прежде всего понимается множество "реалистов".

Отсюда и плач на тему "worse is better": если ты "пурист-прагматик" (не говоря уже про случай "пуриста-идеалиста"), то то, что для тебя "worse", для "реалиста" как раз таки "better". И наоборот. В общем, извечный спор "тупоконечников" и "остроконечников". Ну а упомянутая вначале статья, похоже, была написана "пуристом-прагматиком". Отсюда и ее содержание.


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

среда, 19 февраля 2020 г.

[prog.sadness] Чем больше шуму вокруг C++20 тем больше хочется уйти с C++ вообще

Да уж... Давно я не испытывал такого сильного желания свалить с C++ на какой-нибудь другой язык программирования, как после новостей о завершении работ над C++20 😒 😒 😒

Я даже не могу понять, что тому виной. Может быть то, что хочется держаться подальше от языка, в котором, в принципе такая хитровывернутая система модулей, что сходу в нее и не въедешь. Тем более после 35 лет вполне себе успешного существования без этих самых модулей... Как по мне, так C++ нужно было оставлять без модулей и дальше, а проблему долгой компиляции решать каким-то другим способом.

Может быть потому, что не хочется в очередной раз проходить путь адаптации кардинальных изменений в новом стандарте. Я уже через это прошел когда появился C++98, а потом и C++11. Оба раза это занимало годы и годы оглядываний на старые компиляторы, в которых стандартом либо еще не пахнет, либо же он реализован частично. Даже сейчас еще бывают случаи, когда приходится пользоваться компилятором с неполной поддержкой C++11... Так что впереди еще лет десять привыкания к C++20.

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

Может быть потому, что языку 35 лет, новые стандарты начали штамповать каждые три года, а важные принципиальные разногласия в C++ сообществе так и не устранены. Например, исключения и RTTI часто оказываются под запретом, а сама стандартная библиотека языка вряд ли сможет работать с полностью отключенными исключениями. Или без возможности использовать динамическую память.

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

class Demo {
  [[nodiscard]] bool empty() const noexcept;
  ...
};

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

Может быть это старческая боязнь перемен...

А скорее всего все это в совокупности.

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

Но все равно есть какое-то подсознательно-неосознанное ощущение "а не пора ли свалить с этого перегруженного корабля, у которого на мостике творится одно, в машинном отделение -- другое, а в трюме все вообще по-другому?"