суббота, 28 сентября 2019 г.

[prog.c++] В склерозник: пособия по метапрограммированию на C++11

В догонку ко вчерашнему посту зафиксирую в склерознике пару ссылок на материалы, которые помогли мы разобраться с метапрограммированием на шаблонах в современном C++:

Peter Dimov. Simple C++11 metaprogramming

Peter Dimov. Simple C++11 metaprogramming, part 2

Мне понравилось. Все коротко и по делу. Даже я смог более-менее разобраться, что уже о чем-то да говорит.

пятница, 27 сентября 2019 г.

[prog.c++] Двойственные чувства после достижения задуманного результата

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

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

Итак, потихонечку развитие RESTinio дошло до стадии, когда стало ощущаться отсутствие некоторых полезных вещей. Например, RESTinio позволяет получить доступ к значению HTTP-поля из заголовка (вроде поля "Content-Type"), но зато со значением этого поля уже нужно будет разбираться самостоятельно. Т.е. если вы берете поле "Content-Type", а там лежит "text/plain; charset=utf-8", то парсить эту строку вам нужно будет самим, вручную.

Подумалось, что это не есть хорошо и что RESTinio должно предоставлять что-то пользователю. Но что именно?

В результате было решено не делать в RESTinio полную поддержку полноценного разбора HTTP-полей, а предоставить пару простых функций, которые могут взять содержимое HTTP-поля и разбить его на отдельные значения. Так, из строки "text/plain; charset=utf-8" пользователь может с помощью RESTinio получить отдельные подстроки "text/plain" и "charset=utf-8". А с подстроками уже работать несколько проще.

Но тут возникает вопрос: а как именно должен выглядеть API для такого вот парсинга?

Первый вариант, который мне более-менее понравился и который был достаточно быстро и просто реализован, выглядит так:

вторник, 24 сентября 2019 г.

[prog] Еще один наглядный пример зачем нужен SObjectizer и подобные фреймворки

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

есть таск приготовить пирог, он «верхний». в его реализации есть вызовы тасков: сходи в магазин за ингридиентами пирога, подготовь ингридиенты, испеки. вот эти три явно только последовательно могут быть выполнены.

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

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

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

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

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

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

ИМХО, полезность инструментов вроде SObjectizer, CAF, cpp-taskflow, Boost.Fiber и им подобных в том, что вы можете тупо взять и быстро набросать черновое решение вашей задачи, чтобы посмотреть что в принципе получается. Получается ли вообще. Или вообще не получается. Если более-менее получается, то что у вас с производительностью и другими показателями.

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

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

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

Делать свой велосипед можно только если вы четко, на 146%, убедились в том, что существующие чужие разработки вас совершенно не устраивают. И только если у вас есть ресурсы на это занятие. Причем не только на первоначальную реализацию, но и на последующую поддержку.

Так что, если вы не Yandex, не Kaspersky Lab, не Mail.ru и, уж тем более, не Google/Facebook/Amazon/Aliexpress, то не делайте свои велосипеды. Попробуйте сперва что-нибудь готовое. Благо, есть что попробовать.


Если у вас есть задача, но недостаточно опыта, чтобы понять, как к ней подступиться и что использовать для ее решения, то возьмите SObjectizer спросите у нас ;)

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

[prog] Наглядный пример роста объема функции при эволюции кода

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

Речь о том, что далеко не всегда объем функций/методов удается держать под жестким контролем. Даже мне, давнему фанату принципа, что код функции должен умещаться на один экран. В качестве примера приведу код функции, над которой как раз таки мне самому довелось поработать. Сначала то, что было изначально. Затем то, что получилось после начала эксплуатации, исправлении одной серьезной проблемы и добавления новых хотелок после накопления некоторого опыта эксплуатации. Код из реального проекта, поэтому все комментарии удалены.

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

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

Итак, вот с чего все начиналось:

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

[prog.history] Между тем COBOL-у исполняется 60 лет и исчезать он никуда не собирается

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

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

Свежая статья на тему COBOL-а: "COBOL turns 60: Why it will outlive us all"

А я нескромно дам ссылочку на аналогичную запись из этого блога, написанную к 50-летию COBOL-а. Там ряд интересных фактов приведен. Ряд из которых, наверное, продолжает оставаться актуальным до сих пор.

пятница, 20 сентября 2019 г.

[prog.facepalm] Никогда такого не было и вот опять: Ошибки аллокации достаточно бесполезно ловить в пользовательской программе.

Ну вот как же задалбывают горе-программисты, которые проповедуют вот такие взгляды:

Ошибки аллокации достаточно бесполезно ловить в пользовательской программе.

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

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

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

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

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

Так что особого энтузиазма по поводу светлого будущего софтостроения я не питаю :(

пятница, 13 сентября 2019 г.

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

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


Итак, ViM. Все еще мой основной редактор для написания кода. Пользуюсь им и под Windows, и под Linux, и под FreeBSD. Под Linux-ом, кстати говоря, ViM как-то заметно шустрее работает.

Назвать себя продвинутым пользователем ViM-а не могу, знаю и применяю лишь некий базовый набор команд. Подозреваю, что лет 10 назад я знал про ViM гораздо больше. Но многие знания были утеряны за время менеджерства. Ну и сейчас программирование занимает далеко не 100% моего времени, так что надобности сильно погружаться в дебри ViM-а нет.

Тем не менее, с полной уверенностью могу сказать сейчас про себя: 15 years with Vim and steel learning :) And happy Vimmming!


Раздел второй, Ruby. В последний раз что-то более-менее серьезное писал на Ruby когда в начале 2016-го года добавлял в Mxx_ru поддержку работы с зависимостями (под условным названием MxxRu::externals). С тех пор пишу разве что небольшие одноразовые программки на выброс.

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


Раздел третий, Mxx_ru. Чем больше приходится иметь дел с CMake, тем больше радуюсь, что у меня есть Mxx_ru. Но, боюсь, радоваться мне остается недолго. В C++20 приняли какую-то (пока) неведомую для меня хрень в виде хитровывернутых модулей. Ну оно-то понятно, как можно в C++ добавить что-то понятное, простое и удобное? Очевидно, что никак. Такого не бывает, все должно быть через боль и страдания, это же C++...

Так вот, когда поддержка модулей появится в основных мейнстримовых компиляторах (т.е. VC++, GCC, clang), то придется делать выбор: либо допиливать Mxx_ru до поддержки C++ных модулей, либо полностью уходить с Mxx_ru.

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

Так что если не найдется какая-то гораздо более вменяемая альтернатива CMake (в виде какого-нибудь Meson-а или GN), то вполне возможен сценарий появления на свет Mxx_ru-2.0.


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

Жизнь покажет.