170 похожих чатов

А можно ссылку что такое лупхолы? В целом мне кажется

данная задача нерешаема без скатывания в RTTI...

22 ответов

72 просмотра

Про сами лупхолы можно послушать здесь. И посмотреть мой репозиторий. В особенности комментарии здесь

можно еще посмотреть на эту библиотеку https://github.com/vampyrofangclub/horrifiction (40 строчек кода) - там в ридми показан пример как можно применять библиотеку для стирания типов и рантайм-полиморфизма

Денис-Берёзкин Автор вопроса
Богдан
можно еще посмотреть на эту библиотеку https://git...

Спасибо, посмотрю. Но не понял зачем отдельная библиотека для рантайм-полиморфизма? RTTI ведь стандартный есть...

Текущий RTTI в С++ ограничен сишным интерфейсом (требующий конкретные типы) - то есть это значит что ты не можешь например работать полиморфно с рантайм-объектами у которых методы возвращают auto-тип или параметры принимают auto-тип. Например допустим я хочу сохранять в контейнере объекты имеющие метод auto draw(auto lambdaCallback) а потом вызывать этот метод для каждого объекта в контейнере не зная его настоящий тип. Или например хочу сохранить лямбду в std::function но сейчас это невозможно в случае если у лямбды будет тип возврата auto или она принимает auto-параметр. А вот рантайм-полимформизм через эту технику лупхолов делает все это возможным

