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

[life.cinema] Очередной кинообзор (2019/11-2019/12). С большим послесловием

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

Итак, вот список, в начале которого расположенны наиболее понравившиеся мне фильмы, а в конце -- полный шлак:

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

[comp] На какие "мелочи" я обращаю внимание при выборе ноутбука

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

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

Во-первых, это особенности клавиатуры, а именно:

  • наличие физических кнопок insert и delete. Независимых друг от друга. Вариант, когда insert выполняется через Fn+что-то-там мне сильно не нравится;
  • светодиодный индикатор на клавише CAPS LOCK. Вроде мелочь, но насколько сильно упрощает жизнь лично мне -- не передать;
  • ANSI раскладка на клавиатуре. Это когда клавиша с вертикальной и обратной косой находится под Backspace. Различия между ANSI и ISO раскладками можно посмотреть здесь;
  • наличие всех ctrl/alt/shift. Причем на своих местах. Я был удивлен, но бывают модели без правого ctrl :(
  • кнопка Fn должна быть с левой стороны;
  • отдельно расположенные клавиши управления курсором. Возможно, рядом с ними будут кнопки PgUp и PgDn (как на Dell Latitude и Lenovo ThinkPad). Но именно отдельно, а не упрятанные среди других клавиш (как здесь);
  • отдельные аппаратные кнопки Home и End -- это большущий плюс. Их полезность особенно ощущается, когда держишь ноутбук одной рукой, а второй пытаешься перемещаться по длинному документу.

Что касается раскладки клавиатуры, то на 13.3/14" ноутбуках наиболее подходящие для меня раскладки я видел на Toshiba Portege z930 и 14-дюймовых HP ProBook. Но не уверен, что на ProBook-ах есть светодиодная индикация на CAPS LOCK. Из 15-дюймовых ноутбуков с цифровым блоком на клавиатуре хорошее впечатление произвел Dell Vostro 15 3580.

Во-вторых, по разъемам:

  • раньше мне довольно часто приходилось пользоваться HDMI, поэтому отсутствие полноразмерного HDMI -- это для меня большой минус;
  • время от времени очень полезным оказывается нормальный LAN разъем. Причем именно нормальный, а не такой, как в современных тонких ноутбуках с нижней частью на пружинке. Как это ни странно, ранее умудрялись ставить полноразмерные RJ45 разъемы в такие тонкие ультрабуки как Toshiba Z930 и Dell Latitude e7270;
  • разъем для наушников слева. Поскольку я правша, то когда разъем для наушников справа, то провод от наушников то и дело мешается когда я убираю правую руку с клавиатуры. Например, когда нужно что-нибудь записать ручкой/карандашом на бумаге;
  • разъем для питания либо сзади (и тогда не суть важно, какой формы штекер питания), либо штекер питания Г-образной формы. Хуже всего, когда разъем питания сбоку, а штекер не просто прямой, но еще и достаточно большой и сильно торчит в сторону. Когда сидишь с ноутбуком на диване "по-турецки" и кладешь ноутбук к себе на ноги, то такой торчащий в сторону кабель питания сильно мешает, особенно если ноутбук и сам довольно большой.

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

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

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

В-шестых, угол раскрытия монитора. Чем больше, тем лучше. В идеале -- на 180 градусов, как у Dell Latitude и Lenovo ThinkPad.

Ну и напоследок пару слов о размере и весе ноутбука. По ощущениям, для 13.3/14" вес в диапазоне от 1200г до 1600г (плюс-минус 50 грамм в любую сторону) -- это как раз то, что нужно. А вот по толщине у меня особых претензий к тому, чтобы ноутбук был максимально тонким нет. Как по мне, так пусть будет 25-30мм толщиной, но с хорошим охлаждением, чем ультратонким, но сильно разогревающимся, когда на нем запускаешь компиляцию нетривиального C++ного кода с навороченной шаблонной магией внутри.

Пока из того, что я рассмотрел, наиболее близкими к тому, что мне нужно, являются HP ProBook 440 G6 и Dell Latitude 5401. Но ни один из них пока не "торкнул" настолько, чтобы захотелось его купить. И жаль, что HP-шные ProBook-и на низковольтных процессорах...

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

[prog.golang] Нужны наглядные примеры применения select для записи в Go-шные каналы

Обращаюсь за помощью к людям, которые знают Go (применяют в работе или просто интересуются): мне нужны примеры использования конструкции select для неблокирующей записи сообщений в канал. Примеры из жизни. Особенно такие, где select использовалась бы для записи сразу в несколько каналов. Поделитесь плиз. Особенно хорошо будет если кто-нибудь сможет дать ссылку на github/gitlab/bitbucket/... репозиторий, в котором эти примеры можно будет увидеть вживую.

Нужно мне этого для того, чтобы попытаться представить, как подобную вещь можно реализовать в C++ (в SObjectizer-е понятное дело). Но т.к. сам я к Go отношения не имею, то у меня гуглятся только какие-то учебные примеры, вроде чисел Фибоначчи :(

воскресенье, 8 декабря 2019 г.

[software.thoughts] Мой личный взгляд на тенденции вокруг OpenSource-библиотек

Данный пост можно рассматривать как продолжение недавнего поста про OpenSource лицензии и способы заработка на OpenSource разработках. Сегодня я попытаюсь изложить свой взгляд на то, как менялись тенденции в распространении библиотек и как это, по моему мнению, повлияло на экономику разработки этих самых библиотек. Правда, в разработке софта я не очень давно, профессионально занимаюсь этим всего 25 лет, поэтому буду делиться своими впечатлениями за этот небольшой промежуток времени. И, к сожалению, не смогу рассказать о том, что происходило в 80-е годы прошлого века и еще раньше.

Святые 90-е, рынок во все поля

Итак, насколько я помню, в 90-е годы продажа библиотек была вполне себе понятным и сложившимся бизнесом. Как мне представляется, тому способствовало несколько важных факторов:

пятница, 6 декабря 2019 г.

[prog.c++] Пользуюсь плодами упарывания шаблонами (PEG парсер в RESTinio)

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

language-range = (1*8ALPHA *('-' 1*8alphanum)) / '*'

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

Так вот при работе над очередным релизом RESTinio для разбора такого выражения был написан следующий код:

RESTINIO_NODISCARD
inline auto
make_language_tag_producer()
{
   return produce<std::string>(
         repeat(1u8u, alpha_symbol_producer() >> to_container()),
         repeat(0u, N,
               symbol_producer('-') >> to_container(),
               repeat(1u8u, alphanum_symbol_producer() >> to_container())
         )
   );
}

RESTINIO_NODISCARD
inline auto
make_language_range_producer()
{
   return produce<std::string>(
         alternatives(
               symbol_producer('*') >> to_container(),
               make_language_tag_producer() >> as_result()
         )
   );
}

Функция make_language_tag_producer() формирует объект, который будет отвечать за разбор первой альтернативы для language-range, а именно:

language-range = 1*8ALPHA *('-' 1*8alphanum)

Поэтому в make_language_tag_producer() практически так и говорится:

  • результатом разбора будет объект std::string. Этот объект будет служить результирующим контейнером, в который будут складываться найденные символы;
  • ожидается последовательность от одной до восьми букв. Каждая найденная буква добавляется в результирующий контейнер;
  • затем ожидается ноль или неограниченное количество повторений дефиса за которым следует последовательность из одной до восьми букв или цифр. Все найденные символы (дефис, буквы, цифры) добавляются в результирующий контейнер.

Функция make_language_range_producer() формирует объект, который будет выбирать одну из двух альтернатив. Что, опять же, практически прямолинейно и записано в коде:

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

В принципе, можно было бы записать и вот так:

inline auto
make_language_range_producer()
{
   return produce<std::string>(
         alternatives(
               symbol_producer('*') >> to_container(),
               produce<std::string>(
                     repeat(1u8u, alpha_symbol_producer() >> to_container()),
                     repeat(0u, N,
                           symbol_producer('-') >> to_container(),
                           repeat(1u8u, alphanum_symbol_producer() >> to_container())
                     )
               ) >> as_result()
         )
   );
}

Но мне проще было написать две маленьких функции с простыми правилами, чем одну с многословным правилом внутри.

Показанное выше правило для language-range является частью другого правила:

Accept-Language = 1#( language-range [ weight ] )
language-range  = <language-range, see [RFC4647], Section 2.1>

И парсер для Accept-Language записывается в коде вот так:

static auto
make_parser()
{
   using namespace accept_language_details;

   return produce< accept_language_value_t >(
      non_empty_comma_separated_list_producer< item_container_t >(
         produce< item_t >(
            make_language_range_producer() >> &item_t::language_range,
            maybe( weight_producer() >> &item_t::weight )
         )
      ) >> &accept_language_value_t::languages
   );
}

В общем, получилось прикольно. Пока более-менее нравится. Посмотрим как пойдет дальше работа над реализацией средств для разбора HTTP-полей. Если все будет хорошо и дальше, то может быть появится неплохая тема для рассказа на C++Russia в следующем году ;)

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

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

[prog.c++] Похоже, что втоптался в неприятную особенность static constexpr членов класса

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

Дабы не быть голословным создал просто демо-пример.

Тут есть заголовочный файл qvalue.hpp, внутри которого объявляется класс со static constexpr полем maximum.

Есть два файла (a.cpp и b.cpp), которые внутри себя загружают qvalue.hpp и используют qvalue_t::maximum. Эти файлы компилируются по отдельности и результаты их компиляции затем собираются в один статический файл.

Так вот, если компилировать более-менее современными версиями GCC и clang (скажем, начиная от GCC-5.5 и clang-6.0) с оптимизацией, т.е.:

$ g++ -std=c++14 a.cpp b.cpp main.cpp -O2

то никаких проблем нет, все успешно компилируется и линкуется.

Но вот если собирать без оптимизации, т.е.:

$ g++ -std=c++14 a.cpp b.cpp main.cpp -O0

то возникает ошибка линковки:

/tmp/ccHv44S3.o: In function `a(unsigned int&)':
a.cpp:(.text+0x1e): undefined reference to `qvalue_t::maximum'
/tmp/ccIQACTW.o: In function `b(unsigned int&)':
b.cpp:(.text+0x1e): undefined reference to `qvalue_t::maximum'
collect2: error: ld returned 1 exit status

При этом в VC++ проблем нет.

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

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

[life.music] "The Wall" от Pink Floyd сегодня 40 лет

Если верить Wikipedia, то сорок лет назад, 30 ноября 1979 года вышел знаменитый альбом "The Wall" группы Pink Floyd. С чем я всех любителей этой группы и поздравляю.

Для меня знакомство с "The Wall" состоялось в 1990-ом, хотя отдельные вещи из этого альбома слышал раньше и не то, чтобы они в меня заходили на ура. Но вот летом 90-го повезло посмотреть одноименный музыкальный фильм, который что-то перемкнул у меня в башке и сделал меня преданным поклонником флойдов. Хотя далеко не все их творчество мной воспринимается, но то, что они сделали начиная с 1973-го года слушается мной часто и с большим удовольствием вот уже скоро тридцать лет как.


Ну и отпрыгивая чуть-чуть в сторону. Лично мне кажется, что на период с начала 1960-х по середину 1980-х пришелся золотой век популярной музыки в течении которого создавалось просто невероятное количество нетленных хитов. Потом еще лет десять все это продолжалось по инерции, где-то до 1995-1996 годов, а потом началась неотвратимая деградация, продолжающаяся до сих пор.

Понятное дело, что все это субъективно, но даже я, более чем далекий от музыки человек, могу составить некий список любимых групп и исполнителей, в котором практически у всех рассвет и наивысшие достижения случились именно что до середины 1990-х: The Beatles, Rainbow, Led Zeppelin, Dire Straits, Uriah Heap, Pink Floyd, DIO, Queen, Joe Cocker, Leonard Cohen, Yello, Vangelis, Jean-Michel Jarre, Space, Depeche Mode, Fleetwood Mac, Deep Purple, Mike Oldfield, Eurythmics, Roxette... Это только то, что я могу слушать часто и в больших количествах. Не говоря уже про большое количество групп и музыкантов, у которых есть просто шедевральные вещи, но к которым в целом я отношусь довольно равнодушно. Самый яркий пример -- это Eagles со своим Hotel California.

В общем, если начнешь вспоминать забойные хиты, созданные в прошлом веке, которые и сейчас звучат более чем достойно, вроде Immigrant Song от Led Zeppelin или Smoke on the Water от Deep Purple, то можно будет вспоминать и вспоминать, и вспоминать. И сложится впечатление, что в 60-е, 70-е и 80-е все это было поставлено на поток. Чуть ли не промышленное производство организовали в то время.


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

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

[software] Мое мнение об OpenSource лицензиях и способах заработка на OpenSource библиотеках

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

Принципиальные различия между copyleft- и пермиссивными лицензиями

среда, 20 ноября 2019 г.

[prog.c++] Релиз RESTinio-0.6.1

Мы обновили свой встраиваемый асинхронный HTTP-сервер: RESTinio-0.6.1. В этом релизе стали доступны первые версии вспомогательных средств для парсинга содержимого HTTP-полей, работы с multipart телами запросов (в том числе и частичная функциональность загрузки файлов на сервер согласно RFC1867).

Более подробно о нововведениях можно прочитать в официальном релизе.

RESTinio теперь живет и развивается на GitHub-е, поэтому новую версию нужно брать там. Ну или же через vcpkg и conan.

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

PS. Фактически релиз состоялся еще неделю назад, но публичные анонсы пришлось отложить до тех пор, пока версия 0.6.1 попадет в vcpkg. Поэтому объявляем об обновлении RESTinio только сейчас.

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

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

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

Что-то кино я стал смотреть сильно меньше, чем раньше. Поэтому в прошлом месяце публиковать было особо и нечего. За два месяца же какое-то подобие списка накопилось и вот что получилось:

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

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

В тени луны (In the Shadow of the Moon, 2019). Неоднозначные впечатления. Вроде бы пока смотришь, то интересно и хочется узнать, что будет дальше. Но когда фильм заканчивается и пытаешься задуматься о том, а как все это в принципе возможно и почему так, а не вот так, то возникает ощущение "ну фигня же какая-то". Впрочем, это ощущение характерно практически для всех фильмах о путешествии во времени и временных петлях.

Падение ангела (Angel Has Fallen, 2019). В принципе, вполне в духе предыдущих фильмов этой серии. Но лично мне экшена было недостаточно, да и интриги никакой не оказалось, со злодеями все сразу стало понятно.

Рэмбо: последняя кровь (Rambo: Last Blood, 2019). Халтурно, как мне показалось. Сперва час и пятнадцать минут скучной мелодрамы, затем десять минут не очень внятно показанного экшена, потом еще пять минут соплей. В общем, фильм, который можно было бы и не снимать, и не смотреть.

Человек-паук: вдали от дома (Spider-Man: Far from Home, 2019). В приниципе, красочный и яркий фильм и вполне себе достойное продолжение супергеройской вселенной Marvel... Но очень уж он детский, т.е. детям младшего школьного возраста, наверное, посмотреть можно, а вот для их родителей там не будет ничего интересного.

Гемини (Gemini Man, 2019). Мне не понравился. Есть ощущение, что фильм предназначен для аудитории возрастом 10-12 лет, поэтому детям, может быть, и зайдет. Мне не зашел.

К звездам (Ad Astra, 2019). Как по мне, так скучная, нудная и слишком затянутая муть.


В общем, из достойного разве что "Оперативник" и "Кокаиновый барон".

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

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

среда, 30 октября 2019 г.

[prog.flame] Ну как же плохо, что комитет по стандартизации C++ работает как "бешеный принтер"

Одна из претензий к современному C++ состоит в том, что начиная с C++11 язык слишком быстро развивается и в него завозят слишком много ненужного. Один одиозный персонаж на LOR-е даже специальный термин для этого употребляет: мол, комитет печатает стандарты как бешеный принтер. Свежий топик на эту тему на LOR-е вместе в этим самым одиозным персонажем можно найти здесь.

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

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

class params_with_value_producer_t
   :  public producer_tag< parameter_with_mandatory_value_container_t >
{
   using actual_producer_t = std::decay_t<
         decltype(params_with_value_producer_details::make_parser()) >;

   actual_producer_t m_producer{
         params_with_value_producer_details::make_parser() };

public :
   params_with_value_producer_t() = default;

   RESTINIO_NODISCARD
   auto
   try_parse( source_t & from )
   {
      return m_producer.try_parse( from );
   }
};

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

Но в C++14 с этим нет никаких проблем. Посредством decltype можно узнать тип этого объекта. И объявить внутри класса объект данного типа.

Предлагаю вдуматься в это: современный C++ позволяет внутри нешаблонного класса объявить атрибут некоторого неизвестного автору класса типа.

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

auto
make_parser()
{
   return produce< parameter_with_mandatory_value_container_t >(
         repeat( 0, N,
            produce< parameter_with_mandatory_value_t >(
               ows(),
               symbol(';'),
               ows(),
               token_producer() >> to_lower()
                     >> &parameter_with_mandatory_value_t::first,
               symbol('='),
               alternatives(
                  token_producer()
                        >> &parameter_with_mandatory_value_t::second,
                  quoted_string_producer()
                        >> &parameter_with_mandatory_value_t::second
               )
            ) >> to_container()
         )
      );
}

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

А еще auto можно обнаружить в качестве типа возвращаемого значения метода params_with_value_producer_t::try_parse. Тут получается вообще смешно: класс params_with_value_producer_t содержит объект неизвестного типа, а метод try_parse этого класса возвращает что-то, что производит тот самый объект неизвестного типа. Т.е. можно хранить неизвестно что и возвращать незнамо что. И это в статически типизированном языке!

Короче говоря, то, что в C++14 делается элементарно, даже в C++11 вызывало бы затруднения и вряд ли было бы возможно в C++98/03. И все это можно использовать здесь и сейчас. Очень так неслабо упрощая жизнь самому себе и экономя собственное время (которое, как известно, никак не вернуть).

И это речь шла только о C++14. А ведь C++17 делает жизнь разработчика еще проще (вот недавний пример на тему).

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

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

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

вторник, 29 октября 2019 г.

[prog.c++] Эволюция средств поддержки загрузки файлов на сервер в RESTinio

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

воскресенье, 27 октября 2019 г.

[prog.c++] A follow-up for basiliscos's article "Request Response Message Exchange Pattern"

Ivan Baidakou, the author of rotor actor framework for C++, wrote a blog post with a comparison of request-reply pattern support in different C++ actor frameworks: "Request Response Message Exchange Pattern". I've found the reference to that article on Reddit and started to write a comment but decided to write my own blog post because the answer started to grow rapidly.

суббота, 19 октября 2019 г.

[prog.c++] Шла третья неделя упарывания шаблонами. Предварительный результат четвертого подхода к удобному парсеру HTTP-полей в RESTinio

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

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

struct cache_control_value_t
{
   using directive_t = std::pair<
         std::string,
         restinio::optional_t<std::string> >;

   using directive_container_t = std::map<
         std::string, restinio::optional_t<std::string> >;

   directive_container_t m_directives;

   static auto
   make_parser()
   {
      using namespace restinio::http_field_parser;
      using namespace restinio::http_field_parser::rfc;

      return produce< cache_control_value_t >(
         one_or_more_of_producer< directive_container_t >(
            produce< directive_t >(
               token_producer() >> to_lower() >> &directive_t::first,
               maybe(
                  symbol('='),
                  alternatives(
                     token_producer() >> &directive_t::second,
                     quoted_string_producer() >> &directive_t::second
                  )
               )
            )
         ) >> &cache_control_value_t::m_directives
      );
   }

   static std::pair< bool, cache_control_value_t >
   try_parse( string_view_t what )
   {
      using namespace restinio::http_field_parser;

      return try_parse_field_value( what, make_parser() );
   }
};

Напомню, что этот код решает проблему парсинга и получения значения из HTTP-поля Cache-Control, которое в RFC специфицируется следующим образом:

Cache-Control   = 1#cache-directive

cache-directive = token [ "=" ( token / quoted-string ) ]

где запись 1#element означает вот такое:

1#element => *( "," OWS ) element *( OWS "," [ OWS element ] )

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

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

[prog.c++] Почему C++ никому не нужен, но никуда не денется, на одном маленьком примере

Из свеженького:

template<
   template<classclass Container_Adaptor >
struct to_container_consumer_t : public consumer_tag
{
   templatetypename Container, typename Item >
   void
   consume( Container & to, Item && item )
   {
      Container_Adaptor<Container>::store( to, std::move(item) );
   }
};

Это шаблонный класс, который параметризуется другим шаблонным классом. И у которого всего один шаблонный метод, который параметризуется еще двумя типами.

Задача объекта этого шаблонного класса -- взять некое значение (не суть важно какое) и переместить его в некий контейнер (не суть важно какой). Как именно будет происходить перемещение to_container_consumer_t не знает. Он лишь знает, что это перемещение можно сделать посредством шаблонного параметра Container_Adaptor.

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

Так вот, C++ не нужен в мейнстриме именно потому, что в нем можно выражать метафункции, параметризуемые другими метафункциями. А в мейнтриме это никому не нужно. Мягко говоря.

И C++ никуда не денется именно потому, что в нем можно выражать метафункции, параметризуемые другими метафункциями. Это в принципе мало кому нужно. Но, в принципе, нужно. И вот те, кому нужно, и будут продолжать использовать C++.

За последние 3-4 недели довелось поучаствовать в нескольких спорах на тему применимости C++ и востребованности/оправданности новых возможностей C++. И прочитать еще несколько таких споров. И вот что я думаю: C++ должен превратиться во что-то типа Хаскелля. Многие слышали о нем. Многие думают, что это круто и с его помощью можно творить разные крутые вещи. Многие считают, что это не язык для простых смертных. Но мало кто имеет реальное представление о нем. Еще меньше имеют возможность его применять. И еще меньше им владеют.

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

Так думаю на данный момент.

Под катом маленький пример того, как этот to_container_consumer_t используется. И несколько реализаций Container_Adaptor.

среда, 16 октября 2019 г.

[prog.c++] Наткнулся на неожиданную для себя особенность объявления static constexpr в классе

Столкнулся вчера с ситуацией, в которой не компилировался код, казавшийся мне нормальным. Вот минимальный воспроизводимый пример:

#include <cstdint>

class vholder_t
{
public :
   using underlying_type_t = std::uint_least16_t;

   class trusted
   {
      const underlying_type_t m_value;

   public:
      explicit constexpr
      trusted( underlying_type_t v ) noexcept : m_value{v} {}

      constexpr auto
      get() const noexcept { return m_value; };
   };

   static constexpr trusted max{1000u};

private :
   underlying_type_t m_value;

public :
   vholder_t( trusted v ) noexcept : m_value{ v.get() }
   {}
};

int main()
{
   using trusted = vholder_t::trusted;

   constexpr trusted max{1000u};

   vholder_t v{ trusted{200u} };

   return 0;
}

Ошибка возникала в строчке:

static constexpr trusted max{1000u};

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

Собственно, на эту красоту можно полюбоваться на godbolt-е: https://gcc.godbolt.org/z/lP0XlT.

Самой полезной оказалась диагностика от GCC-8 (и GCC-9). Оказалось, что компилятор почему-то считает, что в месте объявления max тип trusted еще не определен, поэтому trusted и не может быть задействован в constexpr контексте.

Поэтому был применен следующий workaround:

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

[prog.c++] Упоролся шаблонами по полной программе или результат третьего подхода к удобному парсеру HTTP-полей в RESTinio

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

Анализируя результат второй попытки появилась, как тогда казалось, стройная идея по разделению парсера на такие понятия, как value_producer, value_transformer и value_consumer. По первым прикидкам казалось, что это позволит делать декларативное описание структуры значения HTTP-поля на основании грамматики этого поля из RFC.

Оно-то как-то так и оказалось. Но уж слишком многословно и мудрено.

В качестве примера под катом находится класс для разбора значения HTTP-поля Cache-Control. Содержимое этого поля в RFC определяется как:

Cache-Control   = 1#cache-directive

cache-directive = token [ "=" ( token / quoted-string ) ]

При этом конструкция вида 1#element определяется вот так:

1#element => *( "," OWS ) element *( OWS "," [ OWS element ] )

Т.е. правило для Cache-Control можно развернуть так:

Cache-Control   = *( "," OWS ) cache-directive *( OWS "," [ OWS cache-directive ] )

cache-directive = token [ "=" ( token / quoted-string ) ]

Т.е. этому правилу может удовлетворять, например, такая последовательность:

, ,   , max-age=5, , , no-transform,  ,,, ,

Итак, вот что получилось:

суббота, 12 октября 2019 г.

[work;life] СтифСтриму три года. Наполовину реклама, наполовину рефлексия на эту тему

Намедни нашей небольшой компании исполнилось три года. Поэтому поводу тост^W пост.

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

Кому и что мы можем предложить

Постепенно мы сосредоточились на двух вещах:

  • во-первых, наши собственные открытые продукты и связанные с ними сервисные услуги. Так что если кому-то нужна поддержка или консультация по SObjectizer и/или RESTinio, или доработка наших продуктов под ваши специфические нужды, то смело обращайтесь. Без помощи не оставим;
  • во-вторых, работа с небольшими клиентами/заказчиками, которые остались один на один с каким-то (не очень) старым софтом, без которого не обойтись, но который некому фиксить/сопровождать/развивать. На рынке не так уже много спецов по C++, маленькие компании не всегда могут себе позволить держать такого в штате или обратиться к крупному аутсорсеру. В таких случаях обращаются к нам и мы разбираемся в чужом говнокоде, заставляем это "наследие" работать. Высказываем свои соображения, в том числе и по целесообразности продолжения "жрать кактус".

Мы хоть и специализируемся на C++ и, по мере надобности, приводим в рабочее состояние копролиты на чистом С, но не имеем цели всеми средствами удержать клиента в рамках C/C++. Если проблему клиента выгоднее решить на Go, C# или Java, мы именно это и порекомендуем сделать. Лучше пусть клиент возьмет других исполнителей, нежели со временем обнаружит что мы его, мягко говоря, накололи.

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

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

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

Любой желающий может заглянуть в потроха наших разработок (SObjectizer и so5extra, RESTinio, json_dto). Если вас такое качество устраивает, то обращайтесь, пообщаемся.

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

Наш стандартный рейт для краткосрочных заказов 35USD в час. Основная часть клиентов у нас пока из exUSSR и мы понимаем, что этот ценник для многих слишком высокий, но дешевле не можем, извините. Более низкую стоимость мы выставляем для долгосрочных работ или если дело касается открытых доработок наших OpenSource решений.

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

Стоило ли оно того и некоторые выводы/соображения?

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

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

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

Можно выделить несколько вещей о которых я, в принципе, знал, но объем и значимость которых не осознавал в полной мере:

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

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

Ваши проблемы будут с вами 24x7x365. Если не понятно почему, то см. выше про "Никто кроме вас".

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

Воронка продаж. Она существует.

Время -- деньги или "time to market really matters". Ваше отношение ко времени и деньгам изменится. Одним из основных вопросов станет "когда продукт станет доступен на рынке?", а каждый день просрочки вы в прямом смысле начнете ощущать собственным кошельком. Соответственно, качество вашего продукта станет всего лишь одним из свойств продукта. Именно поэтому выше я и говорил, что мы работаем "более-менее качественно": качество стоит очень дорого, мало кто готов за это платить, т.к. платить приходится не только и не столько деньгами, сколько временем, которое не купить и которым никто не располагает в достаточной мере. Ну кроме наемных работников, само собой ;)

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

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

