Радикальная перемена в подходе к обновлениям и дополнениям в Стандарте C++ случилась на недавней встрече WG21, — или, скорее, это было изменение, которое «висело в воздухе» вот уже в течении нескольких последних встреч, и теперь наконец было обсуждено комитетом и задокументировано. Внимание читателей должны привлечь два ключевых пункта в самом начале документа «С++: планы на стабильность, скорость и реализацию языка» (C++ Stability, Velocity, and Deployment Plans [R2])":

  • Является C++ языком, в котором есть новые потрясающие возможности?
  • Известен ли C++ как язык, славящийся своей отличной стабильностью в течение долгого периода времени?

За ними следует следующее предложение (которое было согласовано на собрании): «Комитет должен быть готов рассмотреть дизайн/качество предложений даже в том случае, если эти предложения могут стать причиной изменения поведения языка или ошибки компиляции уже существующего кода».

Позади нас — 30 лет совместимости C++/C (ну хорошо, в последние 15 лет были по мелочи небольшие случаи, когда мы упирались в края и «заигрывали» с нею). Это замечательное достижение, за которое мы в течение вот уже более 30 лет благодарим Бьярна Страуструпа и 64 встречи, проведенные комитетом по стандартизации (Том Плум и Билл Плагер занимали их место в этом нелегком деле в промежуток между WG14 и WG21).

Надмножество C/C++ имеет давнюю историю.

В конце 1980-ых SC22 (комитет ISO высшего уровня по языкам программирования) задал WG14 (комитету по C) вопрос о том, должен ли быть создан стандарт для C++ — и, если это так, то хотят ли WG14 заняться его созданием. WG14 рассмотрели этот вопрос на собрании в апреле 1989 года и приняли решение о том, что по их мнению создание стандарта C++ стоит внимания, но комитет по C — это не те люди, кому стоило бы этим заняться.

В 1990 году, SC22 учредило учебную группу, которая должна была выяснить, имеет ли смысл создавать рабочую группу по C++, и U.S. X3 (комитет ANSI, отвечающий за системы обработки информации) учредил X3J16. Встреча-противостояние тех, кому предстояло в дальнейшем стать WG21, была проведена в Лондоне в марте 1992 года (и это была единственная встреча ISO C++, на которой я присутствовал).

Участники X3J16 приехали в Лондон для встречи ISO, и время от времени дебаты становились по-настоящему жаркими. Двумя основными публичными мнениями были идеи о том, что:

  1. нужно начинать работу над стандартом C++;
  2. C++ еще недостаточно созрел для того, чтобы можно было работать над стандартом.

Причиной, по которой многим хотелось начать работу над стандартом, и о которой не принято было упоминать публично, было стремление остановить, или хотя бы замедлить, появление изменений в языке. Новые релизы Cfront, наряду со слухами о них, были достаточно частыми (настолько, насколько часто это можно было делать по меркам эпохи до Интернета). Разработчики из крупных компаний рвали себе волосы на голове, когда шесть месяцев спустя после старта работы над каким-либо большим приложением версия С++ заменялась новой, слегка измененной.

Может показаться, что производители компиляторов должны были бы радоваться тому, что язык регулярно менялся — ведь изменения давали стимул разработчикам платить за покупку обновлений компилятора. На практике, изменения были настолько значительными, что для этой работы требовались те, кто действительно знал то, что они делают — т.е. требовалось платить большую зарплату сильным программистам; производителям компиляторов же было куда привычнее тратиться на маркетинг мелких обновлений. Утверждалось, что реализация компилятора С++ требовала в семь раз больше затрат, чем реализация компилятора для С. Понятия не имею, насколько это утверждение было справедливо на практике (возможно, это лишь оценка одного из производителей, основанная на его примерном опыте). В 1980-ых у каждого человека и у его собаки был свой собственный компилятор C, но большая часть тех из них, кто пытался реализовать компилятор С++, уперлись в кирпичную стену.

Наконец, последовало голосование: стоит ли остановить/замедлить скорость принятия изменений в С++ — против того, чтобы позволить C++ «исполнить свое предназначение» (так выразился представитель AT&T в своем призыве к аудитории, вследствие чего вся комната заапплодировала). По его итогам, исследовательская группа превратилась в WG (увы, не могу поделиться с вами точными цифрами — никаких данных онлайн об этом событии нет, и я не могу найти бумажной копии — мы пользовались такими до середины/конца 90-ых).

Создание WG21 не имело того эффекта, которого от нее ожидали (замедления внесения изменений в язык), поскольку Страуструп присоединился к комитету, и эволюция С++ продолжалась в быстром темпе. Однако, с точки зрения простых разработчиков, изменения в языке стали происходить медленнее; Cfront перестал обновляться так быстро, поскольку его код стал коллапсировать под грузом предшествующей эволюции — и на свет стали появляться адекватные компиляторы С++, которые можно было использовать на практике (в те ранние годы мощным стимулом роста популярности языка стал компилятор Zortech C++).

