Pull to refresh

Comments 72

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

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

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

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

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

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

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

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

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

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

Дичь реальная. Вместо полезных дел люди занимаются полнейшей фигней, куда проще написать строковые ключи при сериализации, например, в json, чем разбираться с этой мутью. Просто из-за экономии времени. Чето весь народ ударился в метапрограммирование, кто бизнес логику то программирует сейчас?
Ну ладно, неохота ручками писать строковые ключи, так напишите внешний скрипт, который просканирует код и выдаст какую-то готовую функцию. Я так делаю, все быстро работает и не тормозит на шаблонах.

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

Новые фичи в плюсах - такое себе. Вот попросил адептов std::move написать сампл демонстрации их преимуществ. Сделали грубейшую ошибку, которую в реальной работе нужно будет искать очень долго.
И зачем мне упарываться по новым фичам C++, если мой вебсервер работает на пару процентов быстрее нгинкс и в 17 раз быстрее усервера?

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

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

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

Вот кстати да - первый насколько помню пост с "а еще - см мой телеграм" где реакция "о! это полезно будет! надо подписатся"

template <typename This, typename Widget, auto Func>
struct Helper {
    This* this_;
    auto& operator=(const auto& arg) const {
        auto widget = dynamic_cast<Widget*>(this_->parent());
        auto index = widget->indexOf(this_);
        if constexpr(requires { (widget->*Func)(index, arg); }) // <==
            (widget->*Func)(index, arg);
        else
            (widget->*Func)(index);
        return *this;
    }
};

Вы уже рефлексию ждёте, а я только недавно узнал, что в if constexpr requires-expression можно использовать.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UFO just landed and posted this here

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

UFO just landed and posted this here

Да, я понял. но всё равно испугался )))

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

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

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

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

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

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

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

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

UFO just landed and posted this here

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

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

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

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

Плюс

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

Плюс

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

while (pUnk->Release());

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Лично я большой поклонник DDD. Даже больше скажу, DDD это, с моей точки зрения, и есть ООП, каким оно должно быть. Любое использование ООП не для моделирования предметной области, а для каких-то технических штучек, в том числе использование friend, только запутывает читателя кода.

Равным образом, в плюсах объективно не хватает метапрограммирования. И дефицит покрывали за счёт шаблонов, которые, вообще-то, нужны для обобщений. Могу привести примеры того, что делают на шаблонах, только потому, что иначе не сделать. (И выглядит это ужасно). Сейчас вот к шаблонам добавилась эта так называемая «статическая рефлексия». А макросов как не было, так и нет. Я иногда думаю, что связка пых + плюсы это не такая уж безумная идея. Во всяком случае, там можно было бы одной строкой безо всяких повторений и объявлять нативную реализацию метода диспинтерфейса, и заносить его в карту по тому же же имени. И делать это исключительно с теми методами, которые нужны, а не строить догадки на основе публичности или других признаков.

обвешиваясь комментариями "только для класса Foo, не трогать!".

P.S. Как я объяснил выше, мне не нужно поведение, описываемое friend. Но если оно нужно вам в шарпе, вы смотрели на InternalsVisibleTo? КМК, этот способ программировать коллег лучше, чем написание комментариев ;)

С чего бы InternalsVisibleTo было аналогом friend? Это ж способ расширить видимость internal, а нужно сузить.

А что значит само по себе выражение «передача метода в язык автоматическим управлением»?

В COM у метода могут быть аттрибуты (hidden, restricted), из из языка типа VB вы этот метод не увидите или не вызовите.

А вот что есть в спецификации COM, так это запрет на то, чтобы вызывать AddRef у одного интерфейса, а комплиментарный ему Release у другого. То есть, хотя 90% имплементаций имеют один счётчик ссылок «на объект», согласно спецификации у каждого интерфейса объекта (а интерфейсов у объекта может быть много) должен быть свой счётчик ссылок, и каждый интерфейс может порождаться/уничтожаться независимо от других.

Соответственно, вызывая AddRef или Release вы не управляете временем жизни объекта, вы управляете временем жизни конкретного интерфейса. Другое дело, что имплементация в 90% (а то и больше) случаев в угоду упрощения скатывается до единой для всех реализации AddRef/Release и единому счётчику ссылок. Более того, если реализовывать COM-объект на C++ как C++-класс, то по другому и не получится сделать, не прибегая к извратам.

Означает буквально написанное. Вызовы AddRef/Release в VB — прерогатива менеджера памяти. Хотя, конечно, при желании можно извратиться.

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

Нет, не для этого. Visual Basic прекрасно может работать с объектами, не поддерживающими IDispatch. И с другой стороны, хотя любой экземпляр класса, написанного на VB, будет поддерживать IDispatch, вы прекрасно можете работать с такими объектами из других языков, не поддерживающими IDispatch.

Так что нет, IDispatch нужен не для интеграции с VB, он нужен для позднего связывания. А вот для VBScript он конечно же нужен, потому что там априори всё связывание — позднее, в момент интерпретации скрипта.

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

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

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

Синтаксис, как минимум, выглядел бы не так ужасающе

Один специальный синтаксис для сериализации, второй специальный синтаксис для ORM, третий специальный синтаксис для DI... вы точно уверены, что это было бы не так ужасающе чем один-единственный синтаксис для рефлексии?

Мы ползали, ползали, да так и не проползали. Надо перепроползать, перевыпроползать.

Sign up to leave a comment.

Articles