Pull to refresh

Comments 23

Давно уже пора выкинуть С++ на свалку истории. Хватит уже этого франкинштейна качать.
На плюсах можно вполне читаемо и адеквато писать. Другое дело что этому нигде не учат.
{sarcasm}*Комментарий про Rust*{/sarcasm}
А если серьезно, то отличные новости! Может наконец в с++ появится какой-то юзабельный стандартный reflection и семантика для пропертей. Может я не в курсе, но кто сегодня использует c++ там, где нужно писать на C? ИМХО, эта совместимость уже давно себя изжила.
Тоже не хватает этих возможностей в 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);

и т.д.
Вы вполне могли-бы перенести этот функционал в рантайм (используя std::tuple к примеру), вместо этапа компиляции. Или у вас есть пример отличный от:
std::cout <<visit_struct::get<3>(s);
auto variant=visit_struct::get(3,s);
который требуется на этапе компиляции?

В чём смысл запутывания кода с помощью visit_struct, если используется это в рантайме?
Я не совсем понимаю, что Вы имеете ввиду, под «можно перенести это в рантайм испульзуя 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
Ну пример в принципе тот-же самый. Обращение к члену структуры по порядковому номеру, с автоприведение типов (std::tuple). В отличии от std::tuple, можно получить имя по индексу (наверное тут наибольший профит в удобстве?). Использование этого имени (например сравнение), — уже рантайм. Также как впрочем и с tuple. Обращение по индексу — генерация кода (как и в tuple). При этом использование структуры (обращение по имени), — compile time (заметьте без дополнительной генерации кода).

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

Для примера, простыми словами, где использование приведённого вами метода предоставляет преимущества:
а) в лучшей производительности;
б) в читабельности кода;
в) в удобстве использования;
г) в конкретном прикладном случае;
Мне кажется, все пункты справедливы, от а) до г). Проблема с tuple в том, что члены не именованы. В принципе, можно было бы создавать tuple of references, не удивлюсь если под капотом visit_struct так и делает. В моём случае есть возможность комбинировать именованный доступ и доступ по индексу с минимально возможным в принципе оверхедом. Это читаемо и удобно, позволяет избегать дублирования кода, нет необходимости платить за то, что не используется.
Применение то же, что и для Qt Properties, BOOST_HANA_DEFINE_STRUCT, RTTR. Те применения, которые я использую — это разные формы сериализации по сути, как для сохранения на диск и передачи по сети, так и для GUI-редактирования объектов и их свойств. Сериализация может не требовать рантайм-рефлексии так как передавать всё равно весь объект разом. GUI-редактор требует рантайма т.к. опции изменяются в произвольном порядке, известном только в рантайме. Я нашел статью «Designing a C++ Property System» по этой теме, там и мотивация и проблемы рассматриваются.
а разве существуют задачи, где при наличии возможности писать на с++ НУЖНО писать на си?
Есть слепые фанатики, которые на любой вопрос говорят «Да ну на* этот ваш {любой язык программирования}, мы сейчас все быстро и круто на C напишем!».
Да, это все задачи для встраиваемых девайсов с вычислительно слабыми микроконтроллерами, где писать на C++ теоретически можно, но столько памяти сжирает, что в итоге чаще пишут на С. Поэтому на месте С++-комитета я бы не отказывалась от совместимости, просто чтобы не оттолкнуть embedded-сообщество окончательно.
вы сначала говорите, что c++ непригоден для embedded’а, а сразу после утверждаете, что отказаться от совместимости с си плохо с точки зрения embedded. Определитесь уже.

Кстати, отмечу, что поддержка interoperability может быть реализована не только на уровне языка (как и для всех других пар языков). Например — на тех же модулях с явным указанием версии языка — то, о чем я писал ниже
Никто не мешает писать на C++ так, чтобы памяти не жрало.
Если новые плюшки будут настолько несовместимы со старым кодом, что его нельзя будет подтянуть опциями, прагмами или дефайнами, то это потянет за собой разделение гораздо худшее, чем между Python 2 и 3.
Если тот же Python можно запускать только на конкретной версии интерпретатора, то в С++ разные модули наверняка можно будет билдить с разными флагами компилятора, а потом линковать вместе.
Это понятно, про это я и писал. Но вдруг допроектируются до таких изменений, которые, например, не дадут использовать классы из одних модулей в других.
думаю, правильно, если вместо «версий стандарта» мы получим «версии языка». Пара breaking changes между c++11/14/17 все равно есть, так зачем тянуть устаревшие кодобазы в новый c++ если можно просто закрепить за ними версию языка?

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

это должно решаться модулями. В худшем случае, можно адаптировать существующий синтаксис: extern “c++17” {…
это не «совет», а явное указание контракта — «использовать сишный манглинг и соглашение о вызовах». По сути, extern «C» (с учетом того, как его используют) значит «вот этот вот код компилируй как будто он сишный»
Sign up to leave a comment.

Articles