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

[proc.flame] Писать код нужно так, чтобы было очевидно, что в нем нет ошибок, или же так чтобы...

...было неочевидно, есть ли в нем ошибки.

Афоризм классный, жизненный, не мой :)

В том, что он жизненный, мне довелось убедиться на днях. Захотелось посмотреть, насколько безопасно передавать уже использовавшийся AVPacket в av_read_frame. Здравый смысл подсказывал, что должно быть безопасно и что внутри av_read_frame сам FFMPEG должен вызывать av_packet_unref(). Но хотелось убедиться.

Заглянул в потроха FFMEG, дошел до внутренней функции read_frame_internal и слегка офигел. Этот самый афоризм тут же вспомнился. В той его части, которая про "неочевидно, есть ли в нем ошибки" :(

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

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

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


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

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

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

class AVPacketCleaner {
  AVPacket * pkt_;
  bool committed_{false};
public:
  AVPacketCleaner(AVPacket * pkt) : pkt_{pkt} {}
  ~AVPacketCleaner() { if(!committed_) av_packet_unref(pkt_); }
  void commit() noexcept { committed_ = true; }
};

Тогда бы в read_frame_internal (или даже выше, еще в av_read_frame) можно было бы написать что-то вроде:

int av_read_frame(AVFormatContext * s, AVPacket * pkt) {
  ...
  AVPacketCleaner cleaner{pkt};
  ...
  // Перед тем return-ом, который возвращает признак успеха:
  cleaner.commit();
  return 0;
err:
  ... // Обработка ошибок.
}

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

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

int av_read_frame(AVFormatContext * s, AVPacket * pkt) {
  ...
  AVPacket tmp_pkt{...}; // Создание временного экземпляра.
  ... // Далее работа только с tmp_pkt.
  // Перед тем return-ом, который возвращает признак успеха:
  swap(tmp_pkt, *pkt);
  return 0;
err:
  ... // Обработка ошибок.
}

При этом подразумевается, что у AVPacket есть деструктор, который вызывает av_packet_unref().

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

четверг, 1 июня 2023 г.

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

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

Фильмы

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

Ренфилд (Renfield, 2023). Нормальный такой образчик жанра "треш, угар" + "кишки, кровь, раз3.14дорасило". В качестве развлекательного аттракциона для просмотра с отключенными мозгами мне зашло. Но я и любитель такого жанра.

Горизонт в огне (Couleurs de l'incendie, 2022). Сказочка для взрослых (или не очень). Но зато очень красиво снятая.

Переводчик (The Covenant, 2023). Средней руки военный боевик с хэппи-эндом. На меня произвел не самые лучшие впечатления (хотя сделан добротно) т.к. от Гая Ричи ждешь большего. Ну и странно смотреть сказочку про спасение отдельно взятого коллаборанта после того, как американцы сбежали из Афганистана и оставили там без какой-либо помощи десятки тысяч подобных коллаборантов.

Подземелья и драконы: Честь среди воров (Dungeons & Dragons: Honor Among Thieves, 2023). Наверное, неплохой вариант для семейного просмотра с детьми младшего школьного возраста, особенно если они знакомы с настолкой "Dungeons & Dragons".

Экзорцист Ватикана (The Pope's Exorcist, 2023). Далеко не шедевр, но вполне себе крепкий образчик жанра. Так что любители фильмов про изгнание демонов могут смело смотреть: нового ничего не увидите, но и плеваться вряд ли будете.

Мизантроп (To Catch A Killer, 2023). Потенциально интересная история, которая была очень скучно и невнятно рассказана. Да еще и зачем-то приправлена повесточкой. Вот серьезно, не понимаю, что бы изменилось в фильме, если бы этого акцента на повесточке не было бы.

Мать (The Mother, 2023). Первая половина фильма, где и сосредоточен основной экшОн, откровенно слабая, но ее хотя бы можно смотреть. Вторая же половина редкостная муть. Смело можно пройти мимо этого фильма.

Сериалы

Перри Мейсон (Perry Mason, первый сезон, 2020). Снято красиво, актеры играют хорошо. Пожалуй, первый сериал после отсмотренного пару месяцев назад "Больница Никербокер", который по техническому исполнению и уровню погружения в происходящее может сравниться с "больницей". Но вот при попытке связать логически происходящие на экране события слишком уж часто возникает ощущение "ну что за фигня!" Что испортило впечатление от "Перри Мейсон" настолько, что второй сезон даже и не захотелось смотреть.

Мосгаз. Последнее дело Черкасова (2023). Не рекомендую. Даже тем, кому предыдущие сезоны "Мосгаза" зашли. Слишком уж много моментов, когда остается только риторически вопрошать "Ну как же так?"

Калейдоскоп (Kaleidoscope, первый сезон, 2023). Было пару прикольных моментов, которые мне понравились. Но в целом редкостная хрень.

понедельник, 29 мая 2023 г.

[work] Заказчик открыл вакансию на проекте в котором я принимаю участие

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

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

Проект включает в себя задачи по анализу видео- и аудио-данных, инфраструктурную часть по сбору этих данных и организации их обработки, UI-часть в нескольких вариантах (Desktop, Web). Я занимаюсь инфраструктурной частью и здесь уже пройден некоторый путь. А вот UI-часть находится на ранней стадии своего развития и именно в этом направлении сейчас лежит основной объем работ.

Под развитие UI и возможностей по скриптованию на Python компания VisionSmarts и открыла вакансию:

C++ VIDEO SURVEILLANCE & HOME AUTOMATION (WITH SOME PYTHON)

Разработка ведется на C++ (стандарт C++17) и Python. В качестве основных задействованных библиотек FFMPEG, SDL, Dear ImGui, SObjectizer, WebSockets++ и кастомная библиотека для поддержки такого понятия как "shared variables" и организации UI поверх Dear ImGui (в том числе и для remote UI). Для Web-интерфейса применяется WASM и emscripten. Текущие платформы Linux и Windows.

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

Самостоятельность + ответственность требуются еще и потому, что проект разрабатывается небольшими силами: на пике над проектом работало до трех человек (включая меня) и сам заказчик. Значительную часть времени проектом занимался я один.

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

Специально подчеркну, что вакансия открыта у заказчика, а не у нас, обсуждать условия (в том числе и уровень оплаты) нужно непосредственно с VisualSmarts. Ну и стоит обратить внимание на вот эти важные моменты в описании вакансии:

  • We are based in Belgium but you can be anywhere in the world (except countries currently hit with international sanctions).
  • This is for a remote, part-time, freelance job. Only independent developers and small employee-owned firms please.

PS. Я сам в этом проекте уже почти два года, большей частью в качестве part-time разработчика.

четверг, 25 мая 2023 г.

[prog] Ошибочность ощущения что стал меньше ошибаться :)))

Когда я начал программировать, а было это уже давным-давно, то главным впечатлением было "как же много мы, люди, ошибаемся". Про древнее высказывание "Errare humanum est" уже тогда был наслышан, но не подозревал, насколько это верно :)

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

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

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

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

Errare humanum est, короче говоря.

Так и живем ;)

понедельник, 22 мая 2023 г.

[prog.c++] Так вот на счет AVChannelLayout...

...в качестве дополнения к этому посту: Хочется странного: особое отношение C++ного компилятора к структурам, объявленным как extern "C". Попробую вспомнить особенность перехода с FFMPEG 4.4 на FFMPEG 5.1, связанную с AVChannelLayout.

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

