C++ идет в облака

Попасть на CppCon в этом году по ряду причин не вышло, поэтому я смотрю видео на YouTube. В процессе просмотра списка докладов глаз моментально зацепился за The Design of the C++ Runtime for AWS Lambda и я не разочаровался. Доклад в чем-то знаковый, так как он показывает применимость языка в области о которой C++ разработчики не думают и, обычно, ничего не знают.

Ключевые моменты из доклада:

  • AWS SDK for C++ позволяет оперировать всеми сервисами AWS, SDK доступен на GitHub. Там же можно найти огромное количество примеров использования.
  • SDK использует libCURL и разработчик должен сам разобраться с инициализацией хранилища сертификатов, расположение которого, конечно же, может зависеть от версии Linux.
  • Много воевали с libC, который довольно объемный с точки зрения набора библиотек и дистрибутиво-зависимый. Остановились на решении паковать все файлы проекта (включая libC) в один архив и запускать через ld-linux.so.
  • Если хочется уменьшить количество файлов привносимых libC, хотя целесообразность этого действия под вопросом так как еще есть libC++/libStdC++, то можно воспользоваться musl.
  • AWS гарантирует минимальный уровень процессора SkyLake, так что если вам нужен SIMD, то вполне можно включать процессоро-специфические оптимизации.

При том, что один из основных сценариев использования лямбды это – по-быстрому что-то посчитать, причем “по-быстрому” тут ключевое ведь оплата за время, то потенциал использования C++ (еще и с включенными SIMD-оптимизациями) на этом поприще выглядит очень заманчивым.

Rust и не звездные команды

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

Continue reading

Clojure и AWS Lambda

Последнее время я неспешно работаю над своим новостным C++ ботом и в качестве заключительного шага его нужно где-то “поселить” жить. Вариантов, конечно же довольно много, начиная своим компьютером заканчивая облаками, но наиболее экономически целесообразным и простым в поддержке выглядит какое-либо бессерверное решение, например AWS Lambda. Так как Clojure приложение как Lambda я еще никогда не запускал, то решил вынести эксперименты отдельно от основного проекта. Оказалось не сложнее чем с Go, всего-то нужно взять подходящую библиотеку для простой и наглядной реализации точки входа, сделать правильную сборку, да не забыть про Terraform для удобного управления инфраструктурой.
Continue reading

Haskell – это прекрасно, но…

Правда, я не понимаю как я мог заниматься разработкой в течении 18 лет и не удосужиться прочитать хотя бы одной книги по Haskell и даже не написать простенького “Hello world!” на этом изумительном языке. Наверное, меня всё время так или иначе отпугивали разные слухи и истории на тему того, какой это дико сложный и непойми-нахрена-нужный язык. Не иначе!

К счастью у меня, с одной стороны, нашлось довольно большое количество свободного времени для изучения чего-то совершенно не нужного в повседневной работе, а с другой стороны, мне посоветовали несколько книг по Haskell в рамках внезапно разросшейся дискуссии об этом языке на Facebook и пусть ни одна из них мне не подошла, но начало было положено. Проблема с книгами собственно в том, что имея за плечами изрядный опыт программирования, большинство из них выглядят невероятно скучными. Ну зачем долго и в мельчайших деталях обсуждать что такое let или сопоставление с образцом? Ясно же, что Haskell не будет для кого-то первым языком и у читателя уже есть что-то за плечами для того, что бы провести аналогии и понять о чем идет речь с полуслова. К моей радости я наткнулся на What I wish I Knew, которая отвечает на изрядное количество прикладных вопросов относительно языка и наиболее распространенных паттернов использования и мне где-то я разыскал рекомендацию Get Programming with Haskell в которой кратко и по делу рассматривают основные концепции языка. Сначала мне эта книга сильно не понравилась, но оказалось что нужно просто пропустить/пролистать первые две главы и всё будет просто великолепно. Continue reading

Основная головная боль в мире C++

В процессе обсуждения увлекательного вопроса “за что не любят современный C++” всплыл интересный список последствий UB оптимизаций. В отличие от довольно простого управления памятью которое мы получили начиная с C++14, UB – это действительно ужас-ужас, который фактически не реально держать в голове.

Великолепный пример с переполнением при умножении i на миллиард, который позволяет компилятору сильно “упростить” цикл:
Continue reading

Два года с Go

