суббота, 26 сентября 2020 г.

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

Так уж получается, что в последний месяц работать получается немного, зато много думается о разном и разнообразом. В том числе о том, остается ли для C++ место под Солнцем в современном мире. И, если остается, то какое и где (гусарам молчать!).

Данный пост появился под влиянием нескольких факторов.

Как скоро C++20 станет доминирующим стандартом?

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

Появление C++20 -- это знаковое событие. Сравнимое с появлением в свое время C++11.

Только вот я не очень понимаю, положительный ли или отрицательный знак у этого события. Ибо, с одной строны, в C++20 появились важные фичи, которые сильно продвинули язык (лично я сходу могу выделить концепты, сопрограммы и spaceship operator). Но, с другой стороны, в язык добавили модули, к которым сам я отношусь негативно, т.к., на мой взгляд, модули разрушают существующую экосистему C++ (в частности на помоечку отправятся разные доморощенные build tool-ы) и очень резко (можно даже сказать бесповоротно) делят код на старый и новый.

В связи добавлением в C++ модулей я не очень представляю себе, как быстро произойдет массовый переход на C++20.

Если вспоминать долгий и мучительный переход с C++98/03 на C++11, который, кстати говоря, местами еще так и не завершился, то можно предположить, что потребуется лет 7-8 для того, чтобы C++20 стал доминировать среди прочих C++ных стандартов.

С другой стороны, ситуация с C++11 изначально была хуже, т.к. нормальную и полноценную поддержку C++11 в основных копиляторах пришлось ждать несколько лет. Кроме того, 10 лет назад народ был как-то посдержаннее, переход на C++11 происходил неспешно и осторожно. Тогда как на C++14 и, тем более, на C++17 разработчики стали переходить гораздо активнее, чем в свое время на C++11.

Кроме того, времена так же изменились. ИТ всегда развивался динамично. И за последние 10 лет скорость повления изменений только увеличилась. В частности, появились и развились такие конкуренты C++, как Go и Rust. Даже давние соперники, т.к. C# и Java, эволюционируют ой как резво.

Так что в 2020-х годах уже нет роскоши ждать 3-4 года, пока кто-то другой набъет шишек с новым стандартом C++, а компиляторы избавятся от детских ошибок, чтобы затем потратить еще несколько лет и перевести свою разработку на C++11 (или C++14). Сейчас если что-то может дать хоть какие-то бенефиты для разработки, то это нужно использовать "не отходя от кассы".

Поэтому 7-8 лет, скорее всего, являются пессиместической оценкой. А оптимистической может быть оценка в 2-4 года.

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

Ну а разработчикам C++ных библиотек добавится геморроя. Т.к. если хочешь широкого использования своего инструмента, то придерживайся старых стандартов. А если хочешь повышения своей производительности и снижения собственных издержек, то переходи на C++20 и пользуйся концептами вместо SFINAE на шаблонах или spaceship operator вместо ручного расписывания операторов сравнения...

Страшно далеки они от народа?

Вторым фактором к написанию сего поста стала вот эта статья на Medium о разработке HTTP-тунеля на Rust: Writing a Modern HTTP(S) Tunnel in Rust.

Меня там больше всего поразили вовсе не возможности, которые доступны в Tokio из коробки. А обыденные вещи, которые становятся доступны разработчику на Rust за счет системы макросов Rust-а. В частности, вот как выглядит описание аргументов командной строки для http-tunel:

let matches = clap_app!(myapp =>
    (name: "Simple HTTP(S) Tunnel")
    (version: "0.1.0")
    (author: "Eugene Retunsky")
    (about: "A simple HTTP(S) tunnel")
    (@arg CONFIG: --config +required +takes_value "Configuration file")
    (@arg BIND: --bind +required +takes_value "Bind address, e.g. 0.0.0.0:8443")
    (@subcommand http =>
        (about: "Run the tunnel in HTTP mode")
        (version: "0.1.0")
    )
    (@subcommand https =>
        (about: "Run the tunnel in HTTPS mode")
        (version: "0.1.0")
        (@arg PKCS12: --pk +required +takes_value "pkcs12 filename")
        (@arg PASSWORD: --password +required  +takes_value "Password for the pkcs12 file")
    )
)
.get_matches();

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

Другой пример из этой же статьи -- это работа с конфигурационным файлом посредством библиотеки serde:

#[derive(Deserialize, Clone)]
pub struct TargetConnectionConfig {
    #[serde(with = "humantime_serde")]
    pub dns_cache_ttl: Duration,
    #[serde(with = "serde_regex")]
    pub allowed_targets: Regex,
    #[serde(with = "humantime_serde")]
    pub connect_timeout: Duration,
    pub relay_policy: RelayPolicy,
}