Ничто не вернет времени, которое вы потратили на бизнес, а не на семью. Ничто.

Ну с большего как-то все. Надеюсь, что никого сильно не напугал ;)

Благодарности

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

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

Большое спасибо всем, кто выбрал наши продукты и обратился к нам за помощью! Надеюсь, мы вас не разочаровали и вы останетесь с нами и порекомендуете нас своим коллегам и партнерам.

Найти нас можно здесь: stiffstream.com.

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

[prog.c++] Первые результаты второго подхода к удобному парсеру HTTP-полей в RESTinio

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

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

[prog.c++] Что-то я неслабо затупил из-за отсутствия fold expression в C++14

Столкнулся со следующей задачей: есть экземпляр std::tuple, который содержит объекты-парсеры. Нужно пробежаться от первого элемента тупла к последнему и для каждого парсера вызвать метод try_parse с определенными параметрами. Но! Нужно прервать эту итерацию как только очередной try_parse вернет false.

В C++17 это элементарно записывается посредством fold expression:

templatetypename Tuple, std::size_t... Indexes >
bool try_parse_impl(
   const std::string & source,
   Tuple & t, 
   std::index_sequence<Indexes...>)
{
   return (... && std::get<Indexes>(t).try_parse(source));
}

templatetypename... F >
bool try_parse(const std::string & source, std::tuple<F...> & parsers)
{
   return try_parse_impl(source, parsers, std::index_sequence_for<F...>());
}

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