Сейчас подвожу итоги где-то 2-х лет разработки серии проектов на Go и довольно приятно удивляюсь тому, насколько хорошую защиту от дурака имеет Go по-умолчанию. Выстрелить себе в ногу просто невероятно сложно и в общем случае разработчик может просто сконцентрироваться на задаче не думая про UB, особенности move semantic и кучу других вещей, которые постоянно должны быть в голове у C++ разработчика.

Если отбросить очень сильное упрощение языка и попытаться выделить какие же именно решения позволили сильно упростить работу, то я бы отметил два: 1) отсутствие классов и наследования, 2) запрет на циклические импорты.

Continue reading

С++ и надежный, безопасный код

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

Единый стиль форматирования

Казалось бы, единый стиль форматирования проекта – это мелочь, но ведь не просто так почти каждая уважающая себя компания имеет увесистый документ “BestCompanyInTheWorld Coding Standard”, который как минимум на половину состоит из того, как код должен быть отформатирован. При этом, код в BestCompanyInTheWorld почти наверняка отформатирован вразнобой, так как применение стандарта контролируется в лучшем случае в процессе ревью, но кто же помнит все эти хитросплетения пробелов и скобок? Работая с Go я впервые оценил прелесть того, что весь код включая сторонние библиотеки действительно написан в едином стиле, так как существует единое правило форматирования для всего языка. Эта казалось бы мелочь очень сильно упрощает работу над чужой кодовой базой и в случае с Go легко достигается автоматически при помощи доступных по умолчанию форматтеров.
Continue reading

Rust и Я

У меня, как наверное и у многих других C++ разработчиков какие-то сложные отношения с Rust. Этот язык вроде как и очень сильно нравится и в то же время всё сложно. На сей раз моя попытка заняться Rust в серьез зашла куда дальше чем обычно (обычно я после нескольких небольших тестиков удовлетворенно забрасывал изучение) – я нашел компанию которой нужны Rust разработчики, связался с ними и взял тестовое задание. Тестовое задание просто восхитительное, не сложное, но помогает понять главное – хочешь ли ты связываться с языком или нет?

На тестовое задание я честно потратил часов 8 и за это время успел заглянуть в tokio, которая, как я понимаю, является образцово-показательной библиотекой для написания асинхронных сетевых приложений. Честно говоря, я до сих пор в состоянии смятении, но точно знаю одно – видеть такое и не дай бог поддерживать на постоянной основе я точно не хочу. К примеру, вот кусок кода, который читает данные из канала и шлет их по TCP:

Box::new(tcp.map(move |stream| {
    let (sink, stream) = stream.framed(Bytes).split();
    pool.execute(stdin.forward(sink).then(|result| {
        if let Err(e) = result {
            panic!("failed to write to socket: {}", e)
        }
        Ok(())
    })).unwrap();
    stream
}).flatten_stream())

Несколькими месяцами раньше я баловался с биндингами к libClang, где было всё читабельно и очевидно, что наводит на мысли, что написать на Rust можно писать как в адекватном и легко поддерживаемом стиле, так и городить write-only код в духе приведенного выше. И надо отметить что в примере выше чувствуется и стиль и задумка. Если в него какое-то время повтыкать (желательно в IDE с навигацией, очень рекомендую IntelliJ Rust), то становится понятно что хотел сказать автор и почему. Только вот “НУ ЗАЧЕМ?!?!” не покидает.

В итоге мне в очередной раз стало казаться, что за пределами небольших сообществ гиков Rust взлететь не может, так как поддерживать столь неочевидные решения должно быть даже дороже чем решения на C++ с ручным управлением памятью (в конце концов, санитайзерами можно отловить практически всё). Не знаю чем это вызвано, скорей всего тем, что Rust развивает комьюнити гиков, а C++ комитет старперов. В результате в Rust тянут все, что круто выглядит, а в C++ только то, что “мегамозги” одобрили. Побочным эффектом этого выступает то, что в Rust многие удобные вещи доступны сразу или почти сразу, а в C++ это “наверное будет реализовано после C++20” (да-да, это я про Meta).

Наверное, меня всё равно не отпустит и баловаться с Rust я не перестану. Но вот подаваться на позицию “Rust разработчик” я довольно долгое время точно не буду, сообщество должно наиграться в “zero cost abstractions” для начала

IDE и C++

Я довольно давний поклонник IDE от Jetbrains. Тем же IntelliJ IDEA и PyCharm просто нет сопоставимых альтернатив с точки зрения скорости работы и удобства. Собственно говоря у меня у меня даже подписка на них оформлена из соображений “нравится проект – поддержи финансово”, но вот с CLion что-то пошло не так…