Приходить аудио может в разных форматах (благо FFMPEG отличается всеядностью), а вот для воспроизведения должен использоваться зафиксированный в коде формат. Для записи также должен использоваться зафиксированный в коде формат, но (ЕМНИП) не такой, как для воспроизведения.

Кроме того, снятые AVFrame протаскиваются через цепочку SObjectizer-овских агентов и i-й агент в цепочке, в принципе, может не иметь всей информации об исходном потоке и его состоянии. Т.е. агент, который напрямую дергает FFMPEG, обнаруживает разрыв и переподключение к камере (соответственно, у него пересоздаются AVCodecContext-ы), а вот последующие агенты могут информацию о переподключениях не получить и не узнать, что новые AVFrame содержат аудио-данные уже в другом формате.

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

Для этого была сделана простая структурка, содержащая минимум полей: channel_layout в виде единственного std::int64_t, sample_fmt в виде AVSampleFormat и sample_rate в виде обычного int-а. В общем, тривиальный POD тип с constexpr-конструктором. Экземпляры этого типа используются и для того, чтобы зафиксировать формат аудио для воспроизведения/записи, и для того, чтобы сохранить информацию о параметрах текущего аудио-потока.

Благодаря тому, что описание формата можно было объявлять как constexpr константу, то в нескольких местах кода были сделаны static_assert-ы, для того, чтобы в compile-time проверять, что написанный под определенный аудио-формат код остается актуальным (т.е. если где-то описание формата вдруг поменяли, а код не поправили, то это будет обнаружено компилятором).

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

Но вот в FFMPEG 5.1 целочисленное поле channel_layout задеприкейтили (но хотя бы поддерживают), и ввели новый тип данных AVChannelLayout, который является совокупностью из channel_layout+nb_channel из FFMPEG 4.4, но не только. Экземпляр AVChannelLayout теперь может содержать и дополнительную информацию. В том числе и информацию, которая расположена в динамической памяти, т.е. в AVChannelLayout может лежать и вполне себе владеющий указатель.

Поскольку сейчас AVChannelLayout -- это отнюдь не простая структура, то пришлось менять существующий в проекте тип, описывающий формат аудио. Т.е. поле channel_layout типа std::int64_t нужно было заменить на ch_layout типа AVChannelLayout.

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

пятница, 19 мая 2023 г.

[linux.kde] Способ восстановления после сбоя в Kubuntu 22.04 и KDE Plasma 5.24.7

Продолжение темы, начавшейся около месяца назад: [linux.kde.help-wanted] Столкнулся с проблемой в Kubuntu 22.04 и KDE Plasma 5.24.7.

Насколько я смог понять, проблема в падении kwin_x11.

Соответственно, чтобы восстановиться без перезагрузки системы нужно выполнить:

systemctl restart --user plasma-kwin_x11.service

Если есть возможность запустить kconsole, то это можно сделать прямо в сбойнувшей сессии. Если же такой возможности нет, то тогда по Ctrl+Shift+F2 заходим на другой терминал, логинимся и уже пробуем сделать это в новой сессии.

Так же, если через Ctrl+Shift+F2 зашли с другого терминала, то можно сделать:

DISPLAY=:0 kwin_x11 --replace

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

PS. Выяснить это удалось благодаря чистому везению. Однажды kwin_x11 сбойнул так, что KDE умудрился его рестартовать и сформировать отчет о сбое для отправки в KDE. Вот как раз в этом отчете получилось увидеть kwin_x11, ну а дальше поиск в Интернете... Как мне удалось понять, в Kubuntu 22.04 этот самый kwin_x11 сбоит у многих, я не один такой счастливчик. Остается надеятся, что пофиксят рано или поздно.

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

[prog] И так бывает...

Потребовалось сегодня разобраться с тем, как работает vcpkg в одной специфической ситуации. Причем не только на уровне входящих в его состав CMake скриптов, но оказалось нужно заглянуть в C++ную часть. C++ная же часть живет в отдельном репозитории, vcpkg-tool. И вот сделал я себе под Windows локальный git clone для vcpkg-tool... А Windows Defender такой: "Хлоп! Троян!"

Причем у vcpkg-tool даже раздела Issues нет. Так что написал в Issues для самого vcpkg, посмотрим что ответят (если ответят).

Upd. Ответили, что это false-positive у Windows Defender. В связи с этим интересно, а vcpkg и всякую лабуду для Azure в MS не под Windows разрабатывают? Или разрабатывают-то под Windows, но ни у кого из разработчиков Defender не включен? ;)

PS. Под Windows потому что нужно было разобраться с внезапными проблемами сборки проекта именно под Windows. Так-то я в последние года 4 в основном под Linux-ом сижу.

пятница, 12 мая 2023 г.

[prog.c++] Хочется странного: особое отношение C++ного компилятора к структурам, объявленным как extern "C"

Я тут давеча в текущем проекте обновлялся с FFMPEG 4.4 на 5.1 и столкнулся с необходимостью использовать новый FFMPEG-шный тип AVChannelLayout.

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

А раз может быть, то работать с экземплярами AVChannelLayout нужно по придуманным разрабами FFMPEG правилами: инициализация посредством нескольких (черезжопных, на мой взгляд) способов, копирование через av_channel_layout_copy, очистка перед уничтожением посредством av_channel_layout_uninit.

Но проблема в том, что С++ный компилятор про все эти правила не знает. Ну это же обычная структура, для которой C++ный компилятор тупо и автоматически прикручивает конструктор и оператор копирования.

Побитового копирования.

Что недопустимо для структур с владеющими указателями внутри.

И вот чтобы не наступать на грабли непреднамеренного случайного копирования одного экземпляра AVChannelLayout в другой мне захотелось странного: если C++ный компилятор видит структуру, которая объявлена как extern "C", то пусть он не генерирует для них конструктор и оператор копирования по умолчанию, а объявляет их как delete.

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

PS. Вообще, любопытно было столкнуться с типом вроде AVChannelLayout в C++ном коде. Если найду силы, то попытаюсь описать свои приключения/впечатления в отдельном посте. Но не обещаю, к сожалению. Будем посмотреть. Upd: вот и продолжение.

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

[work.sadness] РБ огораживают от Swift-а и как-то ссыкотно становится...

Вынужден ссылаться на dev.by, но что есть, то есть: Приорбанку перекрывают переводы через Swift в USD (тыц и еще раз тыц). Вроде бы с EUR пока все работает, но невольно напрашивается вопрос "как долго"?

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

И если Приору отрубят и переводы в EUR, то...

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

PS. Работу программистом по найму не ищу, интересен только контракт для СтифСтрима. Если решусь закрыть компанию и начну искать для себя место в чьем-то штате, то об этом будет отдельный блог-пост :)

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

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

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

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

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

[management] Наглядная иллюстрация для начинающих разработчиков, мечтающих вырасти в настоящих менеджеров

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

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

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

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

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

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

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

А вот на RSDN-е давеча нашлось хорошее описание типичной ситуации, с которой приходится сталкиваться менеджеру программистов:

