Почему в 2024 году нам приходится писать каст енума к строке вручную, для каждого кастомного типа нужна своя функция логирования, а биндинги к C++ библиотеке требуют кучу повторяющегося кода?

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

Сегодня рассматриваем пропозалы рефлексии, которые с большОй вероятностью попадут в следующий стандарт - C++26.

Что это вообще такое?

Рефлексия это возможность кода исследовать или даже менять свою структуру. Можно разделить на 2 вида - динамическая и статическая.

Динамическая рефлексия доступна в рантайме (во время выполнения программы). Например питон, где вся информация о типе (методы, данные) хранится в доступной коду структуре данных, благодаря чему можно, например, сериализовать любой объект без дополнительного кода, просто вызвав json.dumps(object). Это работает как раз потому, что у функции dumps есть возможность проитерироваться по всем полям данных любого переданного типа.

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

P2996

Основной пропозал, прописывает базу для статической рефлексии. Вводятся два новых оператора и новый хедер <meta> с набором полезных мета функций.

Изменения языка

  1. Новый оператор ^ - да, это переиспользование xor - производит reflection value (reflection/отражение) из типа, переменной, функции, неймспейса и тд. Отражение имеет тип std::meta::info и по сути является ручкой для доступа к внутреннему строению отраженного "объекта".

  2. Splicers - [:R:] - где вместо R вставляется ранее созданное отражение (std::meta::info). Переводит std::meta::info обратно в тип/переменную/функцию/etc.

Изменения библиотеки

  1. Новый тип std::meta::info - для представления отражения.

  2. Метафункции в <meta>, например: members_of - получить список членов какого-то класса, enumerators_of - список констант в переданном енуме, offset_of - отступ переданного субъобъекта (учитывает паддинг), is_noexcept - является ли переданная функция/лямбда noexcept и многое другое.

Использовать все это совсем не сложно, особенно если вы ранее работали с шаблонами.

Примеры (взяты из проползала)

Получение отражения и возврат к изначальному типу

// отражение
constexpr auto r = ^int;
// int x = 42;
typename[:r:] x = 42;
// char c = '*';
typename[:^char:] c = '*';

Обращение к члену класса по имени

class S { int i; int j; };

consteval auto member_named(std::string_view name) {
  for (std::meta::info field : nonstatic_data_members_of(^S)) {
    if (name_of(field) == name)
      return field;
  }
}

S s{0, 0};

// s.j = 42;
s.[:member_named("j"):] = 42;
// Ошибка: x не часть класса.
s.[:member_named("x"):] = 0;

Функция member_named принимает имя члена класса. С помощью std::meta::nonstatic_data_members_of запрашивается список имеющихся членов класса, для каждого элемента списка запрашивается его имя с помощью std::meta::name_of. Тот член у которого совпадет имя с переданным в функцию и будет использован.

Шаблонная функция каста енума к строке

template <typename E>
constexpr std::string enum_to_string(E value) {
  template for (constexpr auto e : std::meta::enumerators_of(^E)) {
    if (value == [:e:]) {
      return std::string(std::meta::name_of(e));
    }
  }
  return "<unnamed>";
}

enum Color { red, green, blue };

static_assert(enum_to_string(Color::red) == "red");

Функция принимает константу из произвольного енума, отражает его тип, с помощью std::meta::enumerators_of получает список констант этого енума и матчит его с переданной константой. Найденное отражение передается в std::meta::name_of, который возвращает имя константы из обьявления этого енума. Про работу template for чуть ниже.

А так же

В P2996 (см ссылку вначале поста) есть куча других примеров, советую хотя бы пробежаться взглядом. Самый интересный, субъективно, это универсальный форматтер. С помощью него можно будет написать шаблонную функцию, которая сможет переводить любой класс в строку без дополнительного кода. Представьте сколько миллионов строчек кода в мире станут ненужными только за счет этого!

P1306

