пятница, 12 июля 2019 г.

[prog.flame] Пятничное дополнение ко вчерашнему посту. Снова про Rust

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

А что будет, когда на Rust-е начнут программировать разработчики уровня сильно ниже среднего?

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

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

Так, на примере C++ видно, что есть языки, которые не прощают тупость разработчика.

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

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

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

В свое время, когда C++ был настоящим мейнстримом, таковых было множество. Со временем, к счастью, изрядная их часть перешла в Java и C#. Но часть, к сожалению, еще осталась. И, если Rust продолжит набирать популярность, они неизбежно туда подтянутся.

Имхо, вариант Haskell-я, когда посредственности в этот язык просто не идут, здесь не прокатит. Т.к. Haskell в силу разных причин никогда и не мог претендовать на широкое использование. В отличии от Rust-а.

Так что будет интересно посмотреть на то, что произойдет. Выпрямит ли Rust-овский компилятор растущие из жопы руки? Или этот самый компилятор наеб*т посредством расставления unsafe в коде, ибо дедлайны, нужно фичи в прод выкатывать, а не ублажать borrow checker.

четверг, 11 июля 2019 г.

[prog.flame] Предлагаю новый слоган для D: "Fast as C++, safe as Rust, easy as Java"

Намедни в LOR-овском срачике сформулировал свое впечатление от наблюдений за тем, кто, как и откуда переходит на Rust. На мой взгляд, в Rust идут люди, которые всю жизнь программировали на безопасных языках с виртуальной машиной. Ну там Java, Python, Ruby, JavaScript, Erlang, Clojure и т.д. А так же C++ники, которые толком C++ не освоили, а уж о том, что такое современный C++, вообще не имеют никакого понятия.

А потом довелось прочитать серию постов небезызвестного своими "плачами Ярославны" НикиТонского: "С высоты", "С высоты-2" и "С высоты-3". И еще более утвердился в том, что мои наблюдения более-менее коррелируют с действительностью.

вторник, 9 июля 2019 г.

[prog.c++] Что-то затупил с выписыванием условия для SFINAE

Стоило отвлечься на пару недель на написание документации и статей, как остаточные знания C++ улетучились из головы и случились жуткие тормоза при попытке написать условие для SFINAE :(

Нужно было вот что: в шаблонный фабричный метод передается ссылка на контейнер (или что-то, что притворяется контейнером, какой-нибудь range, к примеру). Нужно, чтобы этот шаблонный фабричный метод попадал в рассмотрение компилятора только если передали ссылку на контейнер/range. Для этого я решил написать проверку того, что у контейнера есть методы begin/end, и что при разыменовании итератора, возвращенного begin-ом, получается нужный мне тип.

Затык случился при попытке написать проверку на наличие begin/end. Вроде как в C++17 нет штатных средств проверить, что decltype(std::declval<const Another_Container &>().begin()) является валидным типом. Т.е. не нашел в type_traits чего-то вроде is_valid_v<constexpr-expr>. Пришлось извращаться вот таким образом:

templatetypename Another_Container >
static std::enable_if_t<
      !std::is_same_v< Container, Another_Container >
      && !std::is_same_v<
            decltype(std::declval<const Another_Container &>().begin()),
            void >
      && !std::is_same_v<
            decltype(std::declval<const Another_Container &>().end()),
            void >
      && std::is_same_v<
            std::decay_t<
                  decltype(*(std::declval<const Another_Container &>().begin())) >,
            mbox_t >,
      mbox_t >
make(
   environment_t & env,
   const Another_Container & destinations )
   {
      return make( env, destinations.begin(), destinations.end() );
   }

Т.е. сравнивать тип итератора, возвращаемого begin/end, с void. Ведь итератор, в принципе, не может иметь тип void. Поэтому если begin/end есть, то возвращать они должны не void.

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

Upd. Обновленный текущий вариант, полученный в результате обсуждения в FB, пока выглядит так:

templatetypename Another_Container >
static std::enable_if_t<
      !std::is_same_v< Container, Another_Container >
      && std::is_convertible_v<
            decltype(
               ++std::declval<
                  std::add_lvalue_reference_t<
                     decltype(std::begin(std::declval<const Another_Container &>()))>
                  >()
               == std::end(std::declval<const Another_Container &>()) ),
            bool>
      && std::is_same_v<
            std::decay_t<
                  decltype(*std::begin(std::declval<const Another_Container &>())) >,
            mbox_t >,
      mbox_t >
make(

Здесь сделано упрощение + поддержка C-шных массивов + несколько дополнительных проверок, которых не было ранее, а должны были бы быть. И, что самое интересное, это даже VC++ компилируется (как VS2019, так и VS2017).

Upd.2 У варианта с SFINAE обнаружилось два серьезных недостатка. Первый, который был виден изначально, это отсутствие поддержки ADL. Т.е. если Another_Container -- это какой-то пользовательский тип, для которого пользователем определены собственные begin/end функции, то SFINAE бы его забраковал. Поскольку внутри SFINAE используются std::begin/std::end. И как внутри условия SFINAE разбираться с ADL без создания каких-то дополнительных вспомогательных сущностей -- это отдельный квест.

Второй недостаток -- это неспособность Doxygen-а сгенерировать документацию для метода с навороченными условиями SFINAE. Показанный в первом апдейте вариант кода Doxygen не может переворить и просто не включает такой make в итоговую документацию :(

Посему в итоге отказался от SFINAE, а все проверки перенес внутрь метода в static_assert-ы. Получилось что-то вроде:

templatetypename Another_Container >
static mbox_t
make(
   environment_t & env,
   const Another_Container & destinations )
   {
      using std::begin;
      using std::end;

      // Ensure that destinations if a container or range-like object
      // with mbox_t inside.
      static_assert(
         std::is_convertible_v<
            decltype(
               ++std::declval<
                  std::add_lvalue_reference_t<
                     decltype(begin(std::declval<const Another_Container &>()))>
                  >()
               == end(std::declval<const Another_Container &>()) ),
            bool>,
         "destinations should be a container or range-like object" );

      static_assert(
         std::is_same_v<
            std::decay_t<
                  decltype(*begin(std::declval<const Another_Container &>())) >,
            mbox_t >,
         "mbox_t should be accessible via iterator for destinations container (or "
         "range-like object" );

      return make( env, begin(destinations), end(destinations) );
   }

PS. Ну и современный C++ в очередной раз оставил впечатление вроде "ну круто, чё, а нельзя ли все тоже самое, но раза в полтора-два попроще?". Так что ждем C++20 с концептами.