На моём уровне — собираю статистику по команде. Кто сколько items закрыл, средний размер item, как быстро закрывается в среднем каждым из членов команды и т.д и т.п. ... Поднимается вопрос, что имярек делает всё в N раз медленнее других, требует много внимания, снижает среднюю продуктивность команды, не прогрессирует последние M месяцев. Идёшь к владельцу продукта, показываешь статистику, даёшь конкретные примеры того, как он тебя отвлекает от твоих очень важных дел на всякую мелочь, предлагаешь перевести его куда-то в другую команду, где не требуется самостоятельно искать решения, если есть куда переводить и просишь разрешения на найм другого. При этом подумай о том, есть ли способ исправить ситуацию перед тем, как это делать. Потому, что владелец продукта может задать вопрос, а можно ли это как-то исправить, ибо замена работника — удовольствие дорогое. Ну, или как вариант, обнаружь, что имярек ничем от других по производительности не отличается и это просто субъективное ощущение. Если субъективное и только у тебя (посоветуйся с лидами и теми разрабами, с которыми у тебя полностью доверительные отношения — т.е. теми, кто тебе доверяет настолько, чтобы обсуждать деликатные вопросы открыто), то забей на эти ощущения и пусть работает. Если у всех — то это уже мораль команды и к владельцу идёшь уже с именно с акцентом на это.

У нас было два показательных случая с onshore FTE.

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

Второй случай — человек вообще не коммитит нифига, ссылаясь на занятость на внедрении нашего продукта у клиента. На внедрении мне сказали, что и там он нихрена не делает. Сбор статистики, разговор с владельцем продукта, performance plan, перевод в другие команды. Расстанутся ли с ним по итогу или он возьмётся за ум и станет звездой в другой команде — это лично мне не так уж важно.

Т.е. скучный учет и контроль.

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

понедельник, 1 мая 2023 г.

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

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

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

Фильмы

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

65 (2023). Даже и не знаю. Может для семейного просмотра с детьми возрастом около 7 лет такое и зайдет. Но взрослым на такое кино лучше время не тратить.

Убить Пок-сун (Gil Boksun, 2023). Заунывная ерунда. Есть несколько посредственных экшен сцен, но на фоне общего хронометража они погоды не делают.

Координаты "Цитадель" (Black Site, 2022). Откровенная фигня, как по замыслу, так и по исполнению. Разве что Мишель Монахэн прекрасно выглядит для своих лет и это, пожалуй, единственное достоинство данного фильма.

Сериалы

Северная мафия (Poromafia, первый сезон, 2023). Снято красиво. И, вроде бы за происходящим интересно следить. Но 2/3 фильма сделаны со звериной серьезностью, а 1/3 такой треш и угар, что даже и не знаешь, как все это воспринимать в целом.

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

Наемник Куорри (Quarry, первый сезон, 2016). Очень атмосферно. И вроде как даже сюжет неплохой (хотя мне лично интересно было бы последить за совсем другими персонажами). Но настолько затянуто и нудно местами, что сил нет. Его бы раза в два укоротить и вполне смотрибельно получилось бы.

Фильм вне рейтинга

Аватар: Путь воды (Avatar: The Way of Water, 2022). Фильм-разочарование, которое мне сложно оценить адекватно. Дело в том, что ранее фильмы Кэмерона начинали новую веху, если не в кинематографе вообще, то в отдельных жанрах точно. Было кино до "Терминатора" (особенно до "Терминатора-2") и после. Было кино до "Чужих" и после. Было кино до "Титаника" и после. Точно так же было и с "Аватаром". И вот прошло много лет после "Аватара", выходит второй фильм от которого ждешь чего-то, что опять разделит кино на до и после... Но ничего подобного. И близко. При том, что на фоне всего остального киношлака, выходившего в последние 10 лет, второй Аватар по качеству исполнения выглядит просто таки несокрушимой глыбой. Но ждешь прорыва, а прорыва-то и нет. Обычное качественное современное кино.

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

Тем не менее, посмотрел до самого конца с интересом. И, полагаю, последующие части тоже посмотрю. А вот пересматривать (в отличии от первого "Аватара") буду вряд ли.

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

[linux.kde.help-wanted] Столкнулся с проблемой в Kubuntu 22.04 и KDE Plasma 5.24.7

