вторник, 31 декабря 2024 г.

[work-n-life] Послесловие к уходящему году и поздравления с НГ

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

Кроме того, в предновогоднюю пору лента в LinkedIn-е стала напоминать выставку тщеславия: у каждого второго наполеоновские планы, а у каждого третьего достижений за минувший год больше, чем у самого Цезаря. ИМХО, нужен такой предновогодний пост, прочитав который люди вроде меня почувствовали бы себя нормальными, выдохнули с облегчением, мол, а ведь у меня все не так уж и плохо, а может даже и вполне себе хорошо. Так что если вам нужна подобного рода терапия, то вы попали по адресу 😉

В профессиональном плане уходящий 2024-ый год оказался ровным и ничем не выдающимся, сплошная рутина: получаешь новую задачу, вникаешь, куришь бамбук, экспериментируешь, делаешь, тестируешь, доводишь до ума, получаешь новую задачу, вникаешь, куришь бамбук... И так снова и снова. Разительное отличие от нервяка и неопределенности 2019-2021 гг.

Очень надеюсь, что так продолжится и в 2025-ом.

С другой стороны, 2024-ый оказался безрадостным для наших собственных открытых продуктов. Два небольших релиза для SObjectizer, всего одна статья на Хабре и ничего нового в RESTinio 🙁 При этом, далеко не факт, что в 2025-ом получится выкраивать больше времени на собственный OpenSource, постараемся, конечно же, но последние годы научили не загадывать далеко наперед.

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

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

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

Ну вот как-то так, 2024-ой год прошел тихо и спокойно, без потерь и достижений. Планы на будущий год простые: прожить его.

Своим же читателям пожелаю счастливого Нового Года. Пусть все будет хорошо! И пусть ваш 2025-й будет гораздо лучше, чем мой 2024-й 😜

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

[life.audiophilia.diy] Итоги уходящего года в сфере бюджетной аудиофилии

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


Итак, начнем с побывавших в моих руках ЦАПах.

Дешевый свисток на CX31993. Пару лет назад у меня уже была аналогичная модель, но тогда не зашла. А сейчас мне в руки попал девайс без 3.5mm джека и для подключения наушников к нему потребовался свисток с Type-C. И вот на этот раз звук меня порадовал. Да, все простенько и бюджетненько, если сравнивать с гораздо более дорогими источниками. Да, с нейтральными наушниками получается плоско и скучно. Но если взять что-то басовитое, то очень даже ничего. Плюс поддержка гарнитуры, плюс самое низкое энергопотребление из виденных мной. В общем, слушать музыку исключительно в фоновом режиме в хороших наушниках с собственным характером и не переживать за расход батареи -- самое то.

VE Megatron. Мои впечатления полностью совпадают с тем, что было рассказано в обзоре от Soundcheck39: отличное качество звука и огромная мощность за смешные по аудиофильским меркам деньги. Правда, в моем случае Megatron не работает от моего смартфона, видимо не хватает питания, которое телефон выдает по USB. Ну и у меня нет (пока?) настолько высокоомных наушников, для которых потребовалась бы вся мощь Megatron-а -- даже 300омные вкладыши с LCP-диафрагмой приходится слушать на минимальной громкости. Так что всю прелесть Megatron-а еще не оценил.

Hiby FC6. Просто лучшее, что побывало у меня в руках. Знаю, что есть и отрицательные отзывы, но мне нравится то, что слышу. Отдельный вопрос "а стоит ли он своих денег в сравнении с конкурентами?" Но у меня нет на него ответа, т.к. я на него собственных денег не тратил. И, наверное, за свои кровные не купил бы.

Главная проблема с FC6 в том, что в реальности он прожорливый -- по моим замерам 0.14A на 5V. Что сравнимо с Colorfly CDA M1, который выжирал более 10% аккумулятора за час. А это очень много для меня. Так что если много слушать FC6, то телефон приходится заряжать каждый день 🙁


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

14.2mm от Huayunxin на 16 ом. Отличные по техничности динамики с шикарной серединой, отменной 3-х мерностью, но с проваленными самыми низкими частотами (по ощущениям, все что ниже 50Гц играет очень тихо). Как попробовал их, так и использовал на ежедневной основе месяца полтора. Пока не возникло ощущение, что звук детальный, приятный по тоналке, с отличными атаками... Но какой-то пресный. Взял после них более басовитые модели и понял в чем дело: самых-самых низких НЧ то и нет. Осознал это не сразу, но когда осознал, то это начало преследовать, поэтому редко их слушаю. Так что любителям хороших НЧ не советую. А вот ценителям светлой подачи вполне может зайти. Главная проблема -- это найти корпуса для динамиков такого диаметра, шеллы на 14.2mm редкая штука на Aliexpress, поэтому я в качестве доноров использовал какие-то совсем дешевые наушники за один или полтора доллара.

15.4mm динамики на 18 ом с "рупорной" (horn) композитной диафрагмой. Скажу так: бывают динамики для выслушивания деталек на ВЧ, а это динамики для выслушивания деталей на НЧ. Сильный упор на НЧ и СЧ, высокие отведены далеко назад, хотя с сохранением детальности, но эти самые детали на ВЧ нужно очень и очень тщательно выслушивать и даже разыскивать. Зато НЧ мощные, глубокие и ни капли не ватные. В общем, динамики для настоящих басхэдов и ВЧ-фобов. Стоят вменяемых денег, плюс идут сразу с MX500 корпусами.

14.2mm динамики на 32 ома с пометкой 6H. Внезапное и приятное открытие. Стоят недорого, а звучат раза в два-три дороже. По крайней мере я для себя отличия этих динамиков от более дорогих с LCP и DLC диафрагмами слышу, но не уверен, оправдывают ли эти отличия разницу в цене. По звучанию у них нет уклона ни в светлую, ни в темную сторону, хотя они чуть потемнее и пожирнее, чем DLC. Все так, как мне бы и хотелось. ИМХО, просто отличная модель за свои деньги, а особенно хороши будут для людей с небольшими ушами, которым вкладыши с 15.4-мм динамиками носить не удобно.

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


Пару слов о корпусах и кабелях.

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

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

И вот о кабелях: за год переделал/починил несколько пар наушников используя беспонтовые кабеля из магазина 3C-Accessories Store и пришел к выводу, что сменные кабеля от NiceHCK, OpenHeart или ivipQ, за $10 или $20, -- это хорошо, конечно же, но и простой кабель за $4 делает свое дело ничуть не хуже.