Последняя встреча WG21 включала 140 человек в списке посетителей; не все из них были заскучавшими от жизни консультантами, которые ищут творческую отдушину (я про «новые потрясающие возможности») — но я уверен в том, что многие были бы рады избавиться от кандалов, связывающих их по рукам и ногам (более известным как совместимость с C).

Мне кажется, нас ожидает куча предложений, которые поломают совместимость с C тем или иным способом, и некоторые из них попадут в опубликованный стандарт. Это будет подкреплено аргументом о том, что данные изменения сделают жизнь проще будущим разработчикам на C++ (подобное заявление делают сторонники каждого языка, несмотря на то, что эмпирические доказательства этого отсутствуют). Единственным способом выяснить, принесет ли изменение долгосрочную пользу, является долгое ожидание и наблюдение за тем, что случится в итоге.

Интересным вопросом здесь является то, как отреагируют производители компиляторов на серьезные изменения в стандарте языка, «ломающие» его совместимость. В настоящее время активно используемых компиляторов существует не так много, т.е. конкуренция не так велика. Что станет стимулом для производителя компилятора для выпуска его новой версии, которая почти наверняка «сломает» написанный до этого код? Ведь валидация копиляторов относительно стандарта отошла в прошлое.

Если WG21 внесет слишком много серьезных «ломающих» изменений, то вполне возможно, что производители компиляторов для C++ решат их проигнорировать — а разработчики задумаются о том, не вышел ли у комитета ISO по стандартизации C++ срок годности.

Обсуждение на Reddit.