Очень надеюсь, что кто-то из читателей блога сможет помочь. Ибо мое гугл-фу позволило найти только это, но там нет решения: Application windows stopped responding on kde plasma :(

Итак, есть ноутбук, на котором сперва стояла Kubuntu 18.04, которая затем была обновлена до 20.04, а на прошлой неделе я обновился до Kubuntu 22.04 (поскольку Kubuntu сказала, что для моей версии обновления больше присылаться не будут).

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

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

Запустить новую копию Konsole, как я уже сказал, можно. Можно в ней даже выполнить команды killall plasmashell и kstart5 plasmashell. В этом случае KDE Plasma перезапускается, но я вижу только пустой десктоп, иконки приложений на таск-баре есть, когда на них наводишь курсор мыши, то показывается содержимое окна приложения на превьюшке, но на само приложение переключиться не удается.

Upd. В стартовое меню при таком глюке зайти практически всегда можно. И konsole оттуда запустить можно. Но не всегда в консоли при этом работает клавиатура. Через стартовое меню можно зайти в System Settings и там клавиатура работает, хотя в konsole при этом может и не работать.

Помогает только перезагрузка системы, что не есть хорошо.

Может кто подскажет куды бечь, чаго курить, хде капать? В принципе, я даже не против был бы и до 20.04 откатиться, если бы знать как :(

Info Center говорит следующее:

KDE Plasma Version: 5.24.7
KDE Framework Version: 5.92.0
Qt Version: 5.15.3
Kernel Version: 5.15.0-70-generic (64-bit)
Graphics Platform: X11

Если запустить lsb_release, то имеем:

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.2 LTS
Release:        22.04
Codename:       jammy

PS. У меня есть еще пара старых резервных ноутов, на которых я гораздо раньше обновлялся с Kubuntu 20.04 до 22.04, но там никаких проблем пока не было выявлено. А это основной рабочий и подобные глюки сильно портят жизнь :(

пятница, 21 апреля 2023 г.

[prog.c++.wow] clang++-16 внезапно удивил! Неприятно удивил

Пропробовал собрать SObjectizer clang-ом 16-ой версии. Получил неожиданную ошибку:

clang нашел "ошибку" в коде, которому уже лет семь, если не восемь:

auto e = begin(path) + static_cast< state_t::path_t::difference_type >(
   m_current_state_ptr->nested_level() ) + 1;

где path -- это std::array<const state_t *, max_deep>

Поставить адресную арифметику в C++ под запрет, пусть даже при использовании -Weverything и -Werror, это мощно. Внушаить.

Даже не знаю как к этому относиться.

Конкретно этот кусочек я переписал так:

const auto past_the_end = [&path, this]() {
      auto r = begin(path);
      std::advance( r, m_current_state_ptr->nested_level() + 1u );
      return r;
   }();

Можно было бы и двумя строчками обойтись, как-то вот так:

auto past_the_end = begin(path);
std::advance( past_the_end, m_current_state_ptr->nested_level() + 1u );

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

Но хуже всего то, что это оказалась только первая ласточка. Затем clang++-16 еще "подозрительные" места нашел. С которыми так же нужно будет что-то сделать.

А самое худшее, что это все вылезло при работе над новой версией SObjectizer. Но ведь и текущую версию 5.7, затем нужно будет под clang++-16 адаптировать...

Не было печали, что называется :(

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

Так за что боролись, спрашивается?


Upd. Ну а вот от этого я вообще выпадаю в осадок (желающие могут сами проверить на godbolt):

#include <array>

int main() {
#if 1
    char buffer[3];
    buffer[1] = 0;
#else
    std::array<char, 3> buffer;
    buffer[1] = 0;
#endif
}

Вариант с Си-ными массивами, типа, небезопасный. Поэтому ошибка компиляции. А вариант с std::array, надо полагать, безопасный-безопасный. Ага.

Ну ахринеть. Ну уж теперь-то заживем.

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

[prog.c++] А ведь когда-то в C++ предлагали добавить noexcept(auto) :(

Сегодня довелось написать несколько конструкций noexcept(noexcept(...)). Так себе удовольствие. Захотелось поиметь в языке noexcept(auto). Уже даже собирался набросать пост на эту тему. Но чуть ли не первая строка поиска в Google показала, что подобная идея уже была. Даже в виде двух предложений: N3227 и N4473. По второму предложению известно, что его рассматривали, но не стали принимать, а поставили на паузу (P0133).

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

пятница, 14 апреля 2023 г.

[life] История с ежедневным прослушиванием "Плейлист Дня" на Яндекс.Музыке завершилась самым унылым образом

Сама история описана здесь. Ну а завершилась она настолько уныло и буднечно, что даже и не хочется ничего говорить. Яндекс просто перестал отображать счетчик дней в Web-версии. И все :(

Добрался я где-то до 936. Было это на прошлой неделе. А на этой неделе попробовал глянуть на очередное значение и ничего уже не увидел.

В общем, на 1000 так и не удалось посмотреть.

среда, 12 апреля 2023 г.

[prog.c++] На RSDN засветили одну из задачек с собеседования в Тинькофф

Вот в этой теме: Как удаляется vptr?

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

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

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

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

Без этого понимания сложно будет объяснить какой именно из print-ов будет вызван и почему.

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

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

понедельник, 10 апреля 2023 г.

[prog.c++] В склерозник: ссылки на статьи об Undefined Behavior в C++ и C

Ради интереса немного освежил тему UB в C++ (и C заодно) и решил зафиксировать в блоге несколько ссылок, которые показались интересными/полезными. Дабы было проще затем найти.

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

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

Falsehoods programmers believe about undefined behavior. Статья специально для тех, кто думает, что раз у меня сейчас все работает, то все нормально ;)

What Every C Programmer Should Know About Undefined Behavior #1/3 с продолжениями: часть два, часть три. Как по мне, так обязательно к прочтению.

Why undefined behavior may call a never-called function. Разбор нашумевшего несколько лет назад примера, когда clang очень уж хитро поступил с вызовом функции по нулевому указателю.

Undefined behavior can result in time travel (among other things, but time travel is the funkiest). Рассказ о том, до чего может дойти компилятор в эксплуатации UB.

Security flaws caused by compiler optimizations. Несколько простых примеров того, как оптимизатор может выбросить куски кода, важные с точки зрения безопасности/секретности.

Towards Optimization-Safe Systems: Analyzing the Impact of Undefined Behavior. PDF-ка от исследователей от MIT (краткое резюме о результатах этого исследования на русском можно прочитать на opennet.ru).

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

пятница, 7 апреля 2023 г.

[prog.flame] Нет, с любителями PascalCase (да и camelCase) нам не по пути!

Никто, ну вот вообще никто, не убедит меня, что:

TransformObtainedVideoFrameForFurtherProcessing

читается лучше, чем:

transform_obtained_video_frame_for_further_processing

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

transform_obtained_video_frame_for_further_processing

это плохое, негодное и слишком длинное имя.

Никто, слышите?! Даже и не пытайтесь.

PS. Полагаю, что +50% к ЗП может изменить точку зрения на прямо противоположную.

PPS. Тяпница же на дворе ;)

среда, 5 апреля 2023 г.

[prog.c++] Очередное небольшое обновление для RESTinio

Благодаря одному из пользователей RESTinio получил обновление, исправляющее проблему с обработкой запросов с сжатым посредством zlib телом. Новая версия 0.6.18 уже доступна через Conan и vcpkg, так что есть смысл обновиться.

У меня лично работа над этим обновлением вызвала довольно сильные эмоции.

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

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

Посему отрадно узнавать, что RESTinio продолжает использоваться. Мелочь, а приятно ;)

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

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

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

вторник, 4 апреля 2023 г.

[prog.c++] Две отличные статьи про неочевидные моменты C++

Две небольшие, но отличные статьи на сайте devblogs.microsoft.com: "C++17 creates a practical use of the backward array index operator" и "The mystery of the crash that seems to be on a std::move operation". Они, хоть и на английском, но читаются легко. Да и самое главное там -- это примеры. Посему решил сохранить их в склерознике :)

Позволю себе процитировать оттуда пару-тройку фрагментов.

Из первой статьи:

Another practical use is to bypass any possible overloading of the [] operator, as noted in Chapter 14 of Imperfect C++:

#define ARRAYSIZE(a) (sizeof(a) / sizeof(0[a]))

By flipping the order in 0[a], this bypasses any possible a[] overloaded.

std::vector<int> v(5);
int size = ARRAYSIZE(v); // compiler error

However, it isn’t foolproof. You just need to create a more clever fool: If v is a pointer or an object convertible to a pointer, then that pointer will happily go inside the 0[...].

struct Funny
{
    operator int*() { return oops; }
    int oops[5];
    int extra;
};

Funny f;
int size1 = ARRAYSIZE(f); // oops: 6

int* p = f;
int size2 = ARRAYSIZE(p); // oops: 1

Из второй статьи.

Вот этот код на ARM-е ломается при компиляции в режиме C++14:

void polarity_test(std::shared_ptr<Test> test)
{
    test->harness->callAndReport([test2 = std::move(test)]() mutable
    {
        test2->reverse_polarity();
        ::resume_on_main_thread([test3 = std::move(test2)]()
        {
            test3->reverse_polarity();
        });
    });
}

Ломается потому, что вот здесь:

test->harness->callAndReport([test2 = std::move(test)]() mutable

значение test может быть перестать актуальным еще до того, как произойдет обращение к test->harness.

Но, начиная с C++17 это уже не проблема.

воскресенье, 2 апреля 2023 г.

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

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

Фильмы

О чем говорят мужчины. Простые удовольствия (2023). Посмотрел с большим удовольствием, хотя в начале как-то не очень заходило, но к концу фильма все раскочегарилось и зашло более чем норм.

Операция «Фортуна»: Искусство побеждать (Operation Fortune: Ruse de Guerre, 2022). Нормальное развлекательное кинцо. Мне показалось, что Гай Ричи сделал это с изрядной долей стеба над фильмами про супершпионов, но может это только показалось.

Тест (El Test, 2022). Глянуть можно, если больше нечего. Но это не комедия. Уж точно не комедия в привычном для меня смысле.

Марлоу (Marlowe, 2022). Очень красиво, но очень скучно.

Заговор в Каире (Walad Min Al Janna, 2022). Посмотреть было любопытно, но не скажу, что фильм впечатлил. Во-первых, не хватило понимания того, что и как происходит в мусульманском мире. Во-вторых, мне показалось, что не угадали с выбором актера на главную роль: слишком уж забитым и робким выглядел его персонаж, поэтому в то, что он делал в конце фильма, не верилось от слова совсем.

Вкус жизни (Smagen af sult, 2020). Сюжет, как по мне, так ничего выдающегося, как и сам фильм, собственно. Хотя, любителям жанра "мелодрамы" может и зайдет. Но вот работа оператора местами настолько шикарная, что у меня просто челюсть на пол падала от красоты и качества картинки.

На краю (The Ledge, 2022). Посмотреть можно только если есть желание каждые десять минут вопрошать "Да что за фигню нам здесь показывают".

Сериалы

Больница Никербокер (The Knick, сезоны 2014-2015). Отлично. Ну вот просто отлично. Посмотрел благодаря наводке Алексея Водовозова вот из этой лекции и не пожалел. Хотя во втором сезоне, как по мне, слишком уже много времени уделялось романтическим линиям (кто с кем что крутил-мутил), но сильно впечатления это не испортило.

Медленные лошади (Slow Horses, второй сезон, 2022). Если понравился первый сезон, то смело можно смотреть и второй. Мне первый сезон понравился, второй вроде бы тоже ничего так, в стиле первого. Но нашлась во втором сезоне пара моментов, которые лично мне несколько подпортили общее впечатление.

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

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

Одни из нас (The Last Of Us, первый сезон, 2023). Редкостная гадость. Повелся на высокий рейтинг и кучу хвалебных отзывов, как выяснилось, очень зря.

пятница, 31 марта 2023 г.

[prog.flame.humour] Мы на пути к такому понятию, как hand-made software?

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

Может мы уже движемся к тому, что со временем львиная доля софта будет написана ИИ. И только изредка где-то, под спецзаказ и за совсем другие деньги, программное обеспечение будет писаться вручную, по старинке. Эдакий hand-made software, который могут себе позволить только состоятельные заказчики. Которые либо понимают, зачем им это нужно, либо же просто так понтуются.


ЗЫ. Я понятия не имею к чему приведет развитие ИИ и какое влияние этот самый ИИ окажет на софтостроение. Может быть ИИ оставит меня без работы уже совсем скоро (чего не хотелось бы, т.к. пока даже не могу представить на что можно было бы переучиться). Но пока же это еще не произошло, так чего же расстраиваться раньше времени? ;)

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

[prog.c++] Подсмотрел интересный трюк в докладе Ивана Чукича

Доклад "the expected outcome" с Meeting C++ 2022. Где-то на 10-й минуте докладчик демонстрирует простой шаблон класса для работы с кодами ошибок из API на чистом Си.

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

Вкратце этот простой шаблон выглядит так:

template<typename T, typename ValidationPolicy>
class Result {
private:
   T value_;

public:
   bool has_value() const;
   T value() const;
   T error() const;
};

Т.е., мы храним значение, возвращенное какой-то C-шной функций (например, функцией open из POSIX), и посредством ValidationPolicy можем проверить, указывает ли это значение на ошибку или же на нормальный результат.

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

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

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

пятница, 24 марта 2023 г.

[prog.thoughts] ИИ выставит всех программистов на мороз?

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

Во-первых, ну поувольняют, так поувольняют. Придется учиться чему-нить другому.

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

А первым языком программирования был Бейсик на БК1001, где номера строк нужно было вручную расставлять, если мне склероз не изменяет. Потом был Turbo Pascal 3.0 на Robotron 1715, где компьютер выступал лишь в качестве печатной машинки. Никакого тебе выделения синтаксиса, никакой контекстной помощи, никакого пошагового отладчика.

Переход после всего этого на IBM PC с редактором Foton с подсветкой синтаксиса -- это же был как скачок в другой мир. А после Foton-а был MultiEdit, в котором уже были макросы и можно было сделать так, что ты набираешь "if", а редактор сам для тебя вставляет полный оператор if в нужное место. А еще были первые IDE от Borland-а, в которых можно было подвести курсор к имени функции, нажать F1 и получить справку по этой функции. Тут же! Не листая бумажные книжки-справочники.

И Turbo Debugger с пошаговой отладкой и возможностью слежения за значением какой-то переменной (т.к. программа сама прерывается и выпадает в отладчик когда переменная меняется).

А потом пришло следующее поколение IDE, с автокомплитом... А следом и с автоматическими рефакторингами и генерацией всякого разного по шаблону...

Собственно, к чему все эти стариковские воспоминания о более зеленой траве?

Да к тому, что обыденные для современного программиста инструменты для нас в начале 1990-х бы воспринимались такой же фантастикой, как ChatGPT или Co-pilot воспринимаются сейчас. И ничего, программистов меньше не стало ;)

Как по мне, так суть программирования -- это a) понять что нужно делать, b) понять как делать и c) выдать максимально подробные инструкции компьютеру (есть еще и пункт "d", но в контексте разговора хватит и первых трех).

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