Ну и в повседневной жизни кабель с простым 3.5мм джеком оказывается гораздо практичнее, чем в 4.4мм балансным 🙁


А теперь о неожиданном состоянии, в котором оказался после того, как попользовался Hiby FC6 с месяц или около того.

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

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

Тогда как с Hiby FC6 практически все, что у меня есть в наличии, звучит круто.

Да, звучит по разному.

Если у динамиков нет НЧ, то FC6 не совершит магию и не возьмет НЧ ниоткуда. А если ВЧ задвинуты далеко назад, то вперед их FC6 не выдвинет.

Но все равно наушники звучат круто. По разному, но круто.

И в итоге я перестал понимать что лучше, а что хуже.

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

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

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

Думаю, что единственное, что еще хочется попробовать -- это нормальный плеер с Android-ом. Что-то вроде Tempotec V6 или уровнем повыше. Чтобы можно было и файлы с SD-карты, и Yandex.Music, и из Telegram, и из VK. Как раз то, что дает смартфон, но чтобы качеством уровня FC6.

среда, 25 декабря 2024 г.

[prog.c++.sobjectizer] Улыбнуло. А как прореагировать иначе даже не знаю...

Найдено на просторах Интернета:

"Выглядит монстровито", да.

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

Кстати говоря, вчера в рабочей ветке при расширении одной из фич довелось задействовать совсем уж редкую штуку -- layer-ы, которые когда-то были нам нужны, но в последнее время нами практически не применялись. А тут раз, и потребовалась.
Внезапно (tm), ага 🤓

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

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

[prog.c++] Возвращаясь к теме std::vector-а с назначаемой пользователем политикой роста

Вернусь к теме, поднятой пару месяцев назад: В std::vector не хватает вот какой штуки... Речь там шла о том, что хорошо было бы в std::vector иметь возможность управлять политикой роста объема вектора когда он полностью заполняется.

Часть комментариев к этой заметке (не только в блоггере, но и в других соцсетях) произвела на меня впечатление "ты и сам дурак, и хочешь странного, и вообще в std::vector все сделано зашибись, а если тебе чего-то не хватает, то сделай сам" (наверняка подразумевалось совсем другое, но я, как художник, которого каждый может обидеть, воспринял именно так, прошу понять и простить).

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

Итак, был сделан шаблон, который параметризуется несколькими параметрами: исходный тип вектора (например, std::vector или folly::fbvector), типом хранящихся в векторе значений, типом политики роста объема вектора, типом аллокатора (по дефолту std::allocator).

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

Но часть методов реализуется в самом vector_with_custom_growth_policy. Это такие методы как: emplace_back, push_back (две штуки), emplace, insert (две штуки, обе для одиночного элемента). При этом я даже не брался за insert, который получает подпоследовательность в виде [begin, end).

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

Общие впечатления: изначально кажется, что все просто и понятно. Затем внезапно (тм) тратишь время и силы на то, чтобы поддержать вот такую вот простую конструкцию: v.insert(v.end(), v[0]);

Если кто-то не знал, то нормальная реализация std::vector-а должна это поддерживать. Т.е. когда в insert/push_back прилетает ссылка на объект, который уже лежит в векторе, то нужно проявлять осторожность, чтобы при реаллокации вектора не инвалидировать эту ссылку.

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

При этом сил на то, чтобы поддержать версию insert(pos, first, last) и версию insert(pos, initializer_list) уже не хватило 🙁

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

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

PS. Показать реализацию не могу, т.к. делалось это все в рамках закрытого заказного проекта.

вторник, 17 декабря 2024 г.

[prog.thoughts] Программист: хороший, плохой, крутой. Что под этим вообще понимается?

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

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

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

Соответственно, с плохим программистом все просто: это противоположность "хорошего программиста".

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

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

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

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

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


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

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


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

пятница, 13 декабря 2024 г.

[prog.self.reflection] Официальный ответ на претензию "Да вовсе ты и не крутой программист"

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

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

Если это не так, и я где-то что-то подобное ляпнул, то дайте, плз, ссылку. В назидание.

Более того, если говорить о том, как я сам себя оцениваю, то это будет что-то чуть повыше среднего. В верхние 20% точно не попадаю.

Отчасти это компенсируется тем, что я еще немного могу в тестирование и документирование. Т.е. делаю разное, хоть и посредственном уровне.

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

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

четверг, 12 декабря 2024 г.

[prog.flame] Иногда просто поражаешься трудолюбию программистов

Недавно довелось увидеть что-то вроде вот такого:

void data_holder::validate_data()
{
  std::cout << "*** Data validation: 1/15 (checking) ***" << std::endl;
  check();
  std::cout << "*** Data validation: 2/15 (sorting) ***" << std::endl;
  sorting();
  std::cout << "*** Data validation: 3/15 (deduplication) ***" << std::endl;
  deduplicate();
  std::cout << "*** Data validation: 4/15 (normalization) ***" << std::endl;
  normalize();
  ... // И так еще несколько строк пока не будет сделан шаг 15 из 15.
}

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

Так что я бы чуть ли не сразу написал бы что-то вроде:

void data_holder::validate_data()
{
  constexpr int total_steps = 15;
  auto inform = [step = int{1}](const char * name) mutable {
    std::cout << "*** Data validation: " << step << "/" << total_steps
      << " (" << name << ") ***" << std::endl;
    ++step;
  };

  inform("checking");
  check();
  inform("sorting");
  sorting();
  inform("deduplication");
  deduplicate();
  inform("normalization");
  normalize();
  ... // И так еще несколько строк пока не будет сделан шаг 15 из 15.
}

ИМХО, программист должен быть достаточно ленивым, чтобы не повторять однообразную рутинную работу. И еще он должен видеть повторяющиеся паттерны, которые можно преобразовать в повторно используемый код.

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

среда, 11 декабря 2024 г.

[prog.flame] Комментарии в коде нужны. Без них тяжено, проверено на собственном опыте

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

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

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

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

  • почему это сделано именно так, а не иначе?
  • зачем вообще это было сделано?

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

Первоначально я хотел на этом и закончить, но встретил в статье про переиздание знаменитой книги "Мифический человеко-месяц" вот такой абзац:

Однако есть и другая точка зрения на этот вопрос. Например, Мартин (Роберт, а не Джордж), автор книги «Чистый код», утверждает, что комментарии — это зло. Даже будучи среди исходного кода они всё равно могут стать неактуальными. «Не трать время на написание комментариев», – говорит Мартин. Вместо этого он предлагает более тщательно подходить к процессу именования переменных, методов и классов. Если всё делается правильно, то и необходимость в комментариях отпадает.

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