А вот как сделать тоже самое, но в C++14, где fold expression нет?

Единственное, что у меня пока вышло -- это вот такое:

templatebool valid_index, std::size_t I >
struct try_parse_impl
{
   templatetypename... Parsers >
   static bool apply(const std::string & source, std::tuple<Parsers...> & t)
   {
      if( std::get<I>(t).try_parse(source) )
         return try_parse_impl< (I+1 < sizeof...(Parsers)), I+1 >::apply(source, t);
      else
         return false;
   }
};

template< std::size_t I >
struct try_parse_impl< false, I >
{
   templatetypename... Parsers >
   static bool apply(const std::string &, std::tuple<Parsers...> &)
   {
      return true;
   }
};

templatetypename... F >
bool try_parse(const std::string & source, std::tuple<F...> & parsers)
{
   return try_parse_impl< (0 < sizeof...(F)), 0 >::apply(source, parsers);
}

Возможно, вместо всего этого как-то можно было бы обойтись более прямой работой с std::index_sequence. Но что-то для этого моего понимания механизма распаковки параметров в variadic templates не хватает. И примеры, которые я находил в Интернетах не очень помогают, т.к. там идет итерация по всем элементам тупла, без анализа результата на очередной итерации и прерывания цикла.

Поиграться с полным примером можно здесь.

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

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