Комментарии (22)


  1. beduin01
    20.04.2018 17:12
    -5

    Давно уже пора выкинуть С++ на свалку истории. Хватит уже этого франкинштейна качать.


    1. Jamdaze
      21.04.2018 10:21
      -1

      На плюсах можно вполне читаемо и адеквато писать. Другое дело что этому нигде не учат.


  1. koldoon
    20.04.2018 18:48

    {sarcasm}*Комментарий про Rust*{/sarcasm}
    А если серьезно, то отличные новости! Может наконец в с++ появится какой-то юзабельный стандартный reflection и семантика для пропертей. Может я не в курсе, но кто сегодня использует c++ там, где нужно писать на C? ИМХО, эта совместимость уже давно себя изжила.


    1. DaylightIsBurning
      20.04.2018 20:33

      Тоже не хватает этих возможностей в C++. На текущий момент неплохо получается реализовать properties с помощью visit_struct. Сделал runtime обертку для себя, так что при необходимости runtime reflection тоже работает. Плюс автор обещал подумать над совместимостью с cereal. В результате получается неплохая комбинация: static reflection + runtime reflection + serialization. И для всего этого достаточно одного макроса:

      struct my_type {
        int a;
        float b;
        std::string c;
      };
      
      VISITABLE_STRUCT(my_type, a, b, c);

      либо
      struct my_type {
        BEGIN_VISITABLES(my_type);
        VISITABLE(int, a);
        VISITABLE(float, b);
        VISITABLE(std::string, c);
        END_VISITABLES;
      };

      И затем:
      visit_struct::for_each(my_struct,
          [](const char * name, const auto & value) {
            std::cout << name << ": " << value << std::endl;
          });
      std::cout <<visit_struct::get<3>(s);
      auto variant=visit_struct::get(3,s);
      

      и т.д.


      1. thatsme
        21.04.2018 07:52

        Вы вполне могли-бы перенести этот функционал в рантайм (используя std::tuple к примеру), вместо этапа компиляции. Или у вас есть пример отличный от:

        std::cout <<visit_struct::get<3>(s);
        auto variant=visit_struct::get(3,s);
        который требуется на этапе компиляции?

        В чём смысл запутывания кода с помощью visit_struct, если используется это в рантайме?


        1. DaylightIsBurning
          21.04.2018 11:29

          Я не совсем понимаю, что Вы имеете ввиду, под «можно перенести это в рантайм испульзуя tuple»? Смысл в том, что с помощью одной и той же «аннотации» можно получить одновременно и compile time и runtime reflection. Затем, где можно, используется compile-time рефлексия, а где нельзя — runtime через std::variant. Runtime вариант — это аналог Qt Property. Мне кажется, что если при прочих равных compile time reflection лучше runtime т.к. работают проверки компилятора (меньше ошибок) и эффективней (лучше производительность).

          struct S {
              int n;
              float f;
              std::string filler;
              char c;
              float ff;
          };
          VISITABLE_STRUCT(S, n, f, c,ff);
          
          
          int main()
          {
              S s{42,3.14f,"test",'Z',999.9};
              auto printer=[](auto&& v) {
                  std::cout<<v<<'\n';
              };
              visit_struct::to_variant<S>::type var;
              //static_assert(std::is_same<var_type,decltype(var)>::value, "" );
              var=visit_struct::get_variant(2,s);
              std::visit(printer,var);
          
              var.emplace<3>(2.718f);
              visit_struct::set_field(3,s,var);
              var=visit_struct::get_variant(3,s);
              std::visit(printer,var);
              
              for (int i=0; i<visit_struct::field_count<S>(); ++i)
                std::cout<<visit_struct::get_name<S>(i)<<' ';
              std::cout<<'\n';
              return 0;
          }
          
          Z
          2.718
          n f c ff


          1. thatsme
            21.04.2018 19:07

            Ну пример в принципе тот-же самый. Обращение к члену структуры по порядковому номеру, с автоприведение типов (std::tuple). В отличии от std::tuple, можно получить имя по индексу (наверное тут наибольший профит в удобстве?). Использование этого имени (например сравнение), — уже рантайм. Также как впрочем и с tuple. Обращение по индексу — генерация кода (как и в tuple). При этом использование структуры (обращение по имени), — compile time (заметьте без дополнительной генерации кода).

            Это не троллинг, я реально не могу вкурить приемущество использования приведённого вами метода.

            Для примера, простыми словами, где использование приведённого вами метода предоставляет преимущества:
            а) в лучшей производительности;
            б) в читабельности кода;
            в) в удобстве использования;
            г) в конкретном прикладном случае;


            1. DaylightIsBurning
              21.04.2018 19:26

              Мне кажется, все пункты справедливы, от а) до г). Проблема с tuple в том, что члены не именованы. В принципе, можно было бы создавать tuple of references, не удивлюсь если под капотом visit_struct так и делает. В моём случае есть возможность комбинировать именованный доступ и доступ по индексу с минимально возможным в принципе оверхедом. Это читаемо и удобно, позволяет избегать дублирования кода, нет необходимости платить за то, что не используется.
              Применение то же, что и для Qt Properties, BOOST_HANA_DEFINE_STRUCT, RTTR. Те применения, которые я использую — это разные формы сериализации по сути, как для сохранения на диск и передачи по сети, так и для GUI-редактирования объектов и их свойств. Сериализация может не требовать рантайм-рефлексии так как передавать всё равно весь объект разом. GUI-редактор требует рантайма т.к. опции изменяются в произвольном порядке, известном только в рантайме. Я нашел статью «Designing a C++ Property System» по этой теме, там и мотивация и проблемы рассматриваются.


    1. Antervis
      21.04.2018 08:27

      а разве существуют задачи, где при наличии возможности писать на с++ НУЖНО писать на си?


      1. koldoon
        21.04.2018 15:51

        Есть слепые фанатики, которые на любой вопрос говорят «Да ну на* этот ваш {любой язык программирования}, мы сейчас все быстро и круто на C напишем!».


      1. robofreak
        21.04.2018 16:02
        +1

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


        1. Antervis
          21.04.2018 18:51

          вы сначала говорите, что c++ непригоден для embedded’а, а сразу после утверждаете, что отказаться от совместимости с си плохо с точки зрения embedded. Определитесь уже.

          Кстати, отмечу, что поддержка interoperability может быть реализована не только на уровне языка (как и для всех других пар языков). Например — на тех же модулях с явным указанием версии языка — то, о чем я писал ниже


  1. DmitryLeonov
    20.04.2018 19:50

    Если новые плюшки будут настолько несовместимы со старым кодом, что его нельзя будет подтянуть опциями, прагмами или дефайнами, то это потянет за собой разделение гораздо худшее, чем между Python 2 и 3.


    1. mayorovp
      20.04.2018 20:56

      Если тот же Python можно запускать только на конкретной версии интерпретатора, то в С++ разные модули наверняка можно будет билдить с разными флагами компилятора, а потом линковать вместе.


      1. DmitryLeonov
        20.04.2018 22:58

        Это понятно, про это я и писал. Но вдруг допроектируются до таких изменений, которые, например, не дадут использовать классы из одних модулей в других.


  1. Antervis
    21.04.2018 09:58
    +1

    думаю, правильно, если вместо «версий стандарта» мы получим «версии языка». Пара breaking changes между c++11/14/17 все равно есть, так зачем тянуть устаревшие кодобазы в новый c++ если можно просто закрепить за ними версию языка?


    1. Gorthauer87
      21.04.2018 10:47

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


      1. Antervis
        21.04.2018 11:14

        это должно решаться модулями. В худшем случае, можно адаптировать существующий синтаксис: extern “c++17” {…


        1. mapron
          22.04.2018 00:21

          плохая идея, т.к. сейчас это конструкция — только совет для линкера.


          1. Antervis
            22.04.2018 22:48

            это не «совет», а явное указание контракта — «использовать сишный манглинг и соглашение о вызовах». По сути, extern «C» (с учетом того, как его используют) значит «вот этот вот код компилируй как будто он сишный»


  1. sena
    21.04.2018 14:54

    А такие простые вещи они будут когда-нибудь фиксить? Или уже пофиксили?


    1. mapron
      22.04.2018 00:25

      Когда-нибудь может будут, документы создавали же:
      www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0289r0.pdf
      просто их не педалят) Видимо не так много кому надо.