Comments 72
По рефлексии энумераторов. Взял с хабра многолетней давности пример. Вся идея реализации рефлексии умещается в одном экране. Чуть доработал (убрал лямбды, у нас легаси, еще древнее стандарт), получил рефлексию перечислений. В моем проекте невозможно ждать нового стандарта.
Вся реализация в хидере. Перевод перечисления на рефлексию - замена enum EnumName {item1, item2} на ENUM(EnumName, item1, item2). Список перечислений со всем синтаксисом присвоения констант сохраняется.
Добавил сахарка, в том же списке перечислений не нарушая совместимости смог опционально добавлять еще и мультиязычные алиасы (человекочитаемый текст для вывода в контролы), а также выделение существенного имени элемента с обрезкой декорирования венгерской нотации. Конечно же обратная инициализация из строки в энумератор, итерирование по элементам.
Первое впечатление от рефлексии 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, но по статусу не знаю
Когда это появится в компиляторах, многие наконец выдохнут с облегчением. И со спокойным сердцем уйдут на пенсию. (ирония)
Жуть какая..
Splicers - [:R:]
Баян! )))
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?
В основном пропозале возможности объявлять новый енум нету, насколько я знаю
А динамические библиотеки тоже можно будет подгрузить и исследовать?
Рефлексия предлагается строго на этапе компиляции, поэтому нет
Думать вредно! Но наличие статик рефлексии сильно упростит рефлексию динамическую. Мой фреймворк где можно делать удалённый вызов процедур, который сейчас работает с помощью отдельного кодогенератора, наконец-то можно будет упростить.
Каст энума уже работает, без рефлексию. На всякий случай напоминаю)
Знаю про каст к инту, а к строке как?)
конкретно энамы https://github.com/Neargye/magic_enum
+структуры https://github.com/getml/reflect-cpp
насколько помню, там один и тот же принцип. для больших энамов надо немного подкостыливать+медленнее будет компилиться
Magic enum штука хорошая, пока не упираешься в ее ограничения. Наиболее серьезными для меня были: 1) зависимость от компилятора (разрабатывал приложение которое необходимо было компилировать несколькими компиляторами, включая полуэкзотику (хоть и с С++17, Intel Compiler если интересно); 2) проблемы с enum
в шаблонах. Часть уже поправлена, однако иногда требуются танцы с бубном, чтобы затарахтело.
В итоге, на проекте от magic_enum
было решено отказаться в пользу к полуавтоматическому (вставляем руками с помощью утилиты там где надо) касту к строке.
Около года назад впервые попробовал Java и всё это время писал бэкэнд чисто на ней - теперь смотреть больно на плюсы. Хотя раньше и сильно любил их
Здорово, что может появиться рефлексия в ближайшее десятилетие (давайте будем реалистами), хоть и с таким пугающим синтаксисом
Господи... Во что они превращают C++...
Ну ок, допустим в компайл-тайме мы теперь умеем из енума сделать строку. Хотя могли и до этого. А с рантаймом что делать?
А что мешает-то вызвать ту же enum_to_string в рантайме?
У меня вот из этих предложений сложилось впечатление что в рантайме кина не будет:
Динамическая рефлексия доступна в рантайме
Плюс
Статическая работает во время компиляции.
Плюс
Основной пропозал, прописывает базу для статической рефлексии.
Во время компиляции с помощью рефлексии генерируется функция enum_to_string. В рантайме она вызывается.
Динамической рефлексии (см фичи питона) действительно не будет, но для каста енума она не нужна. Сама рефлексия отработает во время компиляции, по сути сгенерировав функцию с кучей if-ов. В рантайме это будет обычной функцией
В Delphi активно пользуюсь RTTI (рефлексией). Кроме сериализации еще одно применение в моем случае - построение юзер интерфейса в рантайме. Интерфейс для редактирования неких данных описываемых структурами/классами. В основном такие прогрммы нужны для тестировния, но без RTTI моя работа была бы адом :). Надеюсь все proposals будут приняты. Жду рефлексию в С++ уже давно.
Напомните, что хорошего прикладного можно запилить с помощью статической рефлексии?
Для динамической, например, сходу можно вспомнить универсальный компонент Object Inspector.
Сериализатор. ORM. DI фреймворк. Биндинг к другому языку. Реализацию IDispatch.
Ну или аналог динамической рефлексии, а потом тот же Object Inspector.
Реализацию IDispatch.
Боюсь, я несколько подзабыл такие материи, как интеграция с Visual Basic (для этого же нужны диспинтерфейсы?), поэтому поправьте, если что-то не так.
Проблема, как я её помню, была в следующем. Руки чесались сделать макрос, в который бы можно было передать имя метода, и который бы:
Добавлял нативную декларацию в кокласс (для дуальности);
Подклеивал (
##
) к переданному имени кавычки и добавлял его в карту 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? КМК, этот способ программировать коллег лучше, чем написание комментариев ;)
А что значит само по себе выражение «передача метода в язык автоматическим управлением»?
В 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
Мы ползали, ползали, да так и не проползали. Надо перепроползать, перевыпроползать.
Нативная рефлексия в C++ уже близко