Показаны сообщения с ярлыком Хвастаюсь. Показать все сообщения
Показаны сообщения с ярлыком Хвастаюсь. Показать все сообщения

пятница, 26 декабря 2025 г.

[prog.c++] Компания YADRO использует SObjectizer в одном из своих проектов

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

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

Ну а пока это все, что получается рассказать.

понедельник, 25 августа 2025 г.

[prog.c++] Необычные вещи из многопоточности, которыми довелось позаниматься за последнее время

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


Специализированный mutex, который в сложившейся терминологии C++ной библиотеки можно было бы назвать как recursive_shared_timed_mutex.

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

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

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


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

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

Чтобы было понятнее, давайте вообразим себе std::map<int, std::string>, с которым несколько нитей работают одновременно. Первая нить читает значения по ключам 0, 1, 2, 3, 4, 5, вторая нить читает значения по ключам 4, 5, 6, 7, 8, а третья нить изменяет значение для ключа 0, удаляет значение для ключа 5 и добавляет значения с ключами 9, 10 и 11.

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

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

При этом нити №1 и №2 все равно не будут видеть новую версию, поскольку они работают с map через "старую" ссылку. Чтобы перейти на актуальное содержимое map им нужно обновить эту ссылку у себя, т.е. явным образом обратиться к map с просьбой дать ссылку на последнюю актуальную версию.

Может оказаться так, что кроме нити №3 модификации в map сейчас вносит и нить №4. И свой commit четвертая нить может выполнить еще до того, как третья нить завершит свои модификации. В этом случае при commit-е нить №3 обнаружит, что актуальный map уже совсем другой.

Это все сильно напоминает работу с ветками в VCS: ты отпочковался от master-а (или trunk-а), сделал изменения в своей ветке, хочешь влить в master, а master-то уже ушел вперед. И нужно делать слияние (merging). Иногда оно происходит автоматически и безболезненно, а иногда приходится разбираться с конфликтами.

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

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

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

суббота, 26 апреля 2025 г.

[prog.c++] Еще раз вспомнил про easy_parser из RESTinio

Потребовалось намедни разобрать строку, в которой содержатся разделенные запятыми неотрицательные целые числа. Делал такое недавно посредством std::regex и мне не понравилось. Скорее всего потому, что и с регулярными выражениями на "Вы", и интерфейс std::regex в C++, по моим ощущениям, делали какие-то инопланетяне (не, наверное там все сделано по уму и с прицелом на сценарии, которые мне даже и не снились). Но когда прикасаешься к std::regex раз в два года, то как-то все слишком сложно 🙁

В общем, не понравился мне недавний опыт.

Тут-то и вспомнилась штука, которую мы делали в RESTinio для упрощения разбора HTTP-заголовков. easy_parser называется.

Вспомнилась, решил восстановить в памяти, полез смотреть что и как...

Слегка прифигел от собственной крутизны 🙂

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

Продолжить можно тем, что построен easy_parser на простых идеях: есть producer-ы, которые производят значения из разобранного текста, есть consumer-ы, которые потребляют произведенные значения. Связь producer-а и consumer-а образует выражение (clause). Можно делать составных producer-ов из последовательности clause-ов. Вот, собственно, и все.

Если вникнуть в эту незамысловатую схему, то дальше сложностей не возникает.

Да, конечно, когда в дело вступают transformer-ы, или когда нужно порождать не единичные значения, а наборы значений (std::vector чего-нибудь, к примеру), то потребуется въехать чуть поглубже. Но тоже не сказать, чтобы архисложно.

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

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

Но это было давно. Последний раз прикасался к easy_parser, если мне не изменяет склероз, в декабре 2023-го года.

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

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

[[nodiscard]]
std::vector<std::size_t>
extractDimsSizeListFromStr(std::string_view listAsStr)
{
   namespace ep = restinio::easy_parser;

   auto parser = ep::produce< std::vector<std::size_t> >(
            ep::non_negative_decimal_number_p<std::size_t>() >> ep::to_container(),
            ep::repeat(0, ep::N,
               ep::symbol(','),
               ep::non_negative_decimal_number_p<std::size_t>() >> ep::to_container())
         );


   const auto parsingResult = ep::try_parse(listAsStr, parser);
   if(!parsingResult)
   {
      throw std::invalid_argument{ "Unable to parse list of dimensions size ("
         + ep::make_error_description(parsingResult.error(), listAsStr)
         + ")"
      };
   }

   return std::move(*parsingResult);
}