Expansion statements - представьте, что у вас есть некоторая коллекция объектов разных типов (например, tuple) и вы хотите по ней проитерироваться. Обычный range loop этого не умеет, потому что переменная, с помощью которой происходит итерация, может быть только одного типа. Есть ухищрения вроде std::apply и переводом коллекции в template pack, но это требует дополнительного кода и субъективно довольно костыльно.

Пропозал предлагает новый оператор - template for - он упоминается и в P2996, поскольку этот функционал значительно упрощает написание многих мета функции.

Базовый пример из пропозала

auto tup = std::make_tuple(0, ‘a’, 3.14);
template for (auto elem : tup)
  std::cout << elem << std::endl;

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

{
  auto elem = std::get<0>(tup);
  std::cout << elem << std::endl;
}
{
  auto elem = std::get<1>(tup);
  std::cout << elem << std::endl;
}
{
  auto elem = std::get<2>(tup);
  std::cout << elem << std::endl;
}

template for это не цикл в классическом его понимании, а способ продублировать блок кода для каждого элемента в коллекции, что позволяет элементам быть разного типа.

P3096

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

void func(int counter, float factor) {
  template for (constexpr auto e : parameters_of(^func))
    cout << name_of(e) << ": " << [:e:] << "\n";
}

В предлагаемую пропозалом мета функцию std::meta::parameters_of передается текущая функция. std::meta::parameters_of возвращает вектор с отражениями аргументов функции. std::meta::name_of извлекает имя аргумента из отражения, а [:e:] извлекает значение аргумента в текущем вызове функции. Кстати, этот функционал уже доступен на годболте.

P3096 довольно спорный пропозал - возможно именно поэтому он предлагается отдельно от P2996. Дело в том, что стандарт позволяет объявлять одну и ту же функцию сколько угодно раз, и с какими угодно именами аргументов - главное чтобы совпадали типы. Например:

// file1.h
void func(int value);
// file2.h
void func(int not_a_value);
// file3.cpp
constexpr auto names = meta::parameters_of(^func); // ? 

Вопрос, какое имя интового аргумента должна вернуть parameters_of: value или not_a_value? В пропозале представлена аргументация в пользу разных решений, но предлагается следующее: при вызове parameters_of компилятор будет проверять консистентность именования, и если есть несовпадения, то это ошибка компиляции. Таким образом существующий код не ломается, хотя и немного ограничивается область применения новой мета функции.

Новые идеи это круто, но пробовали ли это на практике?

Да! Уже есть две рабочие (но не полные) имплементации. В проде это использовать еще рано, но само их наличие показывает зрелость пропозала.

  1. В EDG - это коммерческий компилятор, поэтому посмотреть код не удастся, но он доступен на годболте

  2. В опенсорсном форке Clang - он так же доступен на годболте, если вам не хочется компилировать кланг самостоятельно : )

Что по принятию в стандарт

Ни один из пропозалов еще не принят комитетом, так что теоретически в C++26 мы можем их не увидеть. Однако наличие рабочих имплементаций и поддержка сообщества позволяют надеяться, что в следующий стандарт рефлексия попадет. В порядке убывания вероятности принятия: P2996, P1306, P3096. Будем следить за следующими собраниями стандартного комитета, ближайшее будет очень скоро - 24 июня в Сент-Луисе.