Disclaimer. Приведенный код -- это черновик, который еще не компилировался и не тестировался.

Итак, вот фрагмент без комментариев:

template<typename T>
void
doAttemptToSwitchFromReadOnlyToReadWrite(T * objectToLock)
{
   auto & globalList = getGlobalListFor(objectToLock);
   std::unique_lock globalListLock{ globalList._lock };
   auto it = globalList._objects.find(objectToLock);
   if(it != globalList._objects.end())
   {
      auto & lockInfo = it->second;
      if(AccessMode::ReadOnly != lockInfo._currentMode)
         throw std::runtime_error{
               "doAttemptToSwitchFromReadOnlyToReadWrite: "
               "lockInfo._currentMode != AccessMode::ReadOnly "
               "in the global lock list"
            };

      if(1u == lockInfo._ownersCount)
      {
         lockInfo._currentMode = AccessMode::ReadWrite;
      }
      else if(isWaitingQueueEmpty(lockInfo))
      {
         OwnerCountForLockingUpgrateTrx ownerCounterTrx{ lockInfo };

         const auto requestResult = makeWaitingAccessorInfoThenWait(
               globalListLock,
               lockInfo,
               AccessMode::ReadWrite);
         if(AccessRequestResult::granted != requestResult)
            throw PossibleDeadlock{
                  "doAttemptToSwitchFromReadOnlyToReadWrite: "
                  "unable to upgrade ReadOnly lock to ReadWrite lock even "
                  "after waiting"
               };

         ownerCounterTrx.commit();
      }
      else
      {
         throw PossibleDeadlock{
               "doAttemptToSwitchFromReadOnlyToReadWrite: "
               "there is no possibility to upgrade ReadOnly lock "
               "to ReadWrite lock"
            };
      }
   }
   else
   {
      throw std::runtime_error{
            "doAttemptToSwitchFromReadOnlyToReadWrite: there is no "
            "information about objectToLock in the global lock list"
         };
   }
}

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

вторник, 3 декабря 2024 г.

[work] Формы рабочей коммуникации в удаленном формате: что, когда и для чего

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

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

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

Так что электронная почта отличный вариант, когда нужно обмениваться информацией объемом более 2-3 строк и нам не нужно получить ответ от собеседника в течении ближайших 5-10 минут.

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

При этом обсуждать какие-то более-менее объемные темы в мессенджерах, особенно в групповых чатах, как по мне, такое себе занятие. Мало кто умеет набирать текст быстро и более-менее грамотно, да еще соблюдая правила пунктуации. Поэтому получение 5-10, не говоря уже о 15-20 строчек от собеседника -- то еще удовольствие, тем более что в это время ты не можешь отвлечься на что-то другое, ведь от тебя же ждут быстрой реакции.

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

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

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

