Возвращение к C++

Последние лет 6 я всё меньше и меньше занимался разработкой на C++, количество использования фактически шло по убывающей до нулевого значения год назад. Чего только не было в качестве основного языка, и Go, и Elixir и даже небольшие отрезки времени чистый Python. Но так случилось, что с переходом на новое место работы C++ вновь стал моим основным рабочим инструментом. Появилась насущная необходимость шустро освежить знания в голове и, в идеале, совместить это с чем-то полезные. В итоге я решил решил порешать задачки на тему криптографии от cryptopals дабы как-то скрасить ожидание открытия заинтересовавшего меня курса по криптографии на Коурсере.  Ну а так как теперь мне действительно есть с чем сравнивать C++, ощущения от языка получаются более цельные, как мне кажется.

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

Так же выяснилось что наконец-то можно забыть про весь этот ад с установкой и настройкой сторонних библиотек, т.к. Conan отлично справляется с загрузкой и подключением внешних зависимостей при наличии предельно простой конфигурации. Само собой, если хочется получить так необходимый для C++ статический и динамический анализ всё еще потребуется ручная небольшая доработка сборочных скриптов напильником, но интеграция того-же clang-tidy в CMake выполнена достаточно хорошо.

В рамках создания универсального всемогутора в C++ решили затащить элементы функционального программирования в виде range-v3 и соответствующего раздела C++20. Тут у меня после активного использования Python, Elixir и увлечения Clojure остались смешанные чувства. Я бы описал range как достижение для C++, но полное говно с точки зрения функциональных языков. Можно взять простейший пример для иллюстрации: есть массив массивов, его надо объединить в один, выкинуть дубликаты, отсортировать, и взять 3 минимальных элемента.

Решение на Clojure:

(def v1 (list (list 3 4 5 1 7)
              (list 1 3 6 3 8)))

(-> v1
    flatten
    distinct
    sort
    (#(take 3 %)))

и, казалось бы, такое же решение на C++:

std::vector<size_t> res = v1 | ranges::views::join
                             | ranges::to_vector
                             | ranges::actions::sort
                             | ranges::actions::unique
                             | ranges::actions::take (3);

Для решения на C++ сильно смущают два момента. Первый момент, так как ranges активно используют шаблоны, то понять что пошло не так очень сложно. И опыт с STL тут не сильно помогает, контейнеры и алгоритмы из STL не подразумевают построения цепочек из 5-7 вызовов в рамках одного выражения. А ranges подразумевают, и если что-то пошло не так, то разбираться в диагностических сообщениях компилятора придется долго. Иногда на столько долго, что проще бросить и написать в императивном стиле.

Вторая проблема это наличие views и actions, где views как бы ленивые и работают с итераторами, а actions работают только на месте с контейнером. Звучит логично, но предположить когда надо использовать ranges::actions::unique, а когда ranges::views::unique довольно сложно, при этом компилятор в деле помощи по работе с ranges скорее вредитель, нежели помощник. Так же типичные для ленивых языков подходы к работе с данными принимают неожиданный оборот. Например для Clojure не важно будет ли на вход distinct подана отсортированная последовательность или нет, на выходе ты получишь ленивую уникальную последовательность значений.

(distinct [2 1 2 5 4 4 4])
=> (2 1 5 4)

(-> [2 1 2 5 4 4 4]
    distinct
    sort)
=> (1 2 4 5)

Для ranges это не так, и несмотря на то, что имеются как ranges::actions::unique, так и ranges::views::unique, для решения аналогичной задачи подойдет исключительно последовательность из ranges::actions::unique предваряемый ranges::actions::sort и никак иначе. В итоге я кое-что с использованием ranges написал, но при том каких это требует временных затрат, использовать range в продуктовой разработке я вряд ли буду.

Если говорить о применимости C++ для таких задач как самообразование, то я бы сказал что есть куда более подходящие варианты, тот же Python. Язык в стандартной библиотеке которого нет функций уровня преобразования из/в base64 слабо подходит для изучения какой-либо предметной области или прототипирования. Если вам вдруг стало интересно как предлагается решать проблему base64-кодирования при помощи де-факто стандартного расширения C++ в виде BOOST, можете сходить по линке и прослезиться.

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

2 Comments Возвращение к C++

    1. Alexander Stavonin

      Я стараюсь что бы хобби хотя бы теоретически можно было монетизировать. Как можно монетизировать D я даже не представляю, по мне так он мёртв с коммерческой точки зрение. Даже потенциально.

      Reply

Leave a Reply