Ключевой момент, имеющий отношение к easy_parser, выделен жирным. Там все просто ;) Нужно получить вектор чисел. Для этого требуется одно число (которое сразу же помещается в контейнер). А за ним может быть еще 0 или более последовательностей из запятой и еще одного числа (которое сразу же записывается в контейнер).

Вот, собственно, и всё.

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

Но я подозревал, что простого списка чисел мне окажется недостаточно.

И, действительно, буквально несколькими часами спустя этот список уже приобрел вид:

size[ '(' start [',' step] ')' [ ',' size [ '(' start [ ',' step] ')' ...]]

Т.е. каждому значению size может быть привязано два необязательных значения start и step. Если start и step есть, то они должны быть заключены в круглые скобки. Может быть либо одно значение start, либо и start, и step. В последнем случае start и step разделяются запятой.

Если я еще не забыл полностью PEG, то в PEG это должно выражаться чем-то вроде:

values := one_value (',' one_value)*
one_value := NUMBER start_step?
start_step := '(' NUMBER (',' NUMBER)? ')'

Для чего показанный выше пример был переписан вот так:

struct DimParamsFromUser {
   std::size_t _size{};
   std::optional<int> _startFrom{ std::nullopt };
   std::optional<int> _step{ std::nullopt };
};

[[nodiscard]]
std::vector<DimParamsFromUser>
extractDimsSizeListFromStr(std::string_view listAsStr)
{
   namespace ep = restinio::easy_parser;

   auto oneDimParamsP = ep::produce<DimParamsFromUser>(
            ep::non_negative_decimal_number_p<std::size_t>()
                  >> &DimParamsFromUser::_size,
            ep::maybe(
               ep::symbol('('),
               ep::non_negative_decimal_number_p<int>()
                     >> &DimParamsFromUser::_startFrom,
               ep::maybe(
                  ep::symbol(','),
                  ep::non_negative_decimal_number_p<int>()
                     >> &DimParamsFromUser::_step
               ),
               ep::symbol(')')
            )
         );

   auto parser = ep::produce< std::vector<DimParamsFromUser> >(
            oneDimParamsP >> ep::to_container(),
            ep::repeat(0, ep::N,
               ep::symbol(','),
               oneDimParamsP >> ep::to_container())
         );

   const auto parsingResult = ep::try_parse(listAsStr, parser);
   if(!parsingResult)
   {
      throw std::invalid_argument{ "Unable to parse list of sizes of dimensions ("
         + ep::make_error_description(parsingResult.error(), listAsStr)
         + ")"
      };
   }

   return std::move(*parsingResult);
}

Если присмотреться, то можно узнать чуть ли не оригинальные PEG-правила. С поправкой на то, что ()? из PEG записывается как maybe, а ()* в C++ коде выражается через repeat(0, N, ...).

Но самое, на мой взляд, красивое -- это то, что определение для переменной parser осталось практически таким же, только `std::size_t` заменили на `DimParamsFromUser`, а `non_negative_decimal_number_p` на `oneDimParamsP`.

Оно, конечно же, понятно, что каждый кулик свое болото хвалит... Но ведь лепота же! 🙂


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

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

[prog.c++] Конструирование объекта в котором физически могут отсутствовать некоторые поля

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

Пришлось заняться этим потому что в текущем проекте было большое количество объектов вида:

struct data {
  mandatory_field_one m_one;
  mandatory_field_two m_two;
  mandatory_field_three m_three;

  std::vector<first_opt_attribute_type> m_first_type_attrs;
  std::vector<second_opt_attribute_type> m_second_type_attrs;
  std::vector<third_opt_attribute_type> m_third_type_attrs;
};

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

Объектов типа `data` было много, в некоторых случаях десятки миллионов. И на таком количестве хранение пустых std::vector внутри миллионов объектов типа `data` оказывается очень расточительным (ведь каждый пустой std::vector -- это, как минимум, 24 байта -- size, capacity + указатель на блок с данными).

Объекты же `data` создавались в динамической памяти и ссылки на них хранились как std::unique_ptr.

Причем модель данных не позволяла легко вынести значения опциональных атрибутов из `data` куда-то еще. Например, можно было бы попробовать завести какое-то общее хранилище атрибутов, а в самом `data` тогда хранилась бы ссылка (или индекс) в этом хранилище. Тогда если у объекта атрибутов нет, то в хранилище для объекта ничего нет, а в самом объекте лежит нулевая ссылка. Ну т.е. попробовать бы можно было, но без особых шансов на успех, но зато с большим геморроем 🙁

В общем, хотелось бы, чтобы поля m_first_type_attrs, m_second_type_attrs и m_third_type_attrs таки оставались внутри `data`, но ничего бы не потребляли, если были пустыми.

Здесь бы очень ко двору пришлись бы массивы вроде чего-то такого:

struct data {
  mandatory_field_one m_one;
  mandatory_field_two m_two;
  mandatory_field_three m_three;

  std::size_t m_first_type_attrs_count;
  first_opt_attribute_type m_first_type_attrs[m_first_type_attrs_count];

  std::size_t m_second_type_attrs_count;
  second_opt_attribute_type m_second_type_attrs[m_second_type_attrs_count];

  std::size_t m_third_type_attrs_count;
  third_opt_attribute_type m_third_type_attrs[m_third_type_attrs_count];
};

Но в C++ таких массивов нет. Это во-первых. А во-вторых, даже такой способ хранения, будь он возможен, все равно был бы расточительным. Ведь если для объекта нет атрибутов, то поля *_attrs_count с нулевыми значениями в нем все равно есть. Три поля std::size_t -- это 24 байта, умножаем на десяток миллионов объектов `data` и теряем пару десятков мегабайт на ровном месте 🙁

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

Получилось что-то вроде такого:

// Описание части, которая в объекте присутствует всегда.
struct data_header {
  mandatory_field_one m_one;
  mandatory_field_two m_two;
  mandatory_field_three m_three;

  // Вспомогательные типы для идентификации опциональных полей.
  struct first_attr_tag {};
  struct second_attr_tag {};
  struct third_attr_tag {};

  // Типы опциональных полей.
  using first_attr_vec = compound::vec<first_attr_tag, first_opt_attribute_type>;
  using second_attr_vec = compound::vec<second_attr_tag, second_opt_attribute_type>;
  using third_attr_vec = compound::vec<third_attr_tag, third_opt_attribute_type>;
};

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

std::vector<second_opt_attribute_type> attrs{...};

auto my_data = data::build(
  // Данные для инициализации фиксированной части должны быть всегда.
  data_header{...},
  // А вот опциональные данные нужны только те, которые в объекте присутствуют.
  data::second_attr_vec::move_from(attrs));

// Доступ к полям фиксированной части возможен напрямую.
std::cout << my_data->m_one << std::endl;
std::cout << my_data->m_two << std::endl;

// Доступ к опциональным полям нужно проверять.
if(my_data->has_field<data::second_attr_tag>()) {
  // Поле есть, можно с ним работать.
  for(const auto & a : m_data->get_field_ref<data::second_attr_tag>()) {
    ...
  }
}

Не буду вдаваться в подробности, т.к. все это делалось в закрытом проекте. Но скажу, что весь фокус тут в функции-фабрике build, которая вычисляет сколько же места потребуется (с учетом необходимых выравниваний) и создает обычный динамический массив std::byte. А уже дальше внутри этого массива посредством placement new размещается все то, что в объекте должно присутствовать.

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

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

Что напрягало, как это функциональный стиль C++ного метапрограммирования (работа велась в рамках C++20). Вся эта рекурсия по спискам типов... 😓 Если сталкиваешься с этим самым метапрограммированием раз в пару лет, то непросто на эти рекурсии перестроится.

Еще, конечно же, доставляла тема с std::launder. Но к ней мне придется вернуться еще раз, как минимум.

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

ЗЫ. Кстати говоря, пригодился трюк вот из этой статьи: "Pulling a single item from a C++ parameter pack by its index".

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

[prog.work] Конкурируем на глобальном рынке ;)

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

