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

О блоге

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

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

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

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

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

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

суббота, 24 сентября 2022 г.

[prog.flame] Пару слов на тему cpp2/cppfront от Герба Саттера

Досмотрел выступление Герба Саттера на CppCon 2022, где он рассказывал про свой эксперимент на тему cpp2 и транслятора cppfront.

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

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

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

Но, с другой стороны, вот что меня сильно смущает.

среда, 21 сентября 2022 г.

[prog.c++] Отказался от использования маркера can_throw в arataga

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

В двух словах: в функции/методы, в которых можно было спокойно выбрасывать исключения, передавался специальный маркер типа can_throw_t. Этот маркер указывал, что функция/метод вызывается из контекста, в котором исключения перехватываются и обрабатываются. Соответственно, если такой маркер в функции/методе есть, значит я могу сделать throw или вызвать какую-то бросающую исключение операцию. А если такого маркера нет, то мне нужно обрамить свои действия блоком try-catch.

К появлению can_throw привело то, что в arataga пришлось писать много callback-ов, которые вызывались при выполнении IO-операций через Asio и парсинге HTTP-протокола посредством http_parser. В таких контекстах исключения нельзя было выпускать наружу и требовалось понимать, пишу ли я сейчас код для callback-а или для чего-то другого.

Надо сказать, что прием can_throw мной всегда оценивался как не вполне однозначный.

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

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

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

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

Проект arataga интересен еще и тем, что это один из двух проектов за последние несколько лет, в которых я серьезно задумывался над использованием stackfull-короутин. В arataga в виде короутин можно было бы реализовать обработчики отдельных подключений (т.е. модель coroutine per connection). И, если бы в SObjectizer была поддержка stackfull-короутин "искаропки", то может быть именно с модели coroutine per connection я реализацию arataga и начал бы.

Но в SObjectizer поддержки stackfull-короутин нет. Все еще нет.

Давеча у меня появилось некоторое время чтобы подумать о том, что же в SObjectizer можно добавить. Вариантов для рассмотрения не так, чтобы много, и stackfull-короутины в short-list-е.

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

Дабы освежить в памяти детали работы arataga погрузился в код...

И слегка прифигел от количество can_throw в реализации connection-handler-ов.

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

Обилие can_throw обескуражило настолько, что стало понято, что с этим нужно что-то делать. Только после этого можно было задуматься о попытке переписать arataga на короутинах.

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

Поэтому в итоге can_throw из кода arataga был полностью изъят.

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

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

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


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

PS. Я заикнулся о том, что рассматриваю возможность добавления stackfull-короутин в SObjectizer. Но тут все очень не просто. Погружение в arataga наводит на мысль о том, что stackfull-короутины здесь могут и не помочь. Так что еще рано говорить о том, что решение о добавлении stackfull-короутин в SObjectizer принято. Тут еще нужно думать и думать, да и вообще это уже совсем другая история.

воскресенье, 18 сентября 2022 г.

[photo.wtf] Что-то меня нехило так бомануло при попытке посмотреть запись стрима с разбором композиции в фотографии учеников

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

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

И уже на втором снимке меня бомбануло так, как не бомбило уже давно.

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

И все!

Ну ахиреть, блин.

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

суббота, 17 сентября 2022 г.

[prog] В склерозник: статья с поверхностным разбором принципов решения задач на coding interview

В кои-то веки в дайджесте от Medium проскочила интересная ссылка: Popular Problem-Solving Approaches in Data Structures and Algorithms.