[life.work] Юбилей, наверное. 25 лет профессиональной деятельности как-никак

По случаю тяпницы позволю себе немного погрузиться в воспоминания. Если мне не изменяет склероз, то где-то в октябре 1994-го года я устроился инженером-программистом в КБ Системного Программирования, где для меня и была заведена трудовая книжка. Так что 25 лет назад началась моя профессиональная деятельность в области разработки программного обеспечения.

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

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

Ну в общем, как-то так. Почему-то показалось, что 25 лет -- это срок о котором можно написать пару срок.

Представить себе еще 25 лет в профессии, почему-то страшно :(

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

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

Вот что я о себе написал в начале 2000-х и, полагаю, это было весьма точное определение:

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

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

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

[comp.prog.thoughts] Праздное про ноутбуки и производительность софта

Позволю себе еще раз продолжить тему ноутбуков, ибо давеча был несколько удивлен. Дело в том, что когда у меня летом обострились проблемы с моими старыми рабочими ноутами и начались поиски новой рабочей машинки, я разыскал у себя древний Lenovo ThinkPad E130. Что-то типа вот этого, с двухядерным Pentium, работающем на частоте 1.4MHz, с 4GiB RAM и 320Gb HDD.

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

[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-го года я вообще не надеялся на то, что этот инструмент проживет столько лет. Теперь уже и самому стало очень интересно, будет ли через пять лет продолжение. И если будет, то какое именно?

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

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

[prog.c++] Проверки на noexcept "для бедных"

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

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

В C++11 в этом направлении сделали важный шаг: добавили в язык спецификатор noexcept. Так что теперь из прототипа функции можно узнать о том, что она исключений не бросает.

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

void some_complex_class::cleanup() noexcept {
   cleanup_part_one();
   try {
      cleanup_part_two();
      cleanup_part_three();
   }
   catch(...) {} // Just ignore exceptions.
   cleanup_part_four();
}

Можно легко накосячить.

четверг, 5 сентября 2019 г.

[prog.c++] Небольшое, но важное обновление для SObjectizer-а.

Мы обновили SObjectizer до версии 5.6.1. Изменений совсем мало, просто дошли руки сделать фичу, которая не попала в релиз 5.6.0 из-за недостатка ресурсов при подготовке ветки 5.6. Но зато это первая версия, которая была разработана на GitHub после того, как Atlassian заявил о запланированном удалении Hg-репозиториев с BitBucket-а. Так что теперь дальнейшее развитие SObjectizer-а и so5extra будет происходить на GitHub-е, а SouceForge будет служить зеркалом для тарболлов.

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

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

Ну, например, время от времени высказывается сожаление о том, что SObjectizer завязан на реализацию многопоточности из C++ной стандартной библиотеки. Т.е. на std::thread, std::mutex и вот это вот все.

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

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

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

Сообщить нам о том, чтобы вы хотели видеть в SObjectizer-е можно через Issues на GitHub, соответствующую Google-группу, Feature Requests на SourceForge. Так же, напомню, что наша компания осуществляет поддержку SObjectizer-а и so5extra (как и других наших продуктов). Так что если вы хотели бы иметь кастомизированную версию SObjectizer-а под свои нужды или вам нужна наша помощью в разработке вашего продукта, то с нами всегда можно договориться ;)

среда, 4 сентября 2019 г.

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

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

Мертвые не умирают (The Dead Don't Die, 2019). Укуренный фильм про зомби от укуренного режиссера, известного своими укуренными фильмами. Зайдет точно не всем. Поэтому рекомендовать не могу. Мне показался слишком скучноватым, хотя некоторые моменты улыбнули.

Мышеловка (4x4, 2019). Аргентино-испанская попытка снять драму+триллер на остросоциальную тему. Получилось не очень. Хотя, возможно, это просто в наших палестинах уровень преступности пока еще не достиг такого уровня, как в Испании, поэтому острота поднятой в фильме проблемы не ощущается.

Люди Икс: Тёмный Феникс (Dark Phoenix, 2019). Наверное, достаточно добротное продолжение серии фильмов про людей Икс. Но очень уж скучное, чуть не заснул при просмотре. Хотя снято все красиво, этого не отнять.

Форсаж: Хоббс и Шоу (Fast & Furious Presents: Hobbs & Shaw, 2019). Настолько тупая и невероятная сказочка, что на ее фоне даже фильмы про Джеймса Бонда начинают казаться документальными. Отчасти это компенсируется юмором и красивой актрисой в главной женской роли, но лишь отчасти.

Игра Ганнибала (Nomis, 2018). Просто удивительно, как можно было собрать такой звездный актерский состав и снять такое унылое и скучное говно.

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

[prog.flame] Отличная статья: "How to Build Good Software"

Из этих наших интернетиков принесло ссылку на отличную статью под названием "How to Build Good Software". Статья капитанская. Для тех, кто варится в разработке софта не первый десяток лет, там ничего нового или незвестного нет. Просто хорошая выжимка вещей, продиктованных самим здравым смыслом.

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

Для меня прям бальзамом на душу прозвучало несколько пунктов в статье. Во-первых, про то, что уход разработчиков несет для компании невосполнимые потери в виде потери знаний, которые нигде и никак не были зафиксированы (а зафиксировать все в принципе невозможно). Тут я не буду проявлять скромность и дам ссылку на свой собственный пост в этом же блоге восьмилетней давности как раз на тему важности этих знаний: [prog;work;thoughts] Прибавка к зарплате “за выслугу лет” программистам, да и не только….

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

В общем, статья хорошая. Прочитать ее полезно. А тем, у кого меньше 10 лет в разработке софта за плечами, так и обязательно ;)

среда, 28 августа 2019 г.

[comp.notebooks] Продолжаю многострадальную тему обновления ноутбука...

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

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

Посему муки выбора основной рабочей лошадки продолжаются. Причем, судя по анализу того, что и как приходится делать, в пределе нужно два ноутбука. Один под Windows (для Office, для беспроблемного сопряжения с принтерами/сканерами в офисе/дома, для обработки фото и пр. вещей), второй под Linux (для программирования и тестирования). Ибо плотно поработав в последние 3-4 месяца с Linux-ом на реально железе возвращаться на Linux под виртуалками очень не хочется. А если ноутбуков будет два, то весьма критичным будет их размер и вес, т.к. время от времени я таскаю из офиса домой сразу два ноута. И если вес каждого из них до 1.5кг, то с этим проблем нет. А вот если каждый весит под 2кг, да еще и здоровый+тяжелый блок питания у каждого, то как-то кисло выходит. Ну и, в случае замены сперва одного ноута, а затем и второго, вопрос цены каждого из них становится совсем не праздным.

Так же меня не впечатлила производительность современных низковольтных процессоров, вроде Core i5-8250U или i7-8550U, у которых частоты, номинально, могут скакать более чем в два раза. Но, по факту, из-за тротлинга процессоры все равно до своей максимальной производительности не добираются практически никогда. Поэтому, по субъективным впечатлениям, я не вижу заметного прироста скорости выполнения моих повседневных задач когда пересаживаюсь с ноутов пяти или семилетней давности на новую машинку. А тот прирост, который есть, возможно, в большей степени обусловлен современным SSD.

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

Поэтому в качестве основных вариантов я сейчас рассматриваю дешевые ноутбуки, у которых минимальная частота процессора изначально выше 2GHz. Например, это совсем дешевый HP 240 G7 или две модели чуть подороже: HP 14 и Lenovo IdeaPad 530s-14ARR на AMD Ryzen. При этом на HP 14 должна быть более удобная клавиатура, а у IdeaPad 530s -- IPS-ная матрица.

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

Поэтому план такой: доэксплуатировать то, что есть в наличии, а потом, когда уже совсем припрет, прикупить что-то из категории "дешево, но сердито". Кстати, никому Asus X570UD (i7-8550U, 8GiB, 256Gb SSD + 1000Gb HDD, 15.6" IPS 3840x2160 + GeForce GTX 1050 4GiB) не нужен? В отличном состоянии, гарантия "5-го элемента" еще в течении 11 месяцев. Отдам не дороже 2000 BYN ;)

суббота, 24 августа 2019 г.

[work.f*ck] О трудоемкости/стоимости SObjectizer-а на простом примере

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

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

среда, 21 августа 2019 г.

[work.sadness] Поддержку Mercurial удаляют с BitBucket-а. Полностью. Что в этом самое плохое?

Вчера получил письмо счастья от Atlassian, в котором говорится об удалении поддержки Mercurial-а и всех Mercurial-репозиториев с BitBucket-а к 1-му июня 2020-го года:

After much consideration, we've decided to remove Mercurial support from Bitbucket Cloud and the API. Mercurial features and repositories will be officially removed from Bitbucket and its API on June 1, 2020.

Полный текст этого печального анонса можно увидеть здесь.

Свое первое возмущение по этому поводу я уже высказал вчера в грубой форме в FB. Но это все лирика. Физика в том, что "just a business". Atlassian владеет BitBucket-ом, вкладывает в его поддержку свои деньги и имеет полное право делать то, что им выгодно, наплевав на мнение 1% своих пользователей. Тем более тех пользователей, которые пользуются этим сервисом бесплатно.

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

И вот эта сторона решения Atlassian-а мне непонятна. Хостинг исходных текстов -- это специфическая тема. Люди прибегают к подобным хостерам (будь то GitHub, BitBucket, SourceForge, GitLab или еще что-то) как раз для того, чтобы сделать результаты своих трудов доступным для всех желающих на долгое время. Какой-нибудь древний проект на SourceForge, который уже 15 лет не развивается, все равно доступен и информация оттуда может быть получена даже если автор этого проекта уже давным-давно от проекта отошел (а может автора уже и в живых нет).

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

В конце-концов, для OpenSource основной смысл хостеров типа SourceForge и GitHub именно в том, что там хранится все, что когда-то там было создано.

А вот Atlassian вместо того, чтобы заморозить, но оставить старые Hg проекты доступными, просто удалит все к чертям.

И вот этого вот решения я понять не могу.

Экономически не выгодно вам пилить поддержку Hg в своей хостинговой платформе? OK. Перестаньте это делать. Переведите все существующие Hg проекты на BitBucket-е в режим read-only. Пусть все, что люди ранее сделали на вашей платформе... Нет, даже не так, а вот так: все, что люди ранее сделали на ВАШЕЙ платформе останется доступным для всех желающих.

Понятно, что даже на саппорт read-only репозиториев придется тратить какие-то деньги. И если это для Atlassian-а настолько серьезные деньги, то можно было бы ограничить время существования read-only репозиториев. Скажем, всего 5 лет. Потом навечно в /dev/null. Но хотя бы в течении этих пяти лет ссылки на репозитории, issues, Wiki, тарболлы и пр., которые в невероятном количестве раскиданы по всему Интернету, будут оставаться валидными.

PS. Ну вот честно не могу даже вообразить себе степень эффективности менеджеров Atlassian-а, принявших решение тупо убить проекты с Hg репозиториями вместо того, чтобы перевести их в read-only mode. Хотя бы лет на пять. Какой-то, блин, контрольный самострел в голову.

понедельник, 19 августа 2019 г.

[prog.flame.c++] Dropbox выставляет свою Djinni на мороз

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

Между тем штука значимая.

Я вот несколько лет то тут, то там рассказываю про такую область применения C++, как написание "ядра" кросс-платформенных приложений. Типа того, что бизнес-логика пишется на плюсах, а GUI и какие-то штуки по интеграции с системно-зависимыми сервисами на родных для платформы языках (вроде Java на Android-е, Objective-C/Swift на iOS, C++ или .NET на Windows).

И в качестве примера приводил разработку Djinni от Dropbox-а.

А вот, оказалось, что Dropbox такой подход к кроссплатформенности запарил и они от него отказались.

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

Остальные же причины, в частности, про оверхед пользовательских фреймворков и библиотек, вызывают недоумение. Никто не заставлял Dropbox делать свою собственную библиотеку для работы с Json в С++. Очень похоже на NIH-синдром и его последствия.

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

суббота, 17 августа 2019 г.

[comp.notebooks] Минутка дыбра: продолжение истории про выбор ноутбука

Развитие темы, начатой на прошлой неделе. Я прислушался к мнению уважаемых френдов, которые сказали, что FullHD разрешение -- это уже вчерашний день и нужно что-то принципиально получше. В итоге приобрел 15.6" ноутбук с UHD разрешением (3840x2160): Asus X570UD. Если кому-то интересно почитать о моих текущих впечатлениях, то милости прошу под кат.

пятница, 16 августа 2019 г.

[prog.flame] Ну не могу не утащить великолепную цитату в склерозник. Про RAII в виде создания процессов

Ну прекрасно же! Просто прекрасно. Ни добавить, ни убавить.

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

Взято отсюда. Это человек решил вступить в спор о том, чего не хватает в чистом С, в частности об отсутствии в C классов и RAII.

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

среда, 14 августа 2019 г.

[prog.c++] Быть или не быть tls_inspector-у в RESTinio? И если быть, то каким?

В комментариях к одной из статей про RESTinio на Хабре прозвучала идея сделать объект tls_inspector для проверки TLS-ных подключений. И не только проверки, но и просто извлечения из сертификата клиента различной полезной для сервера информации.

Дошли руки заняться этой темой. Но есть ощущение, что блин получился комом. Если кому-то интересно, то суть я попытался описать здесь: https://github.com/Stiffstream/restinio/issues/44
Там же можно высказать и свое "фи". Хотя его можно высказать и прямо здесь ;)

вторник, 6 августа 2019 г.

[comp.notebooks] Минутка дыбра: очередной приступ поиска нового ноутбука :)