И вот когда приходит время вернуться к отложенному "на потом", то наличие следов первичных обсуждений в почте или Telegram очень сильно облегчает возвращение в контекст. Если же серьезное предварительное обсуждение велось в устном формате, то лично я через полгода уже точно ничего не вспомню. Могу даже забыть и про сам факт такого обсуждения :(

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

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

Тем не менее, живое общение голосом имеет пару-тройку критически важных преимуществ.

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

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

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

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

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

В общем, резюмируя:

Почта -- когда много и не срочно.
Мессенджеры -- когда немного и оперативно.
Телефон и он-лайн созвоны -- когда срочно и/или нужно общение по-человечески.


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

Так что описанное выше -- это не отлитые в граните незыблемые правила, скорее просто выводы из моего личного опыта.

воскресенье, 1 декабря 2024 г.

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

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

Фильмы

Боец. Лучший из лучших (Life After Fighting, 2024). Кино для любителей единоборств. Сюжет и актерская игра практически отсутствуют, зато мордобой зачетный.

Схватка (Kill, 2023). Во-первых, с удивлением для себя обнаружил, что с интересом смотрю индийский фильм. Во-вторых, как по мне, так он покруче нашумевшего недавно "Манкимена". В-третьих, это даже не боевик, это слешер для любителей жанра "кишки, кровь, распидорасило". Но даже для меня, любителя такого, этих самых "кишки, кровь, ..." оказалось даже слишком много.

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

Хищные земли (Elevation, 2024). Простенько и предсказуемо, объяснение происходящего вообще для "малолетних дебилов" (с). Но снято красиво и чтобы скоротать вечер в отсутствии чего-то приличного (а приличного сейчас почти что и нет) вполне сойдет.

Дорога отчаяния (Desperation Road, 2023). Тому, кто обозначил этот фильм как триллер и боевик, нужно поотрывать все что можно. Это драма с некоторым элементом криминала. Драма довольно скучная и затянутая. Но здесь есть хотя бы вменяемый сюжет и нормальная игра актеров.

Кража (Stöld, 2024). Во-первых, это не триллер. От триллера там ничего нет, не ведитесь на описание. Во-вторых, это весьма скучно. Но картинка качественная, можно глянуть, если хочется посмотреть на жизнь саамов в Швеции.

Сериалы

Корни зла (Die Quellen des Bösen, первый сезон, 2023). Сильно двойственные чувства. С одной стороны, очень качественно снято, я вот прям получал удовольствие от картинки. Но, с другой, бездарный финал, который оставляет лишь вопрос "ну и что это все было?" В общем, смело можно не смотреть.

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

Механизмы (Maskineriet, первый сезон, 2020). Начинался многообещающе и бодро, но во второй половине превратился в унылую мелодраму, а в финале нам вообще начали втирать какую-то дичь.

Триггер (третий сезон, 2024). Разочарован. Жаль потраченного времени. Если о первых двух сезонах у вас осталось хорошее впечатление, то лучше третий сезон не смотреть, чтобы это самое впечатление не испортить.

четверг, 28 ноября 2024 г.

[soft.dev.wtf] Мир сходит с ума... На примере маразма с "The Undefined Behaviour Question"

Просто зафиксирую эту историю здесь, т.к. мне она кажется показательной. Если кто не в курсе, то суть, как я ее понял, в том, что в комитет по стандартизации C++ было подано предложение под названием "P3403 The Undefined Behaviour Question". Кому-то из комитетчиков в названии увиделась аналогия с "еврейским вопросом" (Jewish Question) времен гитлеровской Германии. И автора предложения попросили поменять название. Он сперва раздумывал об этом, но потом решил оставить все как есть, т.к. придерживается мнения, что вопрос об undefined behaviour в языке программирования не имеет ничего общего с геноцидом по этническому признаку. После чего (как я понял) его предложение отказались пропускать через комитетскую бюрократию, а его самого подвергли обструкции.

Эта история всплыла на Reddit-е, в разделе /r/cpp, откуда была удалена, но осталась в другом разделе. А потом и сам автор предложения "The Undefined Behaviour Question" подробно изложил свою версию происходивших событий: First-hand Account of “The Undefined Behavior Question” Incident.

Ну что здесь остается сказать?

Сперва ветку master в git-е переименовали в main из-за того, что когда-то в США "master"-ом называли рабовладельцев, а сейчас git checkout master вызывает жуткие страдания у современных чернокожих программистов (ага, ага). Теперь вот это. Верной дорогой, товарищи. Прям как во времена СССР -- а нет ли в названии художественного произведения скрытой антисоветчины?

ИМХО, мир становится глобальным и чем больше это происходит, тем более диким выглядит то, как какая-то маленькая (в масштабах человечества) группа хер знает кого диктует всем остальным свои правила.

Это тем более дико на фоне того, что разработчикам из РБ и РФ обнуляют аккаунты на BitBucket-е, отказываются принимать от них pull request-ы, блокируют доступ к техническим ресурсам в Европе и США. Просто по географическому признаку: мол, если ты из РБ, то ты подлежишь обструкции. Просто потому что.

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

В общем, чем дальше, тем больше начинает казаться, что дистанцирование от загнивающего Запада, не самая плохая штука в современных-то реалиях. Заимствовать оттуда нужно технологии, а не идеологию. А то докатимся до того, что отменим типы разъемов "папа" и "мама" из-за очевидного сексизма, мужского доминирования и объективизации женщин. Тьфу, срамота!

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

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

[prog.c++] Чего мне не хватило в boost::dynamic_bitset

Довелось немного попользоваться boost::dynamic_bitset. В принципе, отличная штука. И написана так, что можно заглянуть в потроха и понять что к чему. В общем, авторам решпект и уважуха.

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

Disclaimer: возможно, что-то из описанного ниже на самом деле в dynamic_bitset есть, просто я этого, несмотря на штудирование документации, не нашел 🙁 В таком случае прошу понять и простить... 😎

Во-первых, очень утомляет то, что для операций test и set нужно гарантировать, что индекс бита меньше текущего размера множества. Для меня это оказалось неудобно. Мне нужно фиксировать в bitset-е обработанные целочисленные индексы в диапазоне от нуля до N. Не всегда они идут последовательно, да и само N изначально неизвестно. Т.е. сперва мне нужно обработать, например, индекс 42, затем 24, затем 1, затем 100500.

Приходится перед вызовом set проверять, а есть ли i в bitset-е. Если нет, то сперва нужно сделать resize, а лишь затем вызывать set.

Аналогично, перед вызовом test нужно проверить, что i меньше size и только затем вызывать test.

В общем, неудобно. Было бы лучше, раз уж у нас dynamic_bitset, чтобы индексы больше или равные size обрабатывались должным образом без участия пользователя:

  • для set чтобы происходило автоматическое расширение множества;
  • для test чтобы возвращался false (ведь бита все равно нет, значит он точно не выставлен в единицу).

Во-вторых, операции AND и OR требуют, чтобы оба операнда были одинакового размера. Что неудобно, когда у меня оказываются два множества разного размера, а я хочу либо слить их в одно, либо вычесть одно из другого.

Из-за того, что операции AND/OR могут применяться только к множествам одного размера, приходится вручную разбираться с тем, какой кусок откуда вырезать, чтобы получить допустимые операнды.

Как по мне, было бы гораздо удобнее, если бы operator& сам бы определил результирующий размер итогового множества и выполнил AND только для фрагментов этого размера. Например, если мы делаем L & R, где L больше R, то имеет смысл выполнять AND только для первых R.size элементов из L.

Но такой функциональности в dynamic_bitset нет, поэтому ее приходится реализовывать вручную.

В-третьих, идет в догонку к "во-вторых". Иногда нужно понять, а есть ли непустое пересечение между L и R. Т.е. мне не нужен точный результат пересечения, нужно просто понимать пусто ли такое пересечение или нет. Но если L и R имеют разные размер, то просто так я этого не узнаю. Мне сперва нужно получить два множества одинакового размера, потом уже для них делать intersect. Т.е. мне требуется создать новый dynamic_bitset, который нужен только для выполнения одного intersect-а и все.

Было бы круто, если бы был какой-то dynamic_bitset_view, похожий на std::string_view. Чтобы я мог в этот dynamic_bitset_view поместить только часть исходного dynamic_bitset-а. Причем чтобы при этом dynamic_bitset_view оставался бы легковесным объектом, который бы ничего никуда бы не копировал.

И тогда intersect вообще можно было бы делать не с исходными множествами L и R, а с их дешевыми фрагментами в виде dynamic_bitset_view.

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

[prog.c++] Послесловие к релизу SO-5.8.3: будет ли SO-5.9 и если будет, то когда?

Осенью 2014-го года мы выпустили первую версию в ветке 5.5. Эта ветка затем развивалась пять лет без серьезных ломающих изменений. Я бы был не против и дальше обходится без заметных переделок, но, к сожалению, версия 5.5 набрала такой груз разнообразных фич, который стало уже тяжело нести. Накопился опыт, взгляды на какие-то вещи принципиально поменялись и было решено разгрести накопившийся наслоения не всегда хорошо сочетающейся функциональности. Так в 2019-ом появилась ветка 5.6.

Cчитаю, что в течении последних пяти лет именно эта ветка и развивается. Хотя, формально, мы сделали переход от 5.6 к 5.7, а затем и к 5.8, поскольку были пусть и небольшие, но ломающие совместимость изменения. Тем не менее, различия между 5.6 и 5.8 гораздо меньше, чем между 5.5 и 5.6. Так что для меня лично 5.6/5.7/5.8 -- это развитие одной и той же линии романа и изменение номера версии всего лишь дань формализму.

Сейчас заканчивается 2024-й год и получается, что семейство 5.6/5.7/5.8 поступательно развивается уже пять лет. Вроде бы повторяется история с пятилетним циклом жизни 5.5 и пора задумываться о том, что дальше.

На данный момент, в отличии от ситуации с 5.5, я не вижу каких-то фатальных недостатков в семействе 5.6/5.7/5.8. Необходимости разгрести авгиевы конюшни пока нет. ИМХО, у 5.8 еще есть запас прочности для продолжения в том же духе.

Поэтому какой-то насущной необходимости начинать ветку 5.9 нет.

Насущной нет, но есть вопрос с освоением новых стандартов C++. И в 2025-ом нужно будет всерьез задумываться над этим вопросом.

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

В 2019-ом было решено, что невыгодно дальше держаться за C++11.

Полагаю, в ближайшие полтора-два года так же невыгодно станет держаться и за C++17. Вот тогда на горизонте и появится ветка 5.9.

Это один возможный сценарий развития событий.

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

Ведь сейчас в SObjectizer аллокации/деаллокации буквально на каждом шагу: за send-ом скрывается new, постановка заявки в очередь к агенту может вести к аллокациям, подписка или установка delivery filter требует аллокаций, даже смена состояния агента, если для состояния используется time_limit, требует аллокаций.

Подобное поведение автоматически ставит крест на применении SObjectizer в системах реального времени. Что иронично, т.к. SObjectizer вырос из SCADA Objectizer, создававшегося именно под реальное время. Но в этом я не вижу ничего страшного, ну нет и нет.

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

Да, это не просто. Да, придется прилагать серьезные усилия как на этапе проектирования софта, так и на этапах его разработки и тестирования. Из-за этого как раз зачастую на выживание при bad_alloc-е и не обращают внимания, у многих разработчиков есть целая мантра, что-то вроде "если нет памяти, то нет и жизни".

Прекрасно понимаю почему так, да и сам далеко не всегда готов платить за разработку дороже, если крах при bad_alloc-е вполне себе допустим.

Но что делать когда крах при bad_alloc-е ну такое себе? Вот тот же прокси-сервер с 200k одновременных подключений... Он рестартует, это OK. Вполне вероятно, что он рестартует за считанные секунды, а то и быстрее. Тогда как восстановление этих 200k соединений -- это же не быстрый процесс. Это неизбежно скажется на впечатлении пользователей от качества сервиса. Но, что хуже, у нас нет устойчивости против подобных падений в будущем. Допустим, мы принимаем 200k подключений и работаем нормально, но затем приходит одно специфическое подключение, при работе с которым мы вынуждены активно потреблять память и это ведет нас напрямую к очередному bad_alloc-у. И мы опять падаем роняя 200k соединений. А через какое-то время все повторяется вновь.

Если же поставить себе целью написать код, который может пережить bad_alloc, то придется придумать какую-то стратегию выживания. У нас должен быть набор действий по восстановлению (например, закрытие проблемного соединения и очистка связанных с ним ресурсов). И, что важно, мы должны быть уверены, что при выполнении данного набора действий у нас нет никаких динамических аллокаций (ведь каждая новая аллокация в таких условиях может вести к повторному bad_alloc-у).

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

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

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

Вот под добавление в SObjectizer чего-то подобного я бы начал ветку 5.9 не раздумывая прям завтра (но не сегодня, сегодня уже поздновато). Но...

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

суббота, 2 ноября 2024 г.

[prog.c++] Новые версии SObjectizer и so5extra: 5.8.3 и so5extra. Мои личные впечатления

Мы зафиксировали новые версии SObjectizer и so5extra: 5.8.3 и 1.6.2. На Хабре опубликована статья с описанием нововведений, так что не буду здесь повторяться. Озвучу здесь свои личные впечатления от работы над этими релизами.


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

Получилось не сказать, чтобы красиво и эргономично, поэтому не удивлюсь, если msg_hierarchy в итоге останется невостребованным. Возможностей C++17 (и моих знаний этих самых возможностей) хватило только на этот вариант. Была бы в C++ рефлексия, можно было бы сделать и покрасивши. Так что будем ждать принятия рефлексии в C++26, затем дождемся года 2029-го или даже 2030-го, чтобы безопасно перейти на компиляторы с нормальной поддержкой C++26... 🙂

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

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

А вот этим летом что-то бум! И перемкнуло в голове. Возникла непонятно откуда взявшаяся мысль о том, а что, если на каждый тип подписки выделять отдельный mbox? Т.е. хочешь подписаться на базовый тип сообщения -- бери mbox именно для этого типа. Хочешь подписаться на конкретный производный тип -- бери другой mbox, именно для этого конкретного производного типа.

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

Принципиальным моментом была именно идея о разных receiving_mbox-ах. Которую пришлось ждать больше трех лет и которая не желала появляться на свет "на заказ".

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


Когда писал статью для Хабра об этом релизе, то поймал себя на неожиданном ощущении.

В 2016-ом, когда я только начал публиковать на Хабре статьи о SObjectizer-е, маркетинговая составляющая была одной из главных. Все-таки мы хотели организовать бизнес вокруг своего OpenSource и нужно было показать "товар лицом". Конечно же, это не были чисто продажные статьи из категории "покупайте наших слонов", но начинающий маркетолог внутри нашептывал, что мол нужно показать насколько у нас все зашибись и какие мы сами крутые, все из себя такие опытные и умелые.

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

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

Как бы не первый год пилим и пилим. Кому могли "продать", тем уже "продали". Миллионов нам SObjectizer не принес. Ну так чего выделываться?

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

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

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


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

В завершении вынужден повторить банальность: если вдруг вам что-то потребовалось от SObjectizer-а, а вы этого там не нашли, то дайте знать. Мы не сможем сделать то, о чем даже и не подозреваем. А вот если нам сказать, то кто знает. Может года через три поймаем очередное "озарение" ;)

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

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

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