Но даже если этот самый ИИ сможет справиться с пунками a) и b), то потребность в программистах все равно останется. Ведь кому-то нужно нести ответственность. Так ведь?

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

Ну а то, что программистов при таком подходе нужно будет меньше, ну так что? Может нас сейчас и так слишком много :)))


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

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

вторник, 21 марта 2023 г.

[prog.c++] Небольшой довесок к теме обеспечения strong exception safety

Не так давно поднял тему обеспечения strong exception safety. Теперь хочу эту тему продолжить маленьким примером.

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

Очевидная реализация вышеописанного может выглядеть вот так:

class device_info_manager_t
{
   std::map<compound_device_id_t, device_description_t> m_devices;
   ...
public:
   ...
   [[nodiscard]]
   std::string
   add_device(
      const placement_t & place_id,
      const name_t & name,
      const device_description_t & info);
};

[[nodiscard]]
std::string
device_info_manager_t::add_device(
   const placement_t & place_id,
   const name_t & name,
   const device_description_t & info)
{
   auto [ins_it, was_inserted] = m_devices.emplace(
         compound_device_id_t{place_id, name},
         info);
   if(!was_inserted)
      throw std::runtime_error{"device already added"};

   return it->first.to_string(); // (1)
}

К сожалению, эта реализация метода add_device обеспечивает лишь базовую гарантию, а не строгую.

пятница, 17 марта 2023 г.

[prog.c++] Очередные шаблоны против копипасты: упоролся тут на почве обеспечения гарантии strong exception safety

Делаю новую фичу в SObjectizer и в одном месте потребовалось реализовать вставку элемента в словарь из словарей словарей. Сначала сделал просто. Потом начал делать нормально ;)

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

Итак, есть вот такая вот структура данных (ключевой тип -- это bindings_map_t):

среда, 15 марта 2023 г.

[life.music] Продолжение саги про выбор недорогих, но хороших наушников на Aliexpress

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

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

Несколько общих слов

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

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

Отчасти потому, что с марта 2022-го из РБ на Aliexpress закупаться стало сложнее, чем до того. Да и посылки, по ощущениям, стали идти дольше. Что сильно снижает удовольствие от процесса: одно дело, когда ты просто оплатил с карточки в белорусских рублях и посылка у тебя уже через 2-3 недели. Другое дело, когда нужна валютная карта, да еще и не любого банка. И ждать потом 1.5-2 месяца.

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

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

Какие наушники использую больше всего?

Вкладыши с 15.4mm динамиками. Одна пара с DLC-диафрагмой (на работе), она пара с LCP-диафрагмой (дома). Забавно, что изначально DLC- и LCP-динамики были для меня слишком светлыми, мне в них не хватало басов.

Но время идет, вкусы и привычки меняются. Больше обращаешь внимания на какие-то детали и особенности звучания. Так что сейчас что DLC, что LCP даже чуть темноватее, чем хотелось бы. Что меня до сих пор удивляет. Т.к. все еще помню свое разочарования от количества баса в LCP-динамиках летом 2021-го года :)