Заинтересовало?

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

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


  1. Mishootk
    26.06.2024 15:26
    +3

    По рефлексии энумераторов. Взял с хабра многолетней давности пример. Вся идея реализации рефлексии умещается в одном экране. Чуть доработал (убрал лямбды, у нас легаси, еще древнее стандарт), получил рефлексию перечислений. В моем проекте невозможно ждать нового стандарта.

    Вся реализация в хидере. Перевод перечисления на рефлексию - замена enum EnumName {item1, item2} на ENUM(EnumName, item1, item2). Список перечислений со всем синтаксисом присвоения констант сохраняется.

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


    1. feelamee
      26.06.2024 15:26
      +1

      а ссылку не оставили (
      прошу предоставить для озакомления .-.


      1. Mishootk
        26.06.2024 15:26

        Это идея для развития. Мне очень помогла.

        https://habr.com/ru/articles/276763/


        1. magrif
          26.06.2024 15:26

          https://github.com/Neargye/magic_enum интересная библиотека


  1. Yura_PST
    26.06.2024 15:26

    Первое впечатление от рефлексии Java после C++ - детям дали два фломастера и они вообще всё разрисовали.

    Интелектуальная нагрузка для написания геттеров и сеттеров нулевая. Разобраться как это делать с помощью рефлексии - минимум пол дня. И если в классе много геттеров и сетеров - проблема в декомпозиции.

    В общем, вместо реально важдных дел (декомпозиция, рефакторинг, документация) - рефлексия, автогенерация и енамы.


    1. Anarchist
      26.06.2024 15:26
      +2

      Рефлексия имеет ещё важное применение: сериализация, передача и удаленное выполнение функций. Этим живёт, например, спарк. Если рефлексия появится в низкоуровневых языках, jvm, наверное, можно постепенно начинать закапывать.


    1. dalerank
      26.06.2024 15:26

      Не-не-не, не надо такого Щастья, на одном из проектов использовался кастомный кланг с рефлексией (такой вот заскок был у архитекта системы, причем он сам половину и написал), люди такую дичь начинали делать с использованием этого механизма, то массив перебирают опираясь на наличей полей в объекте, а ты потом - "...ля куда делась половина выборки?" то начинают цеплять выхлоп рефлексии в метаданные к объекту. Не говоря уже что самая рефлексия в той реализации была очень и очень дорогая, на пустом месте просадка перфа на порядок была. В итоге это все умирало на ревью, и имело крайне узкое место применения, вроде проверок на парсинге ресурсов или уровня. Не относится к пропозалу, но изза хотелок архитекта, приходилось еще и держать двух инженеров на саппорте собственной версии компилятора и поддержке компиляции на обычных


  1. Lainhard
    26.06.2024 15:26
    +6

    Возможно лучшая новость по плюсам за последние N лет. Очень давно жду этого, в свое время даже трогал форкнутый кланг с рефлексией.

    Обычно злюсь, когда авторы дают ссылку на телегу, вне зависимости от ценности статьи, но если обещаете там побольше постить про рефлексию, буду рад подписаться.


    1. krestovii_podhod Автор
      26.06.2024 15:26

      Обещаю что будет пост про P3294 и апдейты по стандартизации рефлексии)


  1. DistortNeo
    26.06.2024 15:26
    +3

    Поизучал proposal. Не увидел, как это будет дружить с атрибутами - без них та же серализация в json как-то мало смысла имеет.

    P.S. Как-то прям ну очень некрасиво в плане синтаксиса. Хотя, чего ещё ожидать, когда язык перегружен функционалом.


    1. krestovii_podhod Автор
      26.06.2024 15:26
      +1

      ^ как по мне выглядит неплохо, особенно учитывая редкость использования xor, но [::] пока кажется чужеродным плюсовому коду. Возможно есть технические причины, почему нужен был синтаксис "оборачивающий" отражение вместо обычного унарного оператора (как, например, ^) - тут не знаю. Но если причина в этом, то дальнейшая логика понятна, потому что все скобочки то или иное значение в языке уже имеют


    1. krestovii_podhod Автор
      26.06.2024 15:26
      +2

      Речь об [[этих]] атрибутах? Есть отдельный пропозал P1887, но по статусу не знаю


  1. Biga
    26.06.2024 15:26
    +1

    Когда это появится в компиляторах, многие наконец выдохнут с облегчением. И со спокойным сердцем уйдут на пенсию. (ирония)


  1. ddruganov
    26.06.2024 15:26
    +7

    Жуть какая..


  1. NeoCode
    26.06.2024 15:26
    +9

    Splicers - [:R:]

    Баян! )))


  1. Porohovnik
    26.06.2024 15:26
    +3

    auto tup = std::make_tuple(0, ‘a’, 3.14);
    template for (auto elem : tup)
      std::cout << elem << std::endl;

    Ух, как это прекрасно, лаконично, и можно выкинуть столько костылей, жаль что на C++26 явно перейдут только лет через 6-8 везде, но будущие всё больше становится светлым.


  1. buldo
    26.06.2024 15:26
    +5

    О, поздравляю коллег плюсовиков.

    Если комитет этого не примет, то я окончательно поверю, что это просто сборище дедов, которые уничтожают язык и самоутверждаются на этом.


  1. kovserg
    26.06.2024 15:26

    нам приходится писать каст енума к строке вручную, для каждого кастомного типа нужна своя функция логирования, а биндинги к C++ библиотеке требуют кучу повторяющегося кода?

    Скорее всего просто язык для этого не подходящий? Не?

    у меня для вас хорошая новость - скоро эти проблемы будут решены

    Зато эти решения создадут в два раза больше новых проблем.

    Рефлексия это возможность кода исследовать или даже менять свою структуру

    А можно собрать все enum-ы каким-нибудь селектором и сформировать новый enum?

    Динамическая рефлексия доступна в рантайме

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


    1. krestovii_podhod Автор
      26.06.2024 15:26
      +1

      А можно собрать все enum-ы каким-нибудь селектором и сформировать новый enum?

      В основном пропозале возможности объявлять новый енум нету, насколько я знаю

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

      Рефлексия предлагается строго на этапе компиляции, поэтому нет


    1. yatanai
      26.06.2024 15:26
      +1

      Думать вредно! Но наличие статик рефлексии сильно упростит рефлексию динамическую. Мой фреймворк где можно делать удалённый вызов процедур, который сейчас работает с помощью отдельного кодогенератора, наконец-то можно будет упростить.


  1. 9241304
    26.06.2024 15:26
    +1

    Каст энума уже работает, без рефлексию. На всякий случай напоминаю)


    1. krestovii_podhod Автор
      26.06.2024 15:26
      +3

      Знаю про каст к инту, а к строке как?)


      1. 9241304
        26.06.2024 15:26
        +1

        конкретно энамы https://github.com/Neargye/magic_enum

        +структуры https://github.com/getml/reflect-cpp

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


        1. UranusExplorer
          26.06.2024 15:26

          структуры https://github.com/getml/reflect-cpp

          Boost.PFR (он header-only и на самом деле не требует остального boost) умеет почти то же самое (только без имён полей) для C++17.

          Так на случай если кто-то застрял на 17-ой версии стандарта и не может пока перейти на 20-ую.


          1. 9241304
            26.06.2024 15:26

            когда кто-то говорит, что буст умеет то же самое, я конечно очень рад (он вообще умеет многое, что скоро появится в стандарте, и в целом отличная либа), но уже одно его название отпугивает )


            1. UranusExplorer
              26.06.2024 15:26

              Ну я специально отметил, что PFR - маленький и header-only, от буста не зависит, и есть даже его версия без бустовского неймспейса.


        1. Anton_Menshov
          26.06.2024 15:26

          Magic enum штука хорошая, пока не упираешься в ее ограничения. Наиболее серьезными для меня были: 1) зависимость от компилятора (разрабатывал приложение которое необходимо было компилировать несколькими компиляторами, включая полуэкзотику (хоть и с С++17, Intel Compiler если интересно); 2) проблемы с enum в шаблонах. Часть уже поправлена, однако иногда требуются танцы с бубном, чтобы затарахтело.

          В итоге, на проекте от magic_enum было решено отказаться в пользу к полуавтоматическому (вставляем руками с помощью утилиты там где надо) касту к строке.


          1. 9241304
            26.06.2024 15:26

            Потому и ждём рефлексию. Но зачем же полуавтоматика, если элементарные декларации с помощью макросов тоже работают?


  1. KuHeT
    26.06.2024 15:26
    +3

    Около года назад впервые попробовал Java и всё это время писал бэкэнд чисто на ней - теперь смотреть больно на плюсы. Хотя раньше и сильно любил их

    Здорово, что может появиться рефлексия в ближайшее десятилетие (давайте будем реалистами), хоть и с таким пугающим синтаксисом


    1. 9241304
      26.06.2024 15:26

      больно потом переписывать бэкенд с явы на го. )))

      а плюсы даже с такой рефлексией будут норм


  1. firehacker
    26.06.2024 15:26

    Господи... Во что они превращают C++...


    1. 9241304
      26.06.2024 15:26
      +1

      вам по прежнему никто не запрещает писать в сишном стиле без использования стандартной библиотеки и даже классов )


    1. UranusExplorer
      26.06.2024 15:26

      C++ наконец-то потихоньку превращается в приличный язык программирования повернутый к программисту не задницей с кучей костылей, а хоть немного лицом, при этом продолжая придерживаться концепции don't pay for what you don't use. Отлично же.


  1. slonopotamus
    26.06.2024 15:26

    Ну ок, допустим в компайл-тайме мы теперь умеем из енума сделать строку. Хотя могли и до этого. А с рантаймом что делать?


    1. mayorovp
      26.06.2024 15:26
      +1

      А что мешает-то вызвать ту же enum_to_string в рантайме?


      1. slonopotamus
        26.06.2024 15:26

        У меня вот из этих предложений сложилось впечатление что в рантайме кина не будет:

        Динамическая рефлексия доступна в рантайме

        Плюс

        Статическая работает во время компиляции.

        Плюс

        Основной пропозал, прописывает базу для статической рефлексии.


        1. mayorovp
          26.06.2024 15:26
          +1

          Во время компиляции с помощью рефлексии генерируется функция enum_to_string. В рантайме она вызывается.


        1. krestovii_podhod Автор
          26.06.2024 15:26
          +1

          Динамической рефлексии (см фичи питона) действительно не будет, но для каста енума она не нужна. Сама рефлексия отработает во время компиляции, по сути сгенерировав функцию с кучей if-ов. В рантайме это будет обычной функцией


  1. crackedmind
    26.06.2024 15:26
    +1


  1. AndriyS
    26.06.2024 15:26
    +2

    В Delphi активно пользуюсь RTTI (рефлексией). Кроме сериализации еще одно применение в моем случае - построение юзер интерфейса в рантайме. Интерфейс для редактирования неких данных описываемых структурами/классами. В основном такие прогрммы нужны для тестировния, но без RTTI моя работа была бы адом :). Надеюсь все proposals будут приняты. Жду рефлексию в С++ уже давно.


    1. HemulGM
      26.06.2024 15:26

      Те же ORM или HTTP серверные решения, в Delphi, без RTTI тоже не обходятся


  1. ImagineTables
    26.06.2024 15:26

    Напомните, что хорошего прикладного можно запилить с помощью статической рефлексии?

    Для динамической, например, сходу можно вспомнить универсальный компонент Object Inspector.


    1. mayorovp
      26.06.2024 15:26
      +4

      Сериализатор. ORM. DI фреймворк. Биндинг к другому языку. Реализацию IDispatch.

      Ну или аналог динамической рефлексии, а потом тот же Object Inspector.


      1. ImagineTables
        26.06.2024 15:26

        Реализацию IDispatch.

        Боюсь, я несколько подзабыл такие материи, как интеграция с Visual Basic (для этого же нужны диспинтерфейсы?), поэтому поправьте, если что-то не так.

        Проблема, как я её помню, была в следующем. Руки чесались сделать макрос, в который бы можно было передать имя метода, и который бы:

        1. Добавлял нативную декларацию в кокласс (для дуальности);

        2. Подклеивал (##) к переданному имени кавычки и добавлял его в карту id-имя-адрес.

        И никаким каком сделать это было нельзя, потому что 1 и 2 относятся к разным скоупам (а именно: 1) декларация кокласса, 2) имплементация конструктора).

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

        Макросов, как известно, нам не завезли. А что даёт в этом вопросе статическая рефлексия? То, что строку с именем метода можно вытащить, и всё? Или что-то большее? Если делать в лоб универсальную библиотечную реализацию Invoke, там же будут всякие спецэффекты типа нежелательного exposing'а наружу AddRef/Release/QI.


        1. mayorovp
          26.06.2024 15:26

          Макросов, как известно, нам не завезли. А что даёт в этом вопросе статическая рефлексия? То, что строку с именем метода можно вытащить, и всё?

          Можно получить список методов (и их имена, разумеется).То есть никаких больше явных карт - карту соберёт компилятор.


          1. ImagineTables
            26.06.2024 15:26

            Вы про это? Или я чего-то не понимаю?

            Если делать в лоб универсальную библиотечную реализацию Invoke, там же будут всякие спецэффекты типа нежелательного exposing'а наружу AddRef/Release/QI.


            1. mayorovp
              26.06.2024 15:26

              Не вижу причин почему в универсальной реализации он более нежелателен чем при использовании тех карт id-имя-адрес


              1. ImagineTables
                26.06.2024 15:26

                Насколько я понимаю, такой exposing для дуальных диспинтерфейсов не «нежелателен», а недопустим, в соответствии со спецификацией COM. (Потому что в VB есть GC, который должен единолично управлять вызовами счётчика ссылок).

                Ну, это совсем неинтересно. Я думал, ответ будет из области фильтрации по атрибутам, а так…


                1. mayorovp
                  26.06.2024 15:26

                  Не помню ничего такого в спецификации COM.


                  1. ImagineTables
                    26.06.2024 15:26

                    Здесь есть кто-то ещё, кто помнит COM? Объясните, пожалуйста, что вы думаете о передаче метода для ручного управления памятью (Release) в язык с автоматическим управлением памятью (Visual Basic).

                    А то я видел и такое:

                    while (pUnk->Release());
                    

                    и автор тоже утверждал, что это не нарушает комовских спецификаций.


                    1. mayorovp
                      26.06.2024 15:26

                      Погодите, а причём тут этот цикл и что вообще есть "передача метода"?

                      Вы всё это время пытались меня убедить в том, что методы IUnknown не должны вызываться через IDispatch::Invoke? Ну да, это очевидно. А в чём проблема исключить эти методы? Их имена так-то известны, можно хоть сравнением имени с константой убрать их из списка...


                      1. ImagineTables
                        26.06.2024 15:26

                        Рад, что мы, наконец, перешли к разговору по существу.

                        А с остальными методами что делать? Бывает, например, что у кокласса диспинтерфейса есть публичные методы для вызова изнутри модуля, про которые совершено незачем знать снаружи. Например, что угодно для работы с указателями. Их тоже ручным сравнением убирать? Так тогда где же польза для реализации IDispatch.

                        Если мы не можем явно объявить/пометить набор для экспозинга, то это и значит: годится в рамках этой задачи рефлексия только на то, чтобы название метода не дублировать, записывая отдельно в строку:

                        То, что строку с именем метода можно вытащить, и всё? Или что-то большее?

                        Что-то большее было бы, например, если бы могли помечать нужные методы.


                      1. mayorovp
                        26.06.2024 15:26

                        А с остальными методами что делать? Бывает, например, что у кокласса диспинтерфейса есть публичные методы для вызова изнутри модуля, про которые совершено незачем знать снаружи.

                        А с фига ли они тогда публичные? friend в помощь.

                        А ещё можно собирать методы не из самого класса, а из реализованных им COM интерфейсов.

                        Что-то большее было бы, например, если бы могли помечать нужные методы

                        Ну, тут надо просто ещё 3-12 лет подождать пока P1887 и P2565 "дозреют".


                      1. ImagineTables
                        26.06.2024 15:26

                        А с фига ли они тогда публичные? friend в помощь.

                        Просто хотя бы ради интереса посмотрите на другие языки. В C#, например, ввели специальные ключевые слова для разрешения публичного доступа изнутри сборки и одновременного сокрытия от доступа снаружи. Так что, не говорите, что это никому не нужно.


  1. IGR2014
    26.06.2024 15:26

    Как-же стрёмно оно выглядит... Лучше что-то похожее на Kotlin Serialization


    1. mayorovp
      26.06.2024 15:26
      +1

      Kotlin Serialization использует Kotlin Reflection

      Какой смысл добавлять в С++ отдельный механизм для сериализации вместо более универсальной рефлексии?