Фильмы

Дэдпул и Росомаха (Deadpool & Wolverine, 2024). Если понравились первые две части Дэдпула, то нужно обязательно смотреть и третью. Правда, в какой-то момент плотность юмора превысила мои способности к восприятию происходящего, так что, боюсь, многое прошло мимо меня. Если же вы хорошо помните персонажей из вселенных "Людей X" и "Мстителей", то наверняка заметите гораздо больше отсылок и подколок, чем получилось у меня.

Ограбление (Napad, 2024). В общем-то неплохо, мне зашло. Напомнило фильмы в духе соцреализма. Хотя к сюжету в нескольких местах есть вопросы, но если не придираться, то в общем-то неплохо.

Ячейка 234 (Unit 234, 2024). Бюджетненько, но вполне себе смотрибельно.

Игра киллера (The Killer's Game, 2024). Отличный аттракцион чтобы отключить мозги. Но если вам не нравится жанр абсолютно несерьезных комедийных боевиков, то лучше воздержаться.

Спящая (La mujer dormida, 2024). Смотреть было интересно, но развязка фильма откровенно разочаровала.

Револьвер (Ribolbeo, 2024). Откровенно слабо. В принципе, можно и пройти мимо.

Не говори никому (Speak No Evil, 2024). Мне не зашло совершенно, не возникло ощущение реальности происходящего. Хотя отметить отличную игру Джеймса Макэвоя нужно, такое впечатление, что он там единственный органично смотрелся.

Субстанция (The Substance, 2024). Не понравилось. Потенциально хорошую идею превратили в какую-то муть, а финал фильма -- откровенный треш. В общем, смотреть противно, а потраченного на просмотр времени жаль.

Сериалы

13 клиническая (первый сезон, 2022). Мне вот прям зашло. Посмотрел все восемь серий первого сезона за два дня на одном дыхании.

Медленные лошади (четвертый сезон, 2024). Самый слабый из всех сезонов. Если первые три вам понравились, то можно глянуть просто для того, чтобы посмотреть что происходит с героями. Если же первые сезоны не впечатлили, то смело можно не смотреть. Как по мне, так если бы не Гари Олдман, то этот сезон был бы откровенным говном.

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

Кино вне категории

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

четверг, 24 октября 2024 г.

[prog.c++] Впечатления от Google-овского sparsetable

Давеча довелось применить sparsetable из Google-овского sparsehash для представления разреженного вектора значений. Разреженный вектор в моем случае -- это вектор с обычной индексацией от 0 до N, где части элементов нет физически. Обращения к элементам идут именно по целочисленным индексам (к тому же индексы в моём случае были представлены std::uint32_t).

В принципе, здесь можно было бы воспользоваться каким-то ассоциативным контейнером на базе деревьев или хэш-таблиц, но все эксперименты показывали, что когда в разреженном векторе хранятся небольшие значения (например, указатели), то любые ассоциативные контейнеры потребляют значительно больше памяти чем обычный std::vector вектор, в котором незанятые ячейки заполнены специальными "мусорными" значениями.

Тогда как sparsetable показал себя здесь очень и очень хорошо.

Причем речь идет именно о классе sparsetable, который хоть и публичный в библиотеке sparsehash, но все-таки выглядит как вспомогательный внутренний класс, использующийся в реализации sparse_hash_map и dense_hash_map. Т.е. это именно что специальная структура данных для представления разреженного вектора, а не какой-то из вариантов хэш-таблицы.

При том, что класс sparsetable отлично зарекомендовал себя именно как специализированная структура данных, не могу не отметить несколько неприятных особенностей реализации sparsetable, обусловленных как старым C++ стандартом, в рамках которого написан sparsetable (а это C++03), так и подходом к разработке софта в Google.

Зафиксирую здесь те из них, которые я нахожу принципиально важными. Очень надеюсь, что найду время и силы сделать форк реализации sparsetable, в котором постараюсь от этих проблем избавится. Но, к сожалению, не уверен получится ли.

Итак, поехали.

Поскольку sparsetable написан для C++03, то он не поддерживает move semantic. Вообще. Причем как для самого sparsetable -- там нет ни конструктора, ни оператора перемещения, хотя swap для sparsetable есть. Но, самое плохое, sparsetable не поддерживает movable-only типы, вроде std::unique_ptr. Т.е. из коробки не получится хранить в sparsetable экземпляры std::unique_ptr 🙁

Еще sparsetable не поддерживает strong exception safety. Там прямо в комментариях в коде написано, что следовало бы реализовать обработку ситуации, когда конструктор копирования для помещаемого в контейнер объекта бросает исключение. Если такое произойдет, то контейнер останется в хз каком состоянии.

И с exception safety там есть еще один прикольный момент: дело в том, что sparsetable выполняет переаллокацию памяти даже при удалении элемента. Т.е. да, вы вызываете erase, внутри erase sparsetable решает заменить блок из которого элемент изъят блоком меньшего размера, для чего обращается к аллокатору... А аллокатор, в принципе, может бросить исключение. Т.е. из erase может вылететь bad_alloc 🙁 Что неприятно, т.к. время от времени я пишу код, в котором для выполнения отката ранее внесенных изменений при исключении делаются erase для возврата модифицированных контейнеров к исходному виду. Представьте себе что в блоке catch или даже в деструкторе объекта вы вызываете erase для sparsetable, а он бросает bad_alloc 🥴

Ну и работа с памятью там имеет одну важную особенность. По умолчанию sparsetable использует собственный аллокатор под названием libc_allocator_with_realloc. Этот аллокатор, как следует из его названия, полагается на Си-шный realloc при переаллокации блоков данных. Но вот если std::realloc возвращает NULL, то sparsetable просто... прерывает работу программы через вызов std::exit. Понятно, что это особенности Google-овского софта, под нужды которого sparsetable и писался, но блин, представьте себе, что у вас сервер, который держит в ОП информацию о десятках тысяч текущий сессий. И вдруг весь этот сервер одномоментно падает из-за того, что при обработке запроса в рамках одной из сессий не удалось переаллоцировать блок в одном sparsetable. Как-то печально, как по мне 🙁

Плюс к тому исходный sparsetable содержит методы для какой-то странной сериализации/десериализации. Полагаю, что в Google от sparsetable это требовалось. Но зачем это всем остальным -- хз.

И совсем отдельная история -- это отношение sparsetable к несуществующим элементам. Так, константная версия operator[] возвращает константную ссылку. В том числе и на несуществующие элементы! Если значения по индексу i нет, то operator[] вернет константную ссылку на статический объект типа T. Что, помимо прочего, означает, что тип T должен быть default-constructible.

Из-за того, что sparsetable выдает несуществующие элементы за существующие, но с общим дефолтным значением, проистекает и то, что у sparsetable есть два типа итераторов. Обычный итератор, который возвращается посредством begin/end, и который итерируется, в том числе, и по несуществующим элементам. И специальный nonempty-итератор + специальные методы nonempty_begin/nonempty_end, которые следует использовать, если хочется пробежаться только по существующим элементам.

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


В общем, под нужды заказчика sparsetable из sparsehash был вытащен, избавлен от части ненужного хлама, слегка доработан напильником для устранения проблем с компиляцией под C++20 и использования std::allocator. Но до глубокой модернизации sparsetable руки так и не дошли. Однако желание сделать это осталось. Буду надеятся, что это получится сделать.


Если кто-то из читателей никогда не слышал про sparsetable, то впечатление можно составить вот из этого описания: Implementation of sparse_hash_map, dense_hash_map, and sparsetable. Там первая часть как раз sparsetable и посвящена. Описание лаконичное, но толковое.

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

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

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

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

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

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

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

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

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

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


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

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

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

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

struct A {};

struct B : public A {
    A _first;
};

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

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

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

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

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

assert(p1 != p2);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

четверг, 3 октября 2024 г.

[prog.c++] В std::vector не хватает вот какой штуки...

Интересно, много ли C++ программистов задумывается о том, а как растет std::vector, когда мы в него добавляем элементы через push_back и не имеем возможности сделать предварительно reserve?

А ведь рост размера std::vector может существенным образом повлиять на расход памяти.

Предположим, что реализация std::vector в вашей стандартной библиотеке использует коэффициент 1.5 для увеличения размера вектора. Т.е. вы делаете push_back в полный вектор и опа! Ваш вектор увеличился в полтора раза. Была емкость на 1000 элементов, а стала на 1500. А использоваться оттуда будет 1001.

А если реализация стандартной библиотеки использует коэффициент 2, то дела еще хуже.

Сильно негативно это начинает проявляться на векторах размером в миллион-два-три и т.д. Если у вас в программе таких векторов не один и не два, то вы с удивлением для себя сможете обнаружить, что в этих векторах где-то 1/5 или 1/4, а то и 1/3 объема не занято. Что в сумме может дать десятки мегабайт впустую потраченной памяти.

Чтобы этого не происходило приходится вручную контролировать размер и емкость вектора и, опять же, вручную дергать reserve перед push_back-ами.

А то, что делается вручную, легко забыть или же сделать неправильно 😣

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

struct my_growth_policy {
  [[nodiscard]] std::size_t operator()(std::size_t current_capacity) {
    return ... // здесь какие-то вычисления.
  }
};

using my_vector = std::vector<my_data, std::allocator<my_data>, my_growth_policy>;

Тогда не пришлось бы вручную контролировать емкость перед каждым push_back-ом.

Но, боюсь, в стандартном std::vector такого не будет никогда.

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

[life.cinema] О фильмах про "Чужого"

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

Мне очень нравятся первые три фильма, каждый из них шедеврален. Хотя первый "Чужой" на мой взгляд, самый слабый из них. Но тут обязательно нужно делать поправку на время, в которое он был снят. Полагаю, в конце 1970-х сложно было сделать лучше. А вот "Чужих" и "Чужой-3" пересматриваю регулярно. Причем чем старше становлюсь, тем больше мне нравится именно третья часть.

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

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

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

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

Не знаю, будут ли еще продолжения, полагаю, что будут. Вопрос только когда.

И, предположу, что там обязательно главным борцом с очередным чужим окажется девушка или молодая женщина. Ну такой сильный женский персонаж, который все превозмогёт и всех победит. Как же может быть иначе? ;) Что делает потенциально продолжение немного предсказуемым.

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

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

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

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