#[derive(Builder, Deserialize, Clone)]
pub struct RelayPolicy {
    #[serde(with = "humantime_serde")]
    pub idle_timeout: Duration,
    /// Min bytes-per-minute (bpm)
    pub min_rate_bpm: u64,
    // Max bytes-per-second (bps)
    pub max_rate_bps: u64,
}

И этого описания достаточно для того, чтобы работать с YAML-овыми конфигами вида:

target_connection:
  dns_cache_ttl: 60s
  allowed_targets: "(?i)(wikipedia|rust-lang)\\.org:443$"
  connect_timeout: 10s
  relay_policy:
    idle_timeout: 10s
    min_rate_bpm: 1000
    max_rate_bps: 10000

Конечно же, за конфиги в формате YAML или JSON (не говоря уже про XML) нужно отрывать руки, но суть же в том, что вместо YAML/JSON может быть и какой-то нормальный формат, а декларации в Rust-овом коде останутся в принципе такими же.

И что лично мне говорят эти примеры?

Они мне говорят о том, что не смотря на внесение принципиальных изменений в C++20 (будь то концепты, короутины, модули или spaceship operators), эти изменения никак не упрощают решение поседневных задач для рядового разработчика. Которому нужно аргументы командной строки распарсить, конфиг прочитать, запрос к БД сделать, по HTTP куда-то сходить. И т.д., и т.п.

Причем новый C++20 не то, чтобы очень уж сильно и принципиально упрощает разработку библиотек/фреймворков для решения этих повседневных задач.

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

С++ становится инструментом для Enterprise?

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

Для себя я это объясняю двумя основными причинами:

  1. Высокий порог входа в C++. Например, когда у меня за плечами 28 лет работы с C++, я могу спокойно использовать C++ даже для того, чтобы быстро набросать какую-то утилитку для разового использования. Не говоря уже про то, чтобы с командой в 3-5 человек с нуля поднять проект в 250-300KLOC, который затем будет работать годами. Но, подозреваю, что старперов, вроде меня, с десятками лет плюсового опыта за плечами, не так уж и много. Молодежи и проще, и интереснее применять что-нибудь современное и модное, а не бодаться с C++, к которому сейчас отношение такое же, как во времена моей молодости к Fortran и COBOL.
  2. Своеобразная экосистема и инфраструктура. Вроде бы всего навалом, но нужно потратить кучу времени и сил для того, чтобы из этого многообразия отобрать то, что тебе нужно, и заставить все это собираться в твоем проекте. Опять же, если за плечами большой опыт, то подобрать сторонние библиотеки для новой разработки не так уж и сложно. Просто уже знаешь где, что и почем. А вот если у тебя этого опыта нет?

Добавлю, что такое состояние экосистемы и инфраструктуры C++ не является сколько-нибудь серьезной проблемой для больших проектов с длинной историей развития, в которых своих велосипедов используется едва ли не больше, чем сторонних библиотек.

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

Ну и, соответственно, все более актуальным становится вопрос о том, а имеет ли смысл продолжать вкладываться в C++, если на этом рынке для мелких игроков все меньше и меньше места?

Вместо заключения

C++ активно развивается и это хорошо. Никуда C++ в ближайшее время не исчезнет.

Но вот есть ли смысл вкладываться в C++ в современных условиях, если ты не Yandex, Intel, Facebook или Google?

А вот хз. Сейчас я в положительном ответе на этот вопрос уже далеко не уверен.

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

Ну а дальше будем посмотреть. Говорят, что за пределами C++ есть жизнь. И даже проекты, отличающие от формошлепства ;)

2 комментария:

XX комментирует...

Было бы круто вам поближе познакомиться с Rust, обкатать его на паре-тройке небольших проектов.

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

Однако есть и риски: в пессимистичном сценарии Rust все еще может не взлететь и так и остаться маргинальным языком на долгие годы.

Но лично я сделал выбор в пользу Rust :)

XX комментирует...

Да clap вообще крутая либа (как и serde), очень мощная по функционалу и довольно легкая в использовании. Решает проблему консольных интерфейсов для утилит раз и навсегда.

Кстати, помимо макроса, в ней можно также задавать параметры в функциональном стиле и декларативном, подобно тому, как делается в serde, с помощью Derive-макроса: https://github.com/clap-rs/clap#using-derive-macros

Вот эти всевозможные Derive - это еще одна сильная сторона Раста. Супер удобно: прописал требуемый режим в атрибуте #[derive(...)] и не надо писать никакой реализации вручную.