В ней кратко описываются несколько категорий подходов к решению алгоритмических задачек, которые любят использовать на собеседованиях. Плюс ссылки на примеры этих самых задач с разборами возможных решений (я так понял, что все ссылки ведут на https://www.enjoyalgorithms.com/coding-interview/).

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

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

[prog.c++] Хочется странного: структуры, которые можно инициализировать только посредством designated initializers

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

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

frames_buffer_t make_buffer(size_t frame_size, size_t frames_count);

При вызове такой функции запросто можно перепутать аргументы местами и написать:

auto buf = make_buffer(32, 1024);

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

Это касается не только подряд идущих аргументов одного типа, тоже самое можно отнести и к структурам, в которых подряд идут однотипные поля. Вроде вот такого:

struct capture_params_t {
  size_t m_frame_size;
  size_t m_frames_count;
  ...
};

Поскольку, опять же, если мы оперируем только примитивными типами, то глядя в код сложно понять, все ли с ним нормально:

capturing_stream_t make_stream(const capture_params_t & params) {
  auto buf = make_buffer(params.m_frames_count, params.m_frame_size);
  ...
}

Или даже так:

capture_params_t tune_params(const capture_params_t & default_values) {
  capture_params_t result{ default_values };
  if(has_enough_memory()) {
    result.m_frame_size = default_values.m_frames_count * 4u;
    ...
  }
  ...
  return result;
}

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

Самый простой вариант в C++ после C++11 -- это что-то вроде:

struct frame_size_t { size_t m_value; };
struct frames_count_t { size_t m_value; }

frames_buffer_t make_buffer(frame_size_t frame_size, frames_count_t frames_count);

Что приводит к тому, что в коде уже приходится писать так:

auto buf = make_buffer(frame_size_t{32}, frames_count_t{1024});

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

В общем, в последнее время пытаюсь регулярно применять strong typedef в том или ином виде.

Но время от времени сталкиваюсь со случаями, когда введение strong typedef в виде отдельных типов выглядит как overkill.

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

class probe_result_t
{
    friend class data_collector_t;

    int m_read_pos;
    int m_write_pos;
    int m_size;

    probe_result_t(int read_pos, int write_pos, int size)
        : m_read_pos{read_pos}, m_write_pos{write_pos}, m_size{size}
    {}

public:
    [[nodiscard]] int size() const noexcept { return m_size; }
};

И вот у него в конструктор передаются параметры одного типа, что мне не нравится.

Но и не хочется превращать этот простой класс во что-то подобное:

class probe_result_t
{
    friend class data_collector_t;

    struct read_pos_t { int m_v; };
    struct write_pos_t { int m_v; };
    struct size_t { int m_v; };

    int m_read_pos;
    int m_write_pos;
    int m_size;

    probe_result_t(read_pos_t read_pos, write_pos_t write_pos, size_t size)
        : m_read_pos{read_pos.m_v}, m_write_pos{write_pos.m_v}, m_size{size.m_v}
    {}

public:
    [[nodiscard]] int size() const noexcept { return m_size; }
};

Как по мне, так отличным решением здесь могло бы стать использование вспомогательной структуры и добавленных в C++20 designated initializers:

class probe_result_t
{
    friend class data_collector_t;

    struct params_t
    {
        int m_read_pos;
        int m_write_pos;
        int m_size;
    };

    params_t m_v;

    probe_result_t(params_t params)
        : m_v{params}
    {}

public:
    [[nodiscard]] int size() const noexcept { return m_v.m_size; }
};

Создавать экземпляр probe_result_t можно было бы просто и наглядно:

probe_result_t data_collector_t::probe_collected_data()
{
    ...
    return { { .m_read_pos = rp, .m_write_pos = wp, .m_size = ds } };
}

Но вот беда... Нельзя в C++ указать, что экземпляр структуры можно проинициализировать только посредством designated initializers. Старый-добрый initializer list вполне себе будет работать:

probe_result_t data_collector_t::probe_collected_data()
{
    ...
    // Так можно написать, а хотелось бы, чтобы это было под запретом.
    return { { rp, wp, ds } };
}

Наверное, мне таки нужны именованные аргументы, которые есть в некоторых других языках программирования. Но в C++ именованных аргументов точно не будет. А вот designated initializers уже есть...