Фильмы

Чужой: Ромул (Alien: Romulus, 2024). Ну хоть отказались от прямого продолжения маразматической линии "Прометея" и "Завета", хотя и есть отсылки к этим двум говношедеврам. В целом мне показался похожим на четвертую часть. И не понравился он мне тем же, чем и четвертая часть -- очередной попыткой скрестить чужого и человека и получить неведомого мутанта. В целом же любители вселенной "Чужого" вполне могут посмотреть, это явно лучше "Прометея" и "Завета".

Восхитительно! (Délicieux, 2021). Этот фильм можно посмотреть просто ради красивой картинки. Временами работа оператора выше всяких похвал, хочется поставить воспроизведение на паузу и рассматривать кадр как картину. Сама же история простенькая и, как мне кажется, откровенно сказочная.

Ускорение (Slingshot, 2024). Хорошо сделанное кино. Но не могу сказать, что мне понравилось, т.к. с некоторых пор не люблю открытые финалы и недосказанность.

Одинокие волки (Wolfs, 2024). Противоречивые впечатления. С одной стороны фильм из категории тех, что мне, в принципе, нравятся. Но не понравился, т.к., во-первых, герои не вызывают сопереживания. И вообще нет ощущения реальности происходящего. Ну типа есть два крутых героя, круче которых, сразу понятно, нет никого. Поэтому ничего с ними произойти не может. И остается только ждать когда случится какой-то очередной прикол. Во-вторых, как-то создатели не озадачились внятным объяснением происходящего, так что я в итоге не понял кто кого куда и зачем отправлял. В-третьих, некоторые моменты в фильме выглядели надуманными и нелепыми. В общем, кино с такими двумя актерами не оправдывает ожиданий.