Бывает у меня периодически такое специфическое состояние: я начинаю выбирать себе новый ноутбук. Иногда просто потому, что время идет, моей "основной" машинке, уже больше пяти лет. А "резервной" уже около семи. Иногда потому, что на обоих машинах время от времени что-то оказывает. Так, последний приступ выбора ноутбука был в районе Нового Года, когда на "основной" стала слишком часто проявляться его родовая травма в виде спонтанных самовыключений, а на "резервной" начала выходить из строя клавиатура и аккумулятор перестал держать заряд. Но тогда удалось обойтись без приобретения новой машины путем латания уже имеющихся. Однако, прошло какое-то время, латки поизносились и вновь стал мучать вопрос о приобретении нового ноута, который сможет поработать в режиме 12/7 следующие несколько лет.

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

Дополнительный забавный момент в том, что в последние месяца три "резервная" машинка, которая у меня под Linux-ом, стала для меня основной рабочей станцией. Т.к. практически все сейчас делается под Linux-ом. А в Windows я захожу лишь по необходимости: с документами в MS Office разобраться, провести проверки под Visual C++, поработать в Lightroom-е, если где-то что-то заснял. Так что сейчас я практически линуксоид ;)

Выбор оказывается весьма непростой.

Основные требования, которые должны быть удовлетворены в обязательном порядке -- это, минимум 8GiB RAM, 4-е ядра (причем нормального Core i5/7 или Ryzen, а не какого-нибудь маломощного мобильного Pentium-а с частотой 1.1GHz), SSD диск от 128Gb, матовый экран.