Иногда слушаю вкладыши с синей PET-диафрагмой. Но рано или поздно начинаю ощущать, что мне в них (пока?) недостаточно веса на НЧ, поэтому все равно возвращаюсь к DLC и LCP-динамикам.

Иногда слушаю внутриканальные затычки с 10mm драйвером, переделанные из Yincrow RW-919. Но пока что не смог подобрать себе подходящие для этого корпуса амбушюры. Так что основными они (пока?) не стали. Хотя по качеству звука, наверное, это самое лучшее из имеющегося. По деталям, возможно, вкладыши с PET-диафрагмой чуть посолиднее, но в этих затычках больше массы на низких частотах.

Еще у меня был эксперимент с 40mm накладными наушниками. Но пока что не удалось сделать нормальное оголовье (не встретились пока на барахолке подходящие доноры), так что здесь еще исход не ясен. Но общие впечатления прикольные. Что-то в этом направлении есть.

Какие источники у меня сейчас?

Как и прежде: телефон + USB ЦАП ("свисток").

До недавнего времени было четыре основных свистка. На работе долгое время пользовался X1 на базе AK4452 (реплика того самого Trasam Q1). Но на днях он стал барахлить, поэтому пришлось от него отказаться.

Tempotec Sonata BHD. Непревзойденное сочетание цены (брал на распродаже чуть ли не за треть стоимости), качества звука и низкого энергопотребления. Но когда долго слушаешь, что начинает казаться, что в звуке не хватает драйва и жизни. Все технично, все детально... Но как будто пресно, что ли.

Безымянный чуть ли не самодельный USB-цап на базе PCM2706 + ES9023 в качестве усилителя. Ценой что-то вроде 13USD. С заметным фоновым шумом, из-за чего приходится применять дополнительное сопротивление на 75ом. Не такой техничный и детальный, как Sonata BHD. Поддерживающий только 16bit/44.1kHz. Но зато очень приятный и драйвовый звук. Почти что тёплый, ламповый :) И, что немаловажно, такой же экономичный, как и Sonata BHD.

Hiby FD3 на двух ES9038Q2M. Просто самый лучший из всего, что у меня побывало. Единственная проблема -- жрет батарейку в три раза активнее, чем Sonata BHD :(

Про Hiby FD3 в Интернете не так много говорят, хотя, судя по тому, что я читал, это прямой конкурент таким распиаренным моделям, как xDuoo Link2 Bal и Moondrop Moonriver 2. Собственно, из-за того, что у меня есть FD3, пробовать Link2 Bal или Moonriver 2 даже и не хочется.

Еще не могу не отметить один ЦАП от HaaFee на базе AK4493EQ (из тех, что до пожара на фабрике Asahi Kasei). Отличный был ЦАП за свои деньги. Но, в какой-то момент у меня на руках этих самых ЦАПов скопилось очень уж много, распродал часть, вместе с ними и HaaFee. Сейчас жалею. Тот самый velvet sound от AK, чего нет в FD3.

HiFi-плееров у меня пока не было. Хотя уже задумываюсь о том, чтобы каким-то обзавестись, т.к. надоело уже таскаться со "свистками" и подключать/отключать их постоянно. Однако, хороший плеер (который бы переиграл бы FD3) для меня сейчас слишком дорогая штука, так что пока не обзавелся. Ну да поживем, увидим.

Некоторые выводы, которые я сделал для самого себя

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

    Так-то, конечно, хорошие наушники будут играть и от телефона. Однако, даже "копеечный" (по аудиофильским меркам) USB-ЦАП за 30-40USD сразу даст вам почувствовать насколько хорошие наушники могут звучать лучше.

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

    Цены на кабеля сильно варьируются. Но нормальный кабель на распродажах вполне можно взять в диапазоне от 10 до 20USD, иногда и дешевле. У меня есть несколько кабелей от NiceHCK, пока доволен.

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

    Если слушать музыку в фоновом режиме, то качество источника+наушников, по большому счету, не суть важно. ЦАП за 15-20USD и вкладыши с динамиками за 10USD + приличный кабель за 15USD -- и этого хватит с лихвой. Ну а если уж музыку начинаешь выслушивать специально, отгородясь от всего мира, тогда да, тогда придется раскошелиться :)

  • Амбушюры очень сильно влияют на звук. Особенно во внутриканалках. Так что если вы все еще ищете "свой звук", то имеет смысл собрать большую коллекцию разнообразных амбушюр. Чем больше, тем лучше.

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

  • Среди всех магазинов, в которых можно приобрести запчасти для самодельных наушников, я безусловно выделяю NSC Audio DIY Store и XinYue Audio Store. Тут и выбор большой, и цены низкие, и обслуживание быстрое и адекватное. Подавляющее большинство моих закупок было сделано в этих двух магазинах. Есть еще Chitty's Store. Там встречаются экзотические штуки, которые в другие магазины доезжают с изрядным опозданием (если вообще доезжают). Но зато цены... По некоторым моделям я видел разницу в 1.5-2 раза.


Вот как-то так.

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

Ссылок на разное не давал, т.к. эти самые ссылки имеют неприятное свойство "протухать" (т.е. устаревать). Если что-то интересует, то я дам в комментарии актуальную, на текущий момент, ссылку.

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

[prog.c++] Кратко про свои впечатления от библиотеки WebSocket++

В текущем проекте потребовалось поработать с библиотекой WebSocket++, которая, как мне видится, является чуть ли не самой часто рекомендуемой библиотекой по работе с WebSocket-ами в C++.

Ряд моментов в дизайне WebSocket++ вызывают недоумение.

Например, в именах некоторых типов используется суффикс _ptr, но где-то это означает shared_ptr (acceptor_ptr), а где-то просто голый указатель (io_service_ptr).

Тип connection_ptr вроде как является shared_ptr-ом, но и вроде как пользоваться им напрямую можно только до тех пор, пока не будет вызван connect у endpoint-а. Потом можно оперировать исключительно connection_hdl. Но connection_hdl -- это weak_ptr, который нужно вручную трансформировать в connection_ptr, что захламляет код обработчиков. Могу предположить, что отдавать готовый connection_ptr в обработчики событий -- это создавать просадку производительности в тех редких случаях, когда обработчикам достаточно только connection_hdl. Но насколько это разумный баланс между производительностью и удобством использования -- это для меня открытый вопрос.

Не смог найти никаких идентификаторов для соединений. Подозреваю, что в качестве connection_id нужно использовать сам connection_hdl + std::owner_less. Но connection_hdl суть weak_ptr и, как мне кажется, в полный рост может встать ABA проблема.

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

Качество и количество примеров не впечатлило. Качество и количество документации впечатлило еще меньше :(

Но в исходниках разобраться можно, что и спасает. И хотя у нас в RESTinio шаблонных наворотов, может быть, еще побольше, но временами кажется, что разработчики WebSocket++ обобщенным программированием увлеклись ну очень уж сильно :)))

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

четверг, 9 марта 2023 г.

[prog.impressions] Любопытная статья про проблемы с unsafe Rust и отсутствием таковых в Zig

Со мной поделились ссылкой на статью When Zig is safer and faster than Rust. Показалась любопытной, хоть далек от Rust, и еще более далек от Zig.

В общем, решил поделиться и я :)

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


Больше всего меня впечатлил вот этот кусочек синтаксиса Zig-а:

const STACK_TOP = 256;
const VM = struct {
    // pointer to unknown number of items
    stack_top: [*]Value,
    // like a rust slice:
    // contains a [*]Value + length
    // has bounds checking too
    stack: []Value,
    // alternative to slices when
    // N is a comptime known constant
    stack_alt: *[STACK_TOP]Value
};

Как по мне, как это и умно, и хитро, и практично.

Глядя на этот фрагмент подумалось, что в C++ можно самостоятельно замутить что-то подобное. Например, класс raw_ptr<T>, который просто хранит указатель и позволяет получить его значение, но не допускает адресной арифметики. А также классы dynamic_span<T> и static_span<T, N>, которые описывают последовательность расположенных друг за другом объектов типа T. Для span-классов возможны операции итерации по содержимому, а также преобразования из static_span в dynamic_span. Соответственно, для доступа к элементу по индексу доступны и operator[], и метод at.

Хотя я бы отошел от принятого в STL соглашения о том, что operator[] не делает проверки в run-time, а at делает. И сделал бы так, чтобы operator[] осуществлял бы проверки в run-time. А для доступа без проверок использовался бы какой-то метод с некрасивым и бросающимся в глаза именем, вроде unsafe_get.

Более того, раз уж в C++ сообществе заговорили о том, чтобы превратить C++ в безопасный язык (не то, чтобы я верил в подобные перспективы, но раз пытаются, то зачем мешать?), то можно было бы пойти и дальше: сделать классы вроде raw_ptr, dynamic_span, static_span и т.п., частью языка. И таки ввести в язык что-то вроде "эпох", "поколений" или "ревизий". Чтобы начиная с какой-то "ревизии" нельзя было оперировать голыми указателями. Ну вот вообще. Только специальными классами. С автоматическим преобразованием raw_ptr в соответствующий указатель при обращении к C API, но только лишь в этом случае.

Эх, мечты, мечты :(


Еще одно впечатление связано с тем, что мне все еще непонятен хайп вокруг Rust-а.

Одно дело, если бы Rust позиционировался исключительно как околосистемный язык, полноценная замена и C, и C++, и Ada. Но, судя по описанным в упомянутой статье проблемам с unsafe Rust, это как бы не совсем так. По меньшей мере есть что улучшать в языке для того, чтобы на нем было удобно программировать в unsafe.

Однако же, изрядное количество шума вокруг Rust-а создавали (может и сейчас создают, просто уже не слежу) пользователи всяких там Ruby и Python-ов. Мол, раньше мы писали на динамически-типизированном языке и имели кучу разных проблем и с производительностью, и с сопровождаемостью... А как попробовали Rust, так волосы сразу стали гладкими и шелковистыми ;)

ИМХО, очень странно выглядят попытки применять Rust для высокоуровневого прикладного программирования.

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

И вот когда увлечение "write once, run everywhere" таки логично сошло до оправданного минимума, то оказалось, что удобного и безопасного нативного языка для прикладного программирования-то и нет.

Ну вот сходу вспоминаются разве что Eiffel (который старый, не модный, сильно платный и нифига не приспособленный для фигак-фигак-и-в-продакшен), Object Pascal в разных его проявлениях (который старый и незаслуженно нелюбимый массами из-за begin/end, хотя казалось бы какая нафиг...), да и не взлетевший D (он то может и взлетел, но низенько-низенько). Вот, собственно, и все, что можно вспомнить из середины-конца нулевых.

Раньше всех сориентировались в Google и сделали Go. Который как раз таки и простой, и безопасный, и нативный, и в меру быстрый. Но убогий :) Да и заточенный только под определенную нишу.

Вот и получается, что Ruby-истам и Python-истам, которых не устраивает скорость и надежность их кода на динамически-типизированных языках, переходить особо-то и не на что. Java и C# в пролете изначально, т.к. их сразу можно было использовать, но почему-то не использовали. Go может подойти, а может и нет. Таки убогость Go никуда не девается.

А тут Rust, нативный, быстрый, безопасный, да еще и модный, и молодежный. Bingo! :)))

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

среда, 1 марта 2023 г.

[prog.c++] Продолжение экспериментов с шаблонами для улучшения tagged_value_t

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

Давеча удалось эту идею проверить. Результат мне понравился. ИМХО, так можно гораздо проще расширять функциональность шаблона tagged_value_t, чем в случае наследования от вспомогательных классов.

Получившееся решение показано под катом. Поиграться с ним можно на wandbox.

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

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

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

Фильмы

Подставной город (Jojakdoen dosi, 2017). Динамичная корейская фантастика. Если корейское кино очень нравится, то можно и глянуть. Если же не нравится, то лучше не тратить время.

Хранитель тайн (Objetos, 2022). Первые 2/3 фильма казались вполне себе ничего и смотрелись интересно, но вот финал все испортил.

Сериалы

Парижская полиция 1905 (Paris Police 1905, 2022). Это продолжение сериала Парижская полиция 1901. Кому зашел первый сезон, тот может глянуть и второй. Но мне показалось, что второй сезон слабее, следить за происходящим не так интересно.

Расследование (Efterforskningen, 2020). Неплохо, но скучновато. Сократили бы на две серии, хотя бы, и смотреть было бы интереснее.

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

понедельник, 27 февраля 2023 г.

[prog.thoughts] ИМХО, не есть хорошо, когда приходится выбирать между "быстрым" и "удобным" языками

Около месяца назад статью "Performance of Kafka Consumers: 1 Billion messages". Ничего интересного или полезного для себя там не нашел.

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

В конце статьи есть абзац:

Maybe Go’s performance overshadows Python, but Python is developer friendly. Of course, I can choose Python in these scenarios. If I don't need a performance or need to develop minimized consumer or I need to one-time run/remove console app. But when I need performance or huge numbers, I would rather Go without question.

Т.е. автор говорит, что когда нужна производительность, то Go его выбор без вопросов. Но когда важно удобство (да и скорость написания кода, пожалуй), то Python.

А вот мне сама необходимость делать такой выбор кажется не то, чтобы странной (хотя да, и странной в том числе), сколько ненормальной.

Ведь одно дело, когда мы выбираем между условными Ada/C++/Rust и не менее условными Java/C#/Scala/Kotlin. Тогда понятно, что небезопасные нативные языки берутся в рассмотрение не просто так, а потому что либо нужно опускаться на весьма низкий уровень, либо нужно выжимать последние крупицы производительности (объективности ради нужно сказать, что есть и другие факторы в пользу группы небезопасных нативных языков, но это уже совсем другая история).

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

Казалось бы, бери себе Kotlin или C# и не парь мозги. Будет и достаточно удобно, и достаточно быстро. Но нет, зачем-то нужно делать выбор между Go и Python. Нипанятна :)


Еще одно впечатление от упомянутой выше статьи: никакого интереса к задаче извлечения большого объема данных из Kafka не возникло. Хотя могу предположить, что это и нужная, и интересная задача, сопровождающаяся сложными техническими моментами. Однако нет, не торкает. А вот сделать что-то вроде Kafka -- вот это уже совсем другое дело... Ну а чего еще ожидать от хронического велосипедостроителя? ;)

пятница, 24 февраля 2023 г.

[life] Год в новой реальности

Да, уже год.

Тогда не верилось, что все это затянется на целый год.

Сейчас не верится, что все это закончится хотя бы года за три.