Ловушка (Trap, 2024). Отличная игра Джоша Хартнетта в фильме, где все происходящее выглядит каким-то откровенным маразмом.

Мистер Блейк к вашим услугам (Complètement cramé, 2023). Не понял что это было. Возможно, я еще слишком молодой, а кино рассчитано на 65-75 летних зрителей. Но выглядит это все как ужасно рафинированная сказочка-сказочка, в которой нет ни одного нормального персонажа, не говоря уже о хоть каком-либо отношении к реальности.

Ворон (The Crow, 2024). Затянуто и скучно.

Оставь мир позади (Leave the World Behind, 2023). Редкая муть. Посмотреть можно разве что если вам почему-то захотелось выкинуть два часа из своей жизни.

Воровка (2023). Халтурная халтура. Смело можно проходить мимо.

Сериалы

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

Точка ноль (первый сезон, 2024). Сплошное разочарование. Ожидал напряженного фильма про борьбу с неизвестным вирусом, но как раз ни напряжения, ни борьбы, ни ожидания глобального коллапса из-за вырвавшейся на свободу болезни в сериале не обнаружилось. Зато рассказали про сына-алкоголика зав.лаборатории, про бывшего любовника главной героини, про отголоски каких-то разборок из 90-х годов и пр. муть. В общем, категорически не рекомендую.

Не смог досмотреть

Властелины воздуха (Masters of the Air, первый сезон, 2024). Снято, конечно же, красиво. Вероятно сцены воздушных боев сделаны мастерски с максимальным сохранением исторических деталей и стремлением к достоверности. Но вот не торкает и все. Скучно и ничего с этим не смог поделать. С трудом дождался завершения второй серии и решил не продолжать.

План Б (Plan B, первый сезон, 2024). Когда во второй серии окончательно прояснилось вокруг какой идеи крутится все происходящее на экране, то стало неинтересно. А когда в третьей серии начались какие-то странные действия со стороны персонажей и приплюсовалась современная толерантная повесточка, то вообще желание продолжать просмотр пропало окончательно.

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