Дело в том, что если не считать нескольких месяцев, проведенных в ЕПАМе, когда довелось чуть-чуть прикоснулся пятым винтиком в пятисотой шестеренке к разработке продукта для глобального рынка, практически всю свою профессиональную жизнь (до создания собственной фирмы) довелось работать над продуктами, ориентированными на узкий локальный рынок. Даже в компании "Интервэйл", которая одна из первых в РФ стала делать системы мобильного банкинга и держала заметную долю рынка SMS-информирования, все равно это были довольно-таки локальные истории.

А вот когда мы создали свой "СтифСтрим" для того, чтобы оказывать поддержку пользователям SObjectizer-а, да еще и следом, для диверсификации, занялись разработкой RESTinio... Вот тогда до меня вдруг дошло, что наши OpenSource продукты в прямом смысле слова конкурируют на самом что ни есть глобальном рынке. И для того, чтобы чего-то здесь добиться, нужно стремиться быть на одном уровне с лучшими. А лучшие здесь выпускаются в OpenSource, например, Microsoft-ом или Facebook-ом. И вот с ними приходится толкаться локтями.

Признаюсь, для меня, как для троечника, с трудом закончившего университет в "условном Бобруйске"*, это осознание не сказать, что сильно обрадовало 😏

Но как-то свыкся.

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