Мир явно меняется кардинально, и это явно не за день-два произойдет. Скорее уж лет пять, если не десять, а то и не пятнадцать.

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

вторник, 21 февраля 2023 г.

[prog.c++] Эксперимент с метапрограммрованием для создания более продвинутого варианта шаблона tagged_value_t

В языке C++ мне очень не хватает т.н. strong typedef. Временами хочется сказать, что вот этот вот int -- это ширина (width), а вот этот вот int -- это высота (height). Но в C++ штатных возможностей вывести разные несовместимые друг с другом типы из одного базового нет. Поэтому приходится велосипедить. Иногда просто вот так:

struct width_t { int value; }
struct height_t { int height; }

void fill(width_t w, height_t h) {...}

fill(width_t{640}, height_t{480});

Иногда и посложнее.

Для упрощения себе жизни когда-то сделал несложный вспомогательный тип tagged_value_t по типу вот такого:

templatetypename V, typename Tag >
class tagged_value_t
{
   V m_value;

public:
   explicit tagged_value_t( const V & value ) : m_value( value ) {}
   explicit tagged_value_t( V && value ) : m_value( std::forward<V>(value) ) {}

   [[nodiscard]]
   const V &
   value() const noexcept { return m_value; }

   [[nodiscard]]
   V &
   value() noexcept { return m_value; }
};

Этот тип кочует у меня из одного проекта в другой и позволяет делать что-то вроде:

struct url_tag {};
using url_t = tagged_value_t< std::string, url_tag >;

struct username_tag {};
using username_t = tagged_value_t< std::string, username_tag >;

struct raw_password_tag {};
using raw_password_t = tagged_value_t< std::string, raw_password_tag >;

struct base64_password_tag {};
using base64_password_tag = tagged_value_t< std::string, base64_password_tag >;

Подобные штуки применяю для указания типов параметров для функций/методов/конструкторов. В принципе удобно: на входе в функцию/метод ошибиться сложно, а внутри можно извлекать значения исходных типов (тех же int или std::string) и работать с ними привычными способами.

Но вот давеча захотелось сделать еще и так, чтобы значения, обернутые в tagged_value, можно было хранить в качестве ключей в ассоциативных контейнерах. Например, в качестве ключа для std::map.

Понятное дело, что для каждого tagged_value выписывать ручками operator< или operator== не хочется. Поэтому решил попробовать посредством метапрограммирования модифицировать tagged_value_t так, чтобы в нем появлялись operator< и/или operator== если таковые операторы определены для исходного типа.

Под катом то, что у меня получилось в первом приближении. Для C++17, т.к. это самый "свежий" стандарт, который я могу себе позволить. Возможно, концепты из C++20 позволят записать все это компактнее.

пятница, 17 февраля 2023 г.

[prog.c++] Вроде бы придумался хороший вопрос про C++ для собеседований

Вчера довелось заглянуть в cppreference в описание стандартного класса std::reference_wrapper. И взгляд зацепился за показанную там возможную реализацию этого класса:

namespace detail {
template <class T> constexpr T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
 
template <class T>
class reference_wrapper {
public:
  // types
  using type = T;
 
  // construct/copy/destroy
  template <class U, class = decltype(
    detail::FUN<T>(std::declval<U>()),
    std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>>>()
  )>
  constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
    : _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
  reference_wrapper(const reference_wrapper&) noexcept = default;
 
  // assignment
  reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
 
  // access
  constexpr operator T& () const noexcept { return *_ptr; }
  constexpr T& get() const noexcept { return *_ptr; }
 
  templateclass... ArgTypes >
  constexpr std::invoke_result_t<T&, ArgTypes...>
    operator() ( ArgTypes&&... args ) const 
    noexcept(std::is_nothrow_invocable_v<T&, ArgTypes...>) 
  {
    return std::invoke(get(), std::forward<ArgTypes>(args)...);
  }
 
private:
  T* _ptr;
};

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

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

Но потом подумалось вот что: а ведь если на собеседовании по C++ у соискателя попросить объяснить почему у предложенной реализации reference_wrapper такие конструкции и как именно это работает, то получится отличный вопрос на знание C++.

Действительно, тут тебе и выбор между шаблоном и не шаблоном, и SFINAE, и std::declval, и оператор запятая, и тип выражения, в котором оператор запятая используется, и про noexcept, и еще раз про noexcept, но уже другой, и std::addressof вместо &, и еще всякое разное по мелочи.

Как по мне, так если человек слабо знает C++, то он толком ничего объяснить не сможет.

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

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

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

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

четверг, 16 февраля 2023 г.

[prog.wow] Век живи, век учись: с указателем после освобождения ничего нельзя делать

Иногда флеймы на профильных ресурсах оказываются очень полезны. Вот свежий пример с LOR-а: "Навеяно свежей дырой в Xorg".

В двух словах: после освобождения указателя (через free в C или delete в C++) с указателем ничего нельзя делать (кроме как присвоить ему новое валидное значение).

Т.е. не то, что разыменовывать нельзя (это-то как раз понятно), но и вообще ничего нельзя: ни распечатать, ни сравнить с чем-нибудь (пусть даже и с NULL/nullptr), ни преобразовать в uintptr_t. НИЧЕГО. Любые попытки сделать что-то подобное есть UB.

Говорят, что у этого есть даже какое-то логическое обоснование. Но мне осознать сие не получается :)

Вышесказанное означает, что если у вас написано что-то подобное:

void data_cleanup(data * ptr) {
  if(ptr != NULL) {
    ... // Какие-то действия по очистке.
    free(ptr);
  }
  else {
    ... // Какие-то другие действия. Допустим, просто
        // печать в лог о том, что data_cleanup был вызван.
  }

  ... // Еще какие-то действия, которые не зависят от
      // значения ptr.

  // А здесь нужно еще что-то сделать если ptr не был NULL.
  if(ptr != NULL) { // (1)
    ... 
  }
}

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

Полагаю, что выйти из ситуации можно вот так:

void data_cleanup(data * ptr) {
  int not_null = ptr != NULL;
  if(not_null) {
    ... // Какие-то действия по очистке.
    free(ptr);
  }
  else {
    ... // Какие-то другие действия. Допустим, просто
        // печать в лог о том, что data_cleanup был вызван.
  }

  ... // Еще какие-то действия, которые не зависят от
      // значения ptr.

  // А здесь нужно еще что-то сделать если ptr не был NULL.
  if(not_null) {
    ... 
  }
}

Но это же нужно быть очень внимательным, чтобы не наступать на подобные грабли.

К счастью, в C++ при использовании умных указателей вроде shared_ptr и unique_ptr все не так страшно. Но вот если нужно записать что-то на чистой ламповой Сишечке или на старых плюсах, в которых умных указателей нет... То грустно.


Данное обсуждение заставило вспомнить еще про одну засаду с голыми указателями. Еще, если кто-то не знал или забыл, в Си и C++ нельзя просто так сравнивать на больше/меньше указатели одного типа. Грубо говоря, если у нас есть указатели a и b одного типа, то выражение (a<b) будет определено, только если a и b указывают на элементы одного массива (либо на элемент за последним элементом этого массива).

Правда, в C++, насколько я понимаю, можно воспользоваться std::less или std::greater из стандартной библиотеки. Поскольку для подобных компараторов определены специализации для указателей. Например, по поводу std::less на cppreference сказано буквально следующее: "A specialization of std::less for any pointer type yields the implementation-defined strict total order, even if the built-in < operator does not."

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