Богдан
Текущий RTTI в С++ ограничен сишным интерфейсом (т...

Это же все поломается если делать в разных единицах трансляции, разве нет?

Денис Михайлов
Это же все поломается если делать в разных единица...

Да, конечно. Но можно смотреть на это как на рефлексию которая точно так же будет в С++26 ограничена одной единицей трансляции

Но кстати конкретно вот такое применение лупхолов для рантайм-полиформизма можно было бы заменить фичей которая называется "давайте разрешим шаблонные виртуальные методы" и тогда это потенциально может работать между разными TU (но в свою очередь это усложняет ABI и требует от линкера дополнительного процесса по мерджингу виртуальных таблиц из разных единиц трансляции)

Богдан
Но кстати конкретно вот такое применение лупхолов ...

Тут можно подумать в сторону того что в модных языках называется boxing. То есть можно по простому сохранить пару object+function. Тогда функции реализующие trait можно писать отдельно.

Что касается техники лупхолов то для объектов с методом "auto draw(auto lambdaCb)" не будет происходить "мономорфизации в стиралке типов", "не требует наличие JIT" и не создает "комбинаторного взрыва в вызывающем коде" По сути техника рантайм-полиморфизма через лупхолы автоматизирует то что и так приходится делать вручную. Например если ты хочешь хранить в контейнере объекты двух типов имеющие шаблонный метод "auto draw(auto lambdaCb)" struct Rect { int width; int height; auto draw(auto labmdaCb){ ... } }; struct Circle { int radius; auto draw(auto labmdaCb){ ... } } то тебе придется объявить некую Shape-обертку чтобы завернуть в нее объект Rect или Circle перед добавлением в контейнер попутно сохранив числовой айдишник типа int TypeId; template<typename T> auto GetIdForType = [](){ return TypeId++; }() struct Shape { void* ptr; int typeId; Shape(auto obj){ ptr = obj; typeId = GetIdForType<decltype(obj)>; } } auto vec = std::vector<Shape>(); vec.push_back(Shape(new Rect(10, 10)); vec.push_back(Shape(new Circle(20)); и дальше тебе нужно будет вручную перебирать возможные типы на совпадение с typeId у объекта Shape for(auto shape: vec){ if(shape.typeId == GetTypeId<Rect*>){ auto obj = static_cast<Rect*>(shape.ptr); //теперь у нас статический тип и мы можем вызывать шаблонный метод draw() auto res = obj->draw([](...){...}); } if(shape.typeId == GetTypeId<Circle*>){ auto obj = static_cast<Circle*>(shape.ptr); //теперь у нас статический тип и мы можем вызывать шаблонный метод draw() auto res = obj->draw([](...){...}) } } Вот и все. Вот пример того как можно реализовать на С++ рантайм-полиморфизм для методов с auto-возвратом или с auto-параметрами без всяких лупхолов И техника рантайм-полиморфизма через лупхолы в данном примере будет иметь точно такой же оверхед - то есть все что будут делать лупхолы (в случае использования этой библиотеки) так это за юзера выполнят эти две строчки на рантайм-проверку typeId для типа Circle и Rect и юзеру всего лишь нужно будет передать лямбду получив параметром объект конкретного типа for(auto shape: vec){ shape.visit([](auto obj){ //теперь obj у нас статический тип и мы можем вызвать шаблонный метод draw() auto res = obj->draw([](){...}); }) } И поскольку мы выяснили что лупхолы не добавляют оверхеда и всего лишь автоматизируют то что разработчик и так вынужден писать вручную то дальше можно рассказать в чем прелесть лупхолов (помимо того что убирает болерплейт ручного матчинга). И прелесть механизма стирания типов через лупхолы заключается в возможности заматчить тип который разработчику неизвестен, например когда мы возвращаем разные лямбды из функции по рантайм-условию auto getFn(bool flag) { if (std::rand() % 2) { return Any(new auto([=](auto val) { return val + 1; })); } else { auto someVal = 10; return Any(new auto([=](auto val) { return someVal + val; })); } } здесь возвращаемый тип лямбды зависит от того будет ли в рантайме делиться на 2 некое рандомное число и ты можешь написать подобно обертки Shape (где сохраняешь void* ptr и int typeId) аналогичную обертку Any для лямбд но дальше у тебя тупик потому что ты не знаешь на какой тип нужно матчить typeId int main(){ auto any = getFn(); if(any.typeId == GetTypeId<??????>){ ... } } а вот механизм лупхолов как раз таки позволяет узнать этот тип и автоматически проверить совпадение typeId и юзеру всего лишь нужно будет передать лямбду где он получит конкретный объект лямбды аргументом int main(){ auto any = getFn(); any.visit([](auto fn) { std::cout << (*fn)(42) << "\n"; //43 std::cout << (*fn)(42.42) << "\n"; //43.42 }); auto any = getFn(); any2.visit([](auto fn) { std::cout << (*fn)(42) << "\n"; //52 std::cout << (*fn)(42.42) << "\n"; //52.42 }); }

Так я уже выше сказал - да, здесь ограничение на то что это будет работать внутри одной единицы трансляции (или точнее могут быть разные TU, но эти типы для которых нужно стереть тип должны быть объявлены в хедерах) собственно как и у будущей рефлексии. Если аргумент про JIT касался возможности работы разных единиц трансляции то тогда пардон, не так понял

а как иначе?

Богдан
а как иначе?

и как это люди обходятся без реализации ваших идей .... загадка .... 🤷🏻‍♂️

Богдан
Так я уже выше сказал - да, здесь ограничение на т...

типы не просто должны быть объявлены в хедерах у каждой единицы трансляции должен совпадать порядок в котором всё множество типов регистрируется в залупхоле у стиралки (точнее даже не порядок, а мнение компилятора о том в каком порядке произвести залупхольное волшебство, чтобы id типов совпали, их порядок в ручных vtable и прочее) иначе как минимум пол дня весёлой отладки странных сегфолтов обеспечены (и у меня уже были пару раз)

Boris Usievich
и как это люди обходятся без реализации ваших идей...

страдаем круглые сутки и ждём не дождемся когда он их начнёт писать уже не сюда, а в рассылку WG21

Гражданин Котейко
типы не просто должны быть объявлены в хедерах у к...

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

Богдан
порядок регистрации совпадать не должен но есть ню...

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

Богдан
Что касается техники лупхолов то для объектов с ме...

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

Похожие вопросы

Обсуждают сегодня

а через ESC-код ?
Alexey Kulakov
29
30500 за редактор? )
Владимир
47
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
13
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
program test; {$mode delphi} procedure proc(v: int32); overload; begin end; procedure proc(v: int64); overload; begin end; var x: uint64; begin proc(x); end. Уж не знаю...
notme
6
Как передать управляющий символ в открытую через CreateProcess консоль? Собсна, есть процедура: procedure TRedirectThread.WriteData(Data: OEMString); var Written: Cardinal;...
Serjone
6
Вот еще странный косяк, подскажите как бороться. Я git clone сделал себе всего embassy и примеры там запускаю. Всё хорошо. Но вот решил в cargo.toml зависимости не как в приме...
Lukutin R2AJP
1
вы делали что-то подобное и как? может есть либы готовые? увидел картинку нокода, где всё линиями соединено и стало интересно попробовать то же в ddl на lua сделать. решил с ч...
Victor
8
Ребят в СИ можно реализовать ООП?
Николай
33
Карта сайта