* Касательно "условного Бобруйска". Меня уже несколько раз на русскоязычных форумах попрекали тем, что нет за плечами диплома МГУ или еще чего попрестижнее. Да самим фактом проживания не в Москве/Питере/Лондоне/Силиконовой долине так же. Посему данный термин мне нравится, не позволяет свалиться в снобизм 😎

четверг, 5 октября 2023 г.

[prog.c++.sobjectizer] Марко Арена начал публиковать серию постов "SObjectizer Tales"

Один из пользователей SObjectizer-а, Марко Арена, начал публиковать серию статей под названием "SObjectizer Tales". Это не очередное руководство о том, как программировать с помощью SObjectizer-а. Это рассказ о том, как человек увидел SObjectizer, что полезного в нем нашел и как он решал стоящие перед ним задачи с помощью нашего инструмента.

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

Содержимое серии на текущий момент:


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

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

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

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

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

Прошу простить мне минуту самолюбования, но да, есть теплое чувство внутри.

понедельник, 24 июля 2023 г.

[work.pr] Прикольно: знакомство с SObjectizer как дополнительный плюс

Не скрою, приятно увидеть такое в чужой вакансии:

Что называется, пустячок, а приятно. А может и не пустячок, а проявление старой мудрости "вода камень точит"

среда, 10 мая 2023 г.

[prog.c++;blog;wow] Интересно, это уже успех или ещё нет? :)))

Посмотрел сегодя откуда ко мне в блог люди приходят. А тут такое:

Не меньне, не больше, а "быстрый ответ" в выдаче Яндекса по запросу "виртуальный деструктор C++ зачем нужен". Однако! :)

Если (а скорее когда) придется проходить собеседование по C++, то на вопрос о виртуальном деструкторе в C++ нужно будет показать этот скриншот.

суббота, 11 февраля 2023 г.

[life] Продолжаю слушать плейлисты дня на Yandex.Music :)

Собственно, вот:

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

вторник, 3 января 2023 г.

[prog.c++.sobjectizer] Бахвальства пост

Прошу простить мне минутку самолюбования, но вот свеженькое с reddit-а:

Отрадно здесь то, что в обсуждении Модели Акторов в C++ ссылки на SObjectizer стали появляться без моего участия :)

Казалось бы мелочь. Но идти к этому пришлось много лет.


Еще интересное нашлось. Некоторое время назад уже упоминал в блоге чужой проект, который использует SObjectizer. Теперь вот обнаружилась статья, которая рассказывает про этот проект: LabNet hardware control software for the Raspberry Pi.


Пара слов про статус проекта. SObjectizer жив. Он используется и не только лишь мной. В этом плане все с ним хорошо.

Есть желание в этом году выпустить SObjectizer-5.8.0, главной фичей которого должно стать решение вот для этой проблемы. Штука о которой речь заходила уже несколько раз и которая в последнее время и мне самому бы пригодилась бы... Но которую вряд ли получится впихнуть в ветку 5.7 с сохранением совместимости, поэтому-то и придется поломать оную совместимость и открыть ветку 5.8.

