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

О блоге

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

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

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

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

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

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

пятница, 18 октября 2024 г.

[prog.flame] Адекватен ли выбор языка программирования для такой задачи (Rust для Radicle)?

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

Недавно узнал о Radicle -- это что-то вроде GitLab и Gitea, т.е. инструмент для совместной работы над программным кодом.

Radicle написан на Rust-е.

Не на Python-е. Не на Ruby. Не на Node.js. Не на Java или C#. Не на Go. А на Rust-е.

Как по мне, так это почти тоже самое, что и GitLab, написанный на C++. С поправкой на то, что, во-первых, Rust -- это можно и молодежно. И, во-вторых, для программирования Web-приложений все-таки Rust побезопаснее C++ будет. Но, в общем-то, тоже самое, вид в профиль.

Посему лично я в недоумении и сам бы для проекта, подобного Radicle, точно бы не стал брать C++ или Rust. Как по мне, так здесь достаточно языка со сборкой мусора: Java, Kotlin, C#, возможно даже Go (простите, динамика в лице Python/Ruby/JavaScript идет лесом, т.к. не из тех мазохистов, которые делают что-то большое на динамически-типизированных языках).

А вот брать нативный язык без GC с претензией на околосистемность... Зачем?

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


Если не лень, то напишите в комментариях, плиз, какой бы вы язык программирования предпочли для реализации проекта по типу Radicle/GitLab/Gitea. Я бы лично выбирал бы между C#, Kotlin и Go (хотя не на одном из них не программировал), но точно бы не C++ и не Rust.

вторник, 15 октября 2024 г.

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

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

struct A {};

struct B : public A {
    A _first;
};

Какой у нее размер? С учетом того, что даже у пустой структуры размер будет, как минимум, 1 байт. И что в C++ есть empty base class optimization.

То, что B наследуется от пустого A благодаря empty base class optimization не увеличивает размер B. Т.е. размер B будет определяться размером поля B::_first, а это один байт. Следовательно, sizeof(B) должен быть равен единице.

На самом деле нет.

Дело в адресах для объекта B и для его поля B::_first:

B b;
A * p1 = &b; // Это легально т.к. B есть A благодаря наследованию.
A * p2 = &b._first;

assert(p1 != p2);

Объект типа B является объектом типа A из-за наследования. Соответственно, адрес объекта типа B будет и адресом объекта типа A.

Внутри типа B есть еще один объект типа A. И у него так же есть свой адрес.

При этом объект B и его поле B::_first -- это два разных объекта типа A.

А в C++ у двух разных объектов одного типа не может быть одинаковых адресов.

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

Убедиться в этом можно на wandbox - цынк

Информация об этой особенности C++ найдена здесь.

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

пятница, 11 октября 2024 г.

[life.work] 30 лет профессионального программизма

Как же все-таки летит время. Вроде бы недавно публиковал аналогичный пост, но про 25 лет, а тут хоба! И уже 30.

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

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

среда, 9 октября 2024 г.

[prog.data.structures] В склерозник: ссылки на тему prefix/radix tree

Довелось недавно столкнуться с темой prefix/radix деревьев. По ходу изучения нашел несколько ссылок, которые для меня оказались полезными, поэтому зафиксирую их в склерозник, чтобы было проще найти в будущем.

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

Adaptive Radix Tree. Собственно, основная PDF-ка, с которой и следует начинать.

HAT-trie, a cache-conscious trie. Обзор сразу нескольких подходов к реализации префиксных деревьев и хеш-таблицы с переходом к рассказу об их комбинации HAT-trie. Плюс реализация этой идеи на C++.

Crit-bit trees и qp tries and crit-bit tries. Еще несколько вариаций на тему префиксных деревьев.

Akamai Radix Tree. Реализация на C++ radix tree от Akamai. Очень интересно если хочется посмотреть, как это дело можно реализовать. Хотя оформлена библиотека (в плане описания, документации, примеров и комментариев в коде), как по мне, бестолково. Поэтому разбираться не просто + реализация на многоэтажных шаблонах, так что требуется хорошее знание C++.

Compressing dictionaries with a DAWG и Directed Acyclic Word Graphs. Пара статей вокруг идей о том, как можно сократить объем данных в префиксном дереве.

Ну и вот эта ссылка регулярно всплывала, хотя напрямую к теме, вроде бы, не относится: Implementation of sparse_hash_map, dense_hash_map, and sparsetable.

От себя добавлю, что для сценария, в котором мне потребовалось что-то вроде prefix/radix дерево, "классический" вариант префиксного дерева вел к слишком большому расходу памяти. И, как я понял, это характерно для подобного рода деревьев, поэтому как раз и есть статьи о том, как уменьшить объем данных.

пятница, 4 октября 2024 г.

[prog.c++] Использование одного аргумента шаблона (aka Traits) вместо нескольких

В догонку ко вчерашнему посту про недостающую функциональность в std::vector.

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

Кстати говоря, есть у меня ощущение, что в любом C++ном проекте, который писали обычные люди, вроде меня, а не монстры, вроде Девида Вандервуда или Барри Ревзина, куча кода поломается, если в std::vector начнут использовать собственные аллокаторы. Поломается потому, что код написан в стиле:

void do_something(const std::vector<int> & data) {...}

И даже шаблонный код написан вот так:

template<typename T>
void do_something(const std::vector<T> & data) {...}

а не вот так (хотя бы вот так):

template<typename T, template Allocator>
void do_something(const std::vector<T, Allocator> & data) {...}

Впрочем, это уже совсем другая история...

Но если пофантазировать?

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

Это подход на базе Traits. Уже не помню, откуда про него узнал, не удивлюсь, если из книг Александреску. Но подход уже старый и мы, например, применяем его в RESTinio.

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

Например, для std::vector это могло бы выглядеть так:

template<typename T>
struct default_vector_traits {
  using allocator = std::allocator<T>;
};

template<typename T, typename Traits = default_vector_traits<T> >
class vector {...};

И если бы со временем нам бы потребовалось добавить в шаблон std::vector еще один параметр (тот же нужный мне growth_policy), то это можно было бы сделать не меняя списка параметров для std::vector:

struct default_vector_growth_policy {
  std::size_t operator()(std::size_t current_capacity) const {
    // Код примерный, прошу помидорами не бросаться ;)
    return (current_capacity > 1 ? static_cast<std::size_t>(capacity * 1.5) : 2);
  }
};

template<typename T>
struct default_vector_traits {
  using allocator = std::allocator<T>;
  growth_policy = default_vector_growth_policy;
};

template<typename T, typename Traits = default_vector_traits<T> >
class vector {...};

И если бы мне захотелось использовать с векторами собственную политику роста емкости, то мне бы потребовалось всего лишь:

struct my_growth_policy {
  std::size_t operator()(std::size_t current_capacity) const {...}
};

template<typename T>
struct my_vector_traits : public std::default_vector_traits<T> {
  using growth_policy = my_growth_policy;
};

using my_int_vector = std::vector<int, my_vector_traits<T>>;

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