Еще один серьезный момент: производитель. Из того, что широко представлено у нас, я доверяю разве что HP и Lenovo, плюс, в несколько меньшей степени, Asus и Dell.

Пожалуй, главный сдерживающий фактор -- это размер и вес. 17" экран с FullHD разрешением, наверное, был бы для меня вполне подходящим выбором. Но, боюсь, в мой рюкзак он не влезет. Да и таскать с собой каждый день туда-сюда 3кг вместо 1.5кг как-то не воодушевляет. Я, конечно, товарищ немаленький и доходягой вроде не являюсь, но лишний груз взваливать на спину не хочется :)

И если с размером и весом проблемы были ожидаемы, то неожиданными оказались вот такие факторы:

  • неудобные клавиатуры. Отсутствие отдельной клавишы Ins -- это обычное дело. Так же, как и кнопок PgUp/PgDn и Home/End. Причем, если для 13.3" или даже 14" ноута это еще понять можно (хотя можно посмотреть на HP ProBook 440 G6, например, там с нужными кнопками все нормально), то вот для 15" или 17" ноутов, в которые умудряются впихивать еще и блок цифровых клавиш отсутствие отдельной Ins -- это что-то за гранью моего понимания;
  • наличие USB 2.0 разъемов на моделях от 2017, 2018 и даже 2019-го годов. Скажем, ноут от 2018-го у которого два USB 2.0 и всего один USB 3.0 -- это вообще что такое?