Но. Но есть большое но :(

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

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

Посему есть понимание того, что хочется сделать. Нет понимания как и когда. Но есть желание. Поэтому как будет, так и будет.

пятница, 30 сентября 2022 г.

[soft.bragging] Третья сотня звезд на GitHub-е для SObjectizer

Однако, триста.

Очередные сто звезд были накоплены практически за год, вторая сотня звезд была набрана в начале сентября 2021-го года.

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

Традиционно уже скажу, что проект живет. В 2022-ом даже получилось сделать полноценный релиз SObjectizer-5.7.4 и so5extra-1.5.0. И пусть это относительно минорные обновления, но на фоне всей той жести и неопределенности, которая творится в мире в последние два года, удалось сделать хотя бы это.

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

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

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

среда, 12 января 2022 г.

[prog.c++] Очень приятно. Да и не пустяк это вовсе

Простите, что опять про SObjectizer, но таки чертовки приятно:

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

Кстати говоря, ребята, которые реализовали ту систему управления сценическим оборудованием, не особо афишируют для какого именно театра она была сделана. Мне же "по блату" намекнули и я до сих пор в нехилом апупении пребываю :) Вот даже и предполагать не мог где в итоге окажется частичка моих трудов.

С SObjectizer-ом вообще есть два примечательных момента, к которым я до сих пор не знаю как относиться.

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

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

Исключение пока что было только одно: более-менее приличное количество вопросов касательно SObjectizer-а назадавал один разработчик из Италии. А вот наши суровые парни, походу, сами во всем разбираются :)

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

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

[soft.bragging] Вторая сотня звезд на GitHub-е для SObjectizer

Однако, двести.

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

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

Пользуясь случаем обозначу текущий статус проекта.

SObjectizer/so5extra не умер. Проект живет. Проект используется. Причем, наверное, вне нашей крохотной компании он используется даже гораздо шире. Хотя информации о том, где и как SO-5 применяется, по прежнему, очень мало. Какие-то сведения до нас доходят отрывочно, случайно и эпизодически. Так, несколько месяцев назад узнал, что SObjectizer применяют на бакэнде в 2GIS.

На данный момент развития проекта нет. Главная причина в том, что для нас самих в SObjectizer-е есть все, что нам сейчас нужно. Добавлять что-то еще просто "шоб было" нет ни смысла, не ресурсов. Так что новая функциоальность в SO-5 появится либо тогда, когда нам самим в нем что-то потребуется, либо если кто-то у нас закажет доработку SObjectizer-а.

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

Поэтому если кто-то сомневается пробовать ли SObjectizer или не пробовать, то не сомневайтесь, пробуйте.

Тем же, кто попробовал, большое спасибо за правильный выбор ;)

понедельник, 12 апреля 2021 г.

[prog.c++] Наглядно на тему сложности освоения SObjectizer-а

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

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

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

О том, что SObjectizer где-то и как-то применяется мы узнаем, как правило, из двух источников:

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

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

Совсем свежий пример в Google-группе. Человек разобрался с SObjectizer-ом, сделал далеко не самый простой имитатор того, что ему нужно в его предметной области, и спросил совета по достаточно интересной и неоднозначной теме.

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

То, что мы вынуждены приостановить работы над нашими OpenSource проектами (включая SObjectizer), не означает, что проект заброшен. Без поддержки, как можете видеть, пользователей не оставляем. Ну а как представится возможность, так и продолжим снабжать SObjectizer/so5extra и RESTinio новыми фичами.

четверг, 11 марта 2021 г.

[prog.c++] Наглядно про "могу помочь вашей команде улучшить качество кода"

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

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

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

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

среда, 29 января 2020 г.

[prog.c++] Почему люди выбирают RESTinio

Давеча мы внезапно получили неплохую реакламу на reddit-е в виде комментария от ned14 (известного, например, как автор Boost.Outcome). Позволю себе процитировать этот комментарий здесь полностью:

I recently had to choose a lightweight modern C++ web framework, and the choices came down to oatpp or https://github.com/Stiffstream/restinio.

I ended up choosing restinio because:

  1. Writing C++ for restinio is much more C++ idiomatic - no macros, one uses {fmt} to append to responses, string views and zero copy are pervasive, it leverages as much of C++ 20 including proposed standard feature reference libraries as possible. This makes sorting out dependencies messy, but it's a once off investment, and https://github.com/cpp-pm/hunter hand waves away the dependency problem in any case.
  2. restinio exposes the ASIO it wraps, which makes the rest of the team feel much more comfortable. ASIO is widely understood, whereas custom internal async i/o frameworks are often sources of surprise. Also, we know how to hook and extend ASIO to do custom stuff, that's team embedded knowledge.
  3. There were recent comparative benchmarks for restinio not written by the author https://github.com/ngrodzitski/test20171219 which showed restinio can be class leading in performance for the thing tested, even marginally edging out Beast for that test.

