вторник, 1 января 2030 г.

О блоге

Более двадцати лет я занимался разработкой ПО, в основном как программист и тим-лид, а в 2012-2014гг как руководитель департамента разработки и внедрения ПО в компании Интервэйл (подробнее на LinkedIn). В настоящее время занимаюсь развитием компании по разработке ПО stiffstream, в которой являюсь одним из соучредителей. Поэтому в моем блоге много заметок о работе, в частности о программировании и компьютерах, а так же об управлении.

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

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

[life.photo] Характерный портрет: вы и ваш мир моими глазами. Безвозмездно :)

Вы художник? Бармен или музыкант? Или, может быть, коллекционер? Плотник или столяр? Кузнец или слесарь? Владеете маленьким магазинчиком или управляете большим производством? Реставрируете старинные часы или просто починяете примус? Всю жизнь занимаетесь своим любимым делом и хотели бы иметь фото на память?

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

суббота, 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.