При этом всем я ориентируюсь на весьма бюджетный сегмент -- плюс минус 1K USD. Ибо мне нужен рабочий инструмент, который я буду гонять и в хвост, и в гриву, и который не должно быть жалко, если я где-то долбану его по дороге или уроню на него что-нибудь, или он тупо сдохнет под непосильной нагрузкой :) Типа премиуальный/имиджевый ноутбук у меня был, это совсем другая история.

Благо, рабочих лошадок стоимостью между $700 и $1000 с 4-х ядерными Core i5 8-го поколения, 8GiB RAM и SSD на 256GB не так уж и мало. Например, HP ProBook 440 G6 6BN87ES с параметрами: Intel Core i5 8265U, 8GiB RAM DDR4, SSD 256GiB, FullHD IPS-дисплей, металлический корпус и вес порядка 1.6кг. Стоит такая машинка на данный момент порядка $800. Жаль только, что 14" :(

Пока в short-list-е у меня две модели от Lenovo: ThinkPad e590 и IdeaPad S340-15IWL. В пользу ThinkPad говорит IPS-ная матрица, намного более удобная для меня клавиатура (защищенная от протечек при том). А в пользу IdeaPad немного меньший размер и заметно меньший вес.

Но в обоих вариантах смущает то, что FullHD на 15.6" экране. С учетом того, что ноут мне нужен для работы с кодом в Linux-е, это настораживает. ИМХО, слишком мелкий пиксель. Хотя шрифты можно увеличить, да. Но все равно как-то мелковато.

В связи с этим хочу спросить у тех, кто работает с ноутами-пятнашками с FullHD разрешением: как вам? Нормально или размер пикселя все-таки хотелось бы побольше?

PS. Жаль, что я завязан на C++. Программировал бы на Go, мог бы обойтись чем-то вроде вот такого. С дополнительным SSD, естественно ;)