I don't want any of this to diss oatpp. I just wanted to explain what motivated me to not choose it, which may aid its authors in telling a better story to improve adoption.

For the record, restinio adoption has gone okay so far, a bit of resistance from some team members about some of its design choices, but everybody is just loving {fmt} for efficiently generating responses. The team is literally benchmarking our REST server for production right now, so far looks promising, it definitely can max out a 1Gb NIC, still awaiting the 10Gb and 40Gb NIC results.

Т.е., если не дословно, но по сути:

  1. Работа с RESTinio происходит в более идиоматическом для C++ стиле, нет макросов, применяются fmt, string_view и вообще сделан упор на библиотеки, которые должны стать частью будущих стандартов C++.
  2. RESTinio дает доступ к ASIO, а у команды есть большущий опыт правильного приготовления ASIO, и это как-то безопаснее, чем иметь дело с самодельным фреймворком для асинхронного I/O.
  3. Были найдены сравнительные бенчмарки, которые показывают, что у RESTinio вполне себе конкуретноспособная производительность.
  4. Пока что RESTinio более-менее хорошо зашла проектной команде. Были недовольные теми или иными проектными решениями в RESTinio, но всем нравится применять fmt для формирования ответов на запросы. И прямо сейчас проводятся бенчмарки разрабатываемого командой REST-сервера. Пока что результаты выглядят обнадеживающими.

Было очень приятно получить такой отзыв. Значит не зря мы над RESTinio работаем. Ну а может кому-то этот отзыв поможет сделать правильный выбор ;)

От себя добавлю, что в случае с RESTinio мы еще более открыты к предложениям, чем с SObjectizer. Поскольку RESTinio еще совсем молодой проект, который даже до версии 1.0 пока не добрался, то мы с удовольствием прислушаемся к пожеланиям пользователей для того, чтобы наполнить RESTinio той функциональностью, которая сделает разработку RESTful приложений на C++ простой и приятной.

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

[prog.c++] RESTinio обновился до версии 0.5.0

Наш небольшой проект RESTinio продолжает развиваться. Сегодня была официально выпущена версия 0.5.0, в которой стало возможно использовать кастомные версии http-parser-а. Так что если кому-то нужно поддерживать нестандартные HTTP-методы во входящих запросах, то сейчас понятно, как это делать без вмешательство в потроха RESTinio.

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

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

пятница, 8 февраля 2019 г.

[prog.c++] Первая чужая статья про опыт использования SObjectizer-а

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

Вот эта статья: "Если проект «Театр» используй акторов…"

Автору статьи большое спасибо. Даже два. Больших. Одно за то, что отважился применить SObjectizer в продакшене. Второе за то, что нашел время и поделился своим опытом.

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

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

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

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

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

В-четвертых, интересно развивается история. SCADA Objectizer создавался для решения задач АСУТП. Затем в SObjectizer-е мы от АСУТП-ных задач ушли. Но при этом использовали, можно сказать, АСУТП-ные подходы для разработке на SObjectizer-е других систем (из области телекома и платежных сервисов). Но, тем не менее, наследственность оказалась столь сильной, что SObjectizer в своем нынешнем виде вновь вернулся к задачам АСУТП. Что лично у меня вызвало огромный приступ ностальгии :)


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

PS. Какой же сегодня все-таки хороший повод сгонять в магазин! ;)

понедельник, 17 декабря 2018 г.

[prog.c++] Поддержка unit-тестирования для агентов в SObjectizer-е начинает дышать!

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

Важная штука: средства для тестирования агентов будут входить прямо в состав SObjectizer-5.5. Это значит, что в их реализации нам придется ориентироваться на какое-то подмножество C++11, что нас не радует. Но, думаю, возможность писать тестовые сценарии для агентов без необходимости ставить себе какой-нибудь so_5_extra, сделает SObjectizer привлекательнее для тех, кто еще кипятит не решился сделать выбор в пользу SObjectizer-а (и кого, может быть, смущает двойное лицензирование so_5_extra).

Еще очень и очень многое предстоит сделать, как в коде, так и в документации. Даже то, что реализовано сейчас, представляет из себя грязный черновой код, который еще предстоит доводить напильником, причесывать и документировать. Шансы успеть до Нового Года есть, но это если никаких принципиальных барьеров не возникнет.

Тем не менее, отрадно.