пятница, 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.

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