На мой взгляд основная проблема в том, что разработчики и менеджеры продукта пришедшие с платформ .NET и JVM просто не понимают что такое разработка на C/C++. В итоге идёт попытка переложить модель разработки с той же Java на C++. В крайне случае у меня сложилось именно такое ощущение как после личного общения с JetBrain-овцами на CppCon, так и после вчерашнего диалога в Twitter.

Собственно, проблема и непонимание состоит в том, что команда CLion упорно хочет видеть “проектную модель”. Я не знаю чем это вызвано, толи наличием libClang под капотом, толи архитектурными проблемами собственного парсера, но без реальной проектной модели CLion превращается в тыкву. Хотя, он даже и с реальной проектной моделью иногда превращается в тыкву, например спотыкаясь на сгенерированном через Protobuf коде.

К сожалению, такой подход не может работать в мире C++ на многих проектах сложнее Hello World. За исключением счастливчиков, которым доступно писать всё в Visual Studio, у разработчиков огромный зоопарк используемых редакторов и причина у этого довольно грустная, в теории CLion и должен был исправить эту ситуацию. Так вот, мои коллеги пишут в Sublime, Emacs, Vim, Eclipce CDT, Studio Code и многих других редакторах по большому счету потому, что все они практически одинаково плохи в роли C++ IDE. Ну может за исключением Emacs/Vim, за счёт его интеграции с Globals/CTags и Eclipse, за счёт наличия грамотно реализованного парсера C++. Но в Emacs/Vim ещё нужно уметь писать – это свой отдельный мир, а Eclipse даже на Xeon-ах с горой памяти на борту умудряется тормозить.

Если же говорить о проектной модели и почему привязка к ней делает редактор не пригодным к промышленному использованию – то её часто просто нет. К примеру на данный момент я периодически переключаюсь между несколькими проектами: один на базе CMake, второй на некой дикой смеси Make, CMake и SCons обвешанной sh/bat скриптами сверху. Так же я иногда заглядываю в проекты которые представляют из себя кучу файлов просто “для консультации”. Таким образом, в теории, я мог бы писать 1 проект из всех над которыми работаю в CLion, но есть ли в этом хоть какой-то смысл если мне нужно писать и другие проекты и иметь пусть приблизительную, но быструю навигацию по коду и хоть какую-то автоподстановку? По мне так никакого, так как привыкать к особенностям 2-х редакторов куда как менее удобно нежели всегда работать в одном. Жаль только надежда на то, что на менеджеров продукта CLion сойдет понимание проблемы угасла.

Go и контроль качества проекта

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

Основная проблема в командной разработке – разнородность уровня и, как следствие, разное качество производимого командой кода. Ревью, безусловно, позволяют в той или иной степени сгладить последствия разнородности и даже подтянуть уровень разработчиков, но работают они не так хорошо как хотелось бы. Кому-то может быть лень, кто-то устал, кто-то не заметил и в репозитории оказывается нечто, которому там не место. Можно ли это хоть как-то исправить в C++? Нет, нельзя. Можно ли эту проблему минимизировать в Go? Довольно легко, что я вчера и сделал.

В Git есть замечательный механизм хуков, которые отрабатывают в зависимости от внутренних событий. Меня интересовал pre-commit хук, позволяющий заблокировать коммит по результату выполнения скрипта. Использование этого хука купе с Go Meta Linter, агрегатором линтеров для Go, позволяет автоматически заблокировать коммит до исправления ошибок.

#!/bin/sh

if ! [ -x "$(command -v gometalinter)" ]; then
echo 'Error: gometalinter is not installed. Please install it first and execute `gometalinter -i`'
exit 1
fi

lint_errors=$(gometalinter ./... --vendor -j 5)
if [[ $? != 0 ]]; then
echo 'Error: gometalinter checks had failed. Please execute `gometalinter ./... --vendor` first and fix ALL issues'
exit 1
fi

Дело остается за малым – поместить файл с хуком в директорию .git/hooks проекта, что я сделал при помощи нашего сборочного скрипта. Да, нам пришлось написать такой скрипт поверх стандартных Go команд типа go build так как есть пре- и пост- шаги которые необходимо предпринять в процессе сборки, а никакого CMake-подобного механизма в Go нет. Хотя, что уж тут, поверх CMake писать аналогичный скрипт тоже приходится.