понедельник, 23 сентября 2024 г.

[prog.c++] Десять лет библиотеке timertt

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

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

Однако, в SObjectizer-5 мы заложились сразу на C++11, в котором многое нашлось в стандартной библиотеке. Кроме того, после выхода SObjectizer-а в "свободное плавание" нам пришлось отказаться от развития ряда построенных над SObjectizer-ом библиотек, так что нам больше не нужны были сокеты и инструменты для ручной загрузки-выгрузки динамических библиотек.

В итоге, к середине 2014-го года единственное, что нас привязывало к ACE, -- это таймеры. Которые, как по мне, в ACE были сделаны очень здорово. И мы оказались в ситуации, когда небольшой SObjectizer, архив которого "весили" ~600Kb, требовал внешней зависимости размером порядка 6Mb в архиве. И нужна нам ACE была только для таймеров 😣

В общем, решили от ACE отказаться полностью для чего и пришлось написать свой timertt, т.к. ничего готового на просторах тогдашнего Интернета не нашлось. А для этого потребовалось погрузиться в тему различных механизмов таймеров и реализовать пару-тройку оных самостоятельно. На C++ных шаблонах, понятное дело, чтобы было хардкорнее...

Если мне не отшибает склероз, на все про все ушло порядка месяца. В конце августа 2014-го работа началась, 4-го сентября 2014-го был сделан первый коммит, 18-го сентября 2014-го был зафиксирован первый стабильный тег.

timertt изначально создавалась под SObjectizer, хотя к самому SObjectizer-у она и не привязана, тут полностью обратная ситуация, можно сказать, что без timertt не было бы того SObjectizer-5, каким мы его знаем сегодня. Но т.к. timertt писалась под SObjectizer, то отдельно мы ее мало пиарили. Было несколько анонсов то здесь, то там, но не более того. Самый эпичный анонс случился на LOR-е, сейчас перечитываешь и остатки волос непроизвольно шевелятся... 😂

Что меня до сих пор удивляет в timertt, так это тот факт, что она очень медленно обрастает какой-то новой функциональностью. А с 2019-го туда вообще ничего не добавляется. Работает себе и работает. Редкий в моей практике случай, обычно если что-то пошло в дело, то новые хотелки возникают регулярно, да и какие-то прятавшиеся баги время от времени вылезают наружу. Тот же SObjectizer постоянно расширяется и для RESTinio есть ряд нереализованных хотелок (но тупо не хватает времени на эти работы). А вот timertt просто работает и каких-то новых пожеланий к ней нет. Это-то как раз и удивляет больше всего. Никогда прежде такого не было, а вот поди ж ты.

В завершение хочется пожелать и самому себе, и библиотеке timertt, и читателям блога вернуться к продолжению этой истории в этом же блоге в сентябре 2034-го 😁


Что-то круглые даты как-то кучно пошли. И это еще не последняя, в начале октября ожидается еще одна ;)

суббота, 21 сентября 2024 г.

[prog.memories] Vim, Ruby, Mxx_ru -- двадцать лет в пути...

Когда-то давно, в сентябре 2009-го года здесь появилась первая заметка про мое знакомство с ViM, Ruby и рождение Mxx_ru: ViM, Ruby, Mxx_ru – пять лет в пути! Часть первая: Mxx_ru (вот вторая часть). Спустя пять лет вышло продолжение истории: Vim, Ruby, Mxx_ru -- десять лет в пути... Затем прошло еще пять лет и была написана заметка Vim, Ruby, Mxx_ru -- пятнадцать лет в пути... И вот, спустя еще пять лет, можно опубликовать очередную часть этой истории 🤓


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

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

В таком минимализме для меня, в общем-то, и кроется весь смысл ViM-а: я оказываюсь практически в привычном окружении на любой новой системе с минимальными телодвижениями. Это как раз то, что мне и нужно.

Про ViM могу рассказать показательную историю. В конце прошлого года подключился к проекту, в котором пока основная часть разработки ведется под Windows (с эпизодическими профилактическими сборками под Linux). Основная среда -- VisualStudio, даже сам проект был оформлен в виде .sln-файла. И, что самое важное, разработка ведется на сервере заказчика, доступ к которому дается через RDC.

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

Пару месяцев терпел, но в конце-концов не выдержал. Установил ViM и начал писать код в привычном для себя окружении, а компилировался в командной строке вызывая devenv с передачей ему .sln-файла и нужных параметров. Можно сказать, вернулся в родной мир.

Так что ViM-ом продолжаю с удовольствием пользоваться. А посему могу смело сказать: 20 years with Vim and still learning :) And happy Vimmming!


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

Пару лет назад в одном из проектов довелось столкнуться с Python-ом. Мне не нужно было на Python-е программировать самому, требовалось встроить его в C++ приложение, но все равно пришлось почитать что это такое и погрузится в некоторые его особенности. Порадовался тому, что в свое время выбрал Ruby, а не Python. Как по мне, так Ruby делали как инструмент для программистов, тогда как Python -- для <censored> не умеющих программировать, мягко говоря. И, судя по тому, какое распространение Python получил за прошедшие годы, умеющих программировать больше не стало 😏


Mxx_ru продолжаю использовать при разработке SObjectizer и json_dto. И кайфую от этого. Но это прекрасное время уже скоро закончится... 🥹

В прошлом году в RESTinio-0.7 мы уже полностью перешли с Mxx_ru на CMake.

Полагаю, что в 2025-ом или в 2026-ом, при переводе SObjectizer-а на C++20 (а может быть и сразу на C++23) мы также откажемся от Mxx_ru в пользу CMake. А там и очередь json_dto подойдет.

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

Тем более, что комитет по стандартизации C++ в очередной раз поднасрал C++никам внедрив в язык настолько говенную мудрёную систему модулей, что разработчики компиляторов все никак не могут допилить ее полноценную поддержку. Но допилят рано или поздно. А когда допилят, то окажется, что все наколеночные системы сборки (вроде нашей Mxx_ru) превратятся в тыкву: либо обновляйся и добавляй поддержку того, что придумали сумрачные гении из комитета, либо переходи на что-то другое.

Возможностей сделать Mxx_ru-2.0 с поддержкой C++ных модулей у меня нет и вряд ли найдутся, сам я не становлюсь моложе, запаса сил все меньше. Поэтому Mxx_ru доживает свои последние годы. Се ля ви.


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