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

О блоге

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

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

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

[life.photo] Характерный портрет: вы и ваш мир моими глазами. Безвозмездно :)

Вы художник? Бармен или музыкант? Или, может быть, коллекционер? Плотник или столяр? Кузнец или слесарь? Владеете маленьким магазинчиком или управляете большим производством? Реставрируете старинные часы или просто починяете примус? Всю жизнь занимаетесь своим любимым делом и хотели бы иметь фото на память?

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

четверг, 3 июля 2025 г.

[prog.c++] Шаблоны против копипасты 11: теперь в сочетании с if constexpr

Был у меня код вроде вот такого ([[nodiscard]] и прочие элементы хорошего стиля убраны для упрощения):

class element_finder_t
{
public:
  search_result_t
  find_element(const search_conditions_t & conditions)
  {
    if(should_use_first_algorithm(conditions))
      return find_by_first_algorithm(conditions);
    else if(should_use_second_algorithm(conditions))
      return find_by_second_algorithm(conditions);
    ...
  }

private:
  search_result_t
  find_by_first_algorithm(const search_conditions_t & conditions)
  {
    ... // Какой-то код для поиска элемента в цикле.
    if(is_conditions_fulfilled(element_info, conditions))
      return element_info;
    ... // Какой-то код для подготовки следующей итерации.
  }

  search_result_t
  find_by_second_algorithm(const search_conditions_t & conditions)
  {
    ... // Какой-то код для поиска элемента в цикле.
    if(is_conditions_fulfilled(element_info, conditions))
      return element_info;
    ... // Какой-то код для подготовки следующей итерации.
  }
};

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

В лоб такое расширение element_finder_t можно было бы сделать так:

вторник, 1 июля 2025 г.

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

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

Фильмы

Маскарад (Mascarade, 2022). Отличное кино. Наверное лучшее, что довелось посмотреть за последние месяцы.

Хищник: Убийца убийц (Predator: Killer of Killers, 2025). Редко даю ссылки на полнометражные мультфильмы. Но этот точно заслуживает внимание тех, кому нравятся фильмы про Хищника.

Пункт назначения: узы крови (Final Destination: Bloodlines, 2025). Мне понравилось, любители жанра могут смело смотреть. Сравнивать с самым первым фильмом "Пункт назначения", который сейчас чуть ли не культовым статусом обладает, не буду, т.к. плохо его помню.

Воздушный перевозчик (La extorsión, 2023). На удивление неплохо. А если бы актеры еще и поменьше кривлялись, то было бы и просто отлично. Но это, наверное, особенность национальной актерской школы.

Игра вдовы (Widow's Game/A La viuda negra, 2025). Простенько. И это не детектив, т.к. быстро становится понятно что к чему. Но как рассказ о незамысловатой, но трагичной, криминальной истории, вполне себе норм. Большим плюсом лично для меня то, что фильм европейский и по своей стилистики сильно отличается от голливудской продукции.

Дело «Мальдорор» (Maldoror, 2024). Специфическое кино. Далеко не шедевр. И советовать его к просмотру сложно. Но оно хотя бы по своей стилистике очень и очень сильно отличается от красочного голливудского гламура.

Новичок (The Amateur, 2025). Снято, вроде бы, нормально. И актеров неплохих подтянули. Но при просмотре возникает ощущение, что смотришь какую-то сказочку, которая и заканчивается счастливым сказочным образом.

Грешники (Sinners, 2025). Как-то по шуму вокруг и восторженным отзывам ждал чего-то вроде "От заката до рассвета", только с неграми, а вышло что-то затянуто нудное и, я бы даже сказал, детское. Сцена после титров слегка спасает происходящее на экране, но не сильно.

Сериалы

Ганстерленд (MobLand, первый сезон, 2025). Круто, конечно, снято. И смотреть интересно. А вот финал разочаровал. Ожидал кровавой бойни на весь Лондон и окрестности. Вышло же не то, чтобы предсказуемо, но без удивления.

Ночной агент (The Night Agent, первый сезон, 2023). Очень простенько, местами предсказуемо, местами совершенно невероятно. Но зато бодренько.

Невозможно поверить (Unbelievable, первый сезон, 2019). В принципе, неплохо. Но в последней серии развели столько соплей, что прям караул, сильно подпортили общее впечатление.

Отдел нераскрытых дел (Dept. Q, первый сезон, 2025). Чем смотреть это с мучением растянутое на 9 серий "нечто" лучше глянуть датский "Мистериум. Начало" от 2013-го года. По тому же произведению и с теми же общими чертами, но гораздо более удачный фильм.

суббота, 28 июня 2025 г.

[prog.c++.dreams] В очередной раз выяснил, что в C++ using это не strong typedef :(

В С++ есть отличная штука под названием using. Позволяет вводить удобные псевдонимы для сложночитаемых имен типов или же прикрывать тонкой завесой детали реализации.

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

using type_a = ...;
using type_b = ...;

void do_something(type_a v) {...}
void do_something(type_b v) {...}

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

Например, раньше было:

using small_data = std::map<some_key, some_value>;
using large_data = std::unordered_map<special_key, some_value>;

using type_a = small_data::iterator;
using type_b = large_data::iterator;

А в один прекрасный момент стало:

using special_key = some_key;

using small_data = std::map<some_key, some_value>;
using large_data = std::map<special_key, some_value>;

И все 🙁
Типы type_a и type_b оказались одинаковыми.

Недавно в очередной раз наступил на подобные грабли, но немного в другом контексте. Было что-то вроде:

namespace processing
{

class processor {...};

template<typename Data>
void
handle(const processor & how, const Data & what)
{
  // Должна быть найдена подходящая функция за счет ADL.
  apply(what, how);
}

// namespace processing

namespace data_type_a
{

struct data {...};

void apply(const data & what, const processing::processor & how) {...}

// namespace data_type_a

namespace data_type_b
{

struct data {...};

void apply(const data & what, const processing::processor & how) {...}

// namespace data_type_b

И т.д.

Т.е. смысл в том, что в конкретном пространстве имен data_type_X должна быть функция apply, который компилятор посредством ADL находит для вызова внутри processing::process.

Все шло хорошо до момента, пока не появились data_type_i и data_type_j, в которых тип data был определен через using:

namespace data_type_i
{

using data = std::map<...>;

void apply(const data & what, const processing::processor & how) {...}

// namespace data_type_i

namespace data_type_j
{

using data = std::vector<...>;

void apply(const data & what, const processing::processor & how) {...}

// namespace data_type_j

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

А дело в том, что если заменить псевдонимы, то получались функции вида:

void apply(const std::map<...> &, const processing::processor&);
void apply(const std::vector<...> &, const processing::processor&);

И естественно, что ADL не мог их найти ни в пространстве имен std, ни в processing.

Было больно, т.к. пришлось отказаться от очень красивого способа привязки специфических данных к их обработчику 😪

В очередной раз захотелось, чтобы using в С++ мог работать как strong typedef. Чтобы можно было написать что-то вроде:

namespace data_type_i
{

using(new) data = std::map<int, std::string>;

// namespace data_type_i

И чтобы компилятор начал считать, что data и std::map<int, std::string> теперь разные типы. И что тип data теперь принадлежит пространству имен data_type_i, а не std.

PS. В свете добавления в C++ рефлексии может оказаться, что наколхозить какой-то нестандартый strong_typedef_for, типа:

namespace data_type_i
{

using data = my::strong_typedef_for< std::map<int, std::string> >;

// namespace data_type_i

через рефлексию будет быстрее и проще, чем дождаться появления using(new) в стандарте. Обычная традиция C++: если что-то можно собрать своими руками дендро-фекальным методом, то включать в стандарт удобный и нормальный вариант никто не будет.

понедельник, 23 июня 2025 г.

[prog.c++] Есть ли теперь смысл при разработке C++ библиотек придерживаться не самых свежих стандартов?

В заголовок поста вынесен вопрос, который занимает меня в последние пару-тройку лет. A последний год так точно.

Поясню в чем дело.

В C++ на протяжении десятков лет есть специфическая картина: существует официальный С++ на "бумаге", т.е. описанный в соответствующем стандарте, будь то С++98, С++11 или C++23, и есть реальный C++, доступный конкретному разработчику в конкретном компиляторе. И, как правило, в имеющихся в нашем распоряжении компиляторах далеко не все фичи из самого последнего официального стандарта реализованы.

Эта картина особенно важна при разработке кросс-платформенных библиотек. Если ты хочешь, чтобы твою библиотеку использовали разные люди в разных проектах и на разных платформах, то ты вынужден занижать версию стандарта. Например, вместо C++23 (который вроде как уже два года нам доступен) делать библиотеку на C++17 или даже C++14.

При разработке софта для конечного пользователя ситуация, зачастую, гораздо проще: очень часто софт пишется под конкретную платформу и вполне можно заложиться на конкретный компилятор. Но с разработкой библиотек не так. В проекте X библиотека может работать уже в режиме C++23, тогда как в проекте Y -- все еще в C++17.

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

Но после C++20 смиряться все сложнее.

C++11 стал совсем другим языком в сравнении с C++98. Два следующих стандарта, C++14 и С++17, инкрементально улучшали C++, но не могу сказать, что они переводили язык на какой-то принципиально другой уровень (даже не смотря на такие крутые фичи C++17 как structured binding и if constexpr). А вот начиная с C++20 все принципиально поменялось:

  • C++20 добавил концепты и operator spaceship (про модули промолчу ибо не пробовал);
  • С++23 добавил deducing this. Не смотря на то, что (на мой взгляд) сделали это через одно место (и можно было бы по-другому), но таки важную реальную проблему эта фича решает;
  • С++26 добавляет compile-time рефексию и, я очень надеюсь, контракты.

Все это вместе, не побоюсь этого слова, делает из C++ совсем другой язык в такой же степени, как C++11 после C++98. Если не в большей.

И вот глядя на все это великолепие в свежих стандартах С++ я не могу найти для самого себя ответ на вопрос: а зачем на при разработке своих библиотек оставаться на C++17/14/11?

Вот реально.

Ладно бы нам за наши OpenSource проекты платили. Но этого нет. И RESTinio, и SObjectizer приносят нам деньги не напрямую, а приводя к нам клиентов через репутацию. Зачастую новые фичи в тот же SObjectizer добавляются "just for fun" -- просто что-то выглядит интересным или представляет из себя вызов, поэтому делаешь это ради получения удовольствия.

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

Поэтому чем дальше, тем больше мыслей о том, чтобы в следующем году перевести SObjectizer сразу на C++26. На счет RESTinio ситуация посложнее, но если представиться возможность плотно поработать над RESTinio, то и там тоже можно будет сразу же брать С++26.

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

вторник, 17 июня 2025 г.

[prog.c++] Еще раз про noexcept в C++: это очень специфическая гарантия отсутствия исключений

Увидел вот такое в ленте LinkedIn:

У этого "откровения" полсотни лайков и несколько репостов. Ужас.

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

И уж совсем дико читать про "Makes code safer and more predictable" и, особенно, про "Prevents unexpected termination during exception propagation". К сожалению, пока в C++ недостаточно средств для того, чтобы noexcept было удобно использовать для написания безопасного и предсказуемого кода. Но это тема отдельного большого разговора.

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