Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.218.127.141] |
|
Страницы: (4) [1] 2 3 ... Последняя » все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Если кто помнит (а кто не помнит - тем напомню) в прошлом году на конфах некто Герб Саттер взорвал C++-общественность докладами про метаклассы:
https://www.youtube.com/watch?v=6nsyX37nsRs Если вкратце, программист описывает некий метакласс (например, Value или BitFields), в нём определяет определяет констрейнты, связанные с этим метаклассом, какие методы/поля надо ещё добавить, а потом компилятор соответствующим образом модифицирует AST для экземпляров этого метакласса: Прикреплённая картинка
Подробности описаны здесь. Всё бы ничего, но в стандарт это затащат не раньше 23-го года. А то и 26-го. До этого ещё static reflection должны принять и всё такое. В общем, долго ещё ждать. А вот пощупать уже сейчас хочется, поэтому решил я из своего автопрограммиста сделать автометапрограммиста. Чтобы мог похожую функциональность предоставить в рамках актуальных стандартов всем желающим, а не только тем, кто сидит на кастомных сборках clang'а. Пока предполагаю, что код, который нужно будет непосредственно писать разработчику, будет выглядеть как-то так: // Объявление метакласса METACLASS_DECL(interface) { enum Type { One = 1, Two, Three }; static void generate(interface* instance) { // Проверка свойств конкретного инстанса метакласса compiler.require(instance != nullptr, "Class instance is NULL!"); // Инжект в тело метакласса какого-то кода META_INJECT {int member;}; } }; // Инстанс метакласса METACLASS_INST(interface, IObject) { public: int AddRef(); int Release(); IObject* QueryInterface(uint32_t ifaceId); }; |
Сообщ.
#2
,
|
|
|
Скрытый текст Flex Ferrum, Ой, Флекс, прямо де жа вю. Недавно закончил читать доку по Расту - в аккурат концепция трэйтов тамошних. Один в один! Так, для справки напомню, ну и в меру моей "одноразовой" компетенции ... В Расте многие концепции ООП решаются совсем иначе классики С++/OOP Pascal/etc 1) Структуры - хранят только данные, наследования данных нет (но есть композиция и агрегация) 2) Реализации - хранят код "методов" для обработки данных смежной структуры 3) Трэйты - хранят "поведение", иными словами декларацию обязательно реализованных методов для отдельного поведения 4) Реализация трэйтов - понятное дело, реализация методов трэйта для структуры 6) Трэйты-объекты - предназначены для обеспечения динамической диспетчеризации (пока я механизм не осознал, прочел чисто концепцию).Типа Box<T:Display> обозначает ссылку на любой тип, реализующий трэйт Display Получается что? Что в С++ к 23-26 году таки затащят то, что сейчас есть в Расте??? D_KEY, OpenGL, плс, правьте - если я где-то ашыпся! |
Сообщ.
#3
,
|
|
|
JoeUser, ну, судя чисто по тому, что ты описал - это не совсем метаклассы (в понимании Саттера). Идея Саттера - "прокачать" шаблоны, концепты и статический рефлекшн (генеративный) так, чтобы из C++-кода можно было управлять генерацией AST. Это больше похоже на макросы из Nemerle.
Добавлено То есть, скажем, пишешь ты очередную либу типа Dependency Ijection. Создаёшь метакласс Component, в нём описываешь - как генерируется метаинформация о связывании компонента, какие дополнительные методы в реализацию компонента вдвигаются и т. п. Или, положим, пишешь метакласс SerializableStruct, который добавляет в структуру методы сериализации/десериализации. |
Сообщ.
#4
,
|
|
|
Цитата Flex Ferrum @ Создаёшь метакласс Component, в нём описываешь - как генерируется метаинформация о связывании компонента, какие дополнительные методы в реализацию компонента вдвигаются и т. п. Честно говоря - слишком размыто. Пока не отвечу, пока не понимаю чего надо. Цитата Flex Ferrum @ Или, положим, пишешь метакласс SerializableStruct, который добавляет в структуру методы сериализации/десериализации. В Расте пишется трэйт Serializе. Который определяет обязательные методы serialize()/deserialize(). Как только будет произведена имплементация данного трэйта для хотя бы для одной структуры - мы можем использовать "контейнеры", к примеру, в которых будут использованы объекты, поддерживающие поведение"Serialize". Дальше уже компайлер определяет - можем ли мы заполнять контейнер конкретным классом, либо у него не хватает реализации (методов) для конкретного поведения Serializе. Пока - все ровное, не Добавлено PS: Ни кто не против, что я сюда частично обсуждение ЯП Rust приплел? |
Сообщ.
#5
,
|
|
|
Цитата JoeUser @ В Расте пишется трэйт Serializе. Который определяет обязательные методы serialize()/deserialize(). Как только будет произведена имплементация данного трэйта для хотя бы для одной структуры - мы можем использовать "контейнеры", к примеру, в которых будут использованы объекты, поддерживающие поведение"Serialize". А трейт для структуры ты реализуешь сам (ручками) или можешь обобщённый код на основе reflection'а написать? |
Сообщ.
#6
,
|
|
|
Цитата Flex Ferrum @ А трейт для структуры ты реализуешь сам (ручками) или можешь обобщённый код на основе reflection'а написать? Боюсь ошибиться (надо папиков Раста спрашивать). Но в Расте гораздо больше чем мне казалось - является шаблонами. Даже элементарное объявление "let mut str = "string" - является шаблоном. Чтобы ответить однозначно - приведи код на С++, если C++ не умеет - приведи псевдомашинный код чего хотелось. Пока могу сказать одно ... Трэйты, объекты-трэйты, и трэйт-имплементации в Расте - параметризуются. |
Сообщ.
#7
,
|
|
|
Пример... Да вот хотя бы в моём посте на картинке.
|
Сообщ.
#8
,
|
|
|
Флекс, там слишком много буквоф!!!
Смысла прописывать операции, близкие по действию - не вижу. Более того, это забивает основной вопрос! Типа, чонадо?! Прошу "синтетический" пример, где есть только реализация "вопрошаемого" и ничего более. Добавлено И еще момент - на сколько я понял, искать аналоги "решений" для Раста, базируясь на идеологии С++ - это неправильно. Правильно искать решения задачи на этих ЯП - пусть и чаще совсем непохожие друг на друга. |
Сообщ.
#9
,
|
|
|
Цитата JoeUser @ Более того, это забивает основной вопрос! Типа, чонадо?! Ну смотри. Ты описываешь структуру: FlagType {auto in, out, trunk, create, open}; Дальше описываешь метакласс, который делает примерно следующее: $class FlagType { constexpr { int curValue = 1; for (auto m : FlagType.members) { m.SetValue(curValue); curValue <<= 1; } } }; То есть код метакласса обходит все мемберы "своего" класса и каждому присваивает значение. |
Сообщ.
#10
,
|
|
|
Flex Ferrum, увы, в силу моих теперешних знаний, аналогов не вижу.
И тем не менее есть моменты: 1) Статическая инициализация сверх-большой структуры - приведет в увеличению кода значительно. 2) Динамическая инициализация сверх-большой структуры - приведет к небольшому проседанию "старта" при условии быстрых вычислений. 3) Можно пойти путями "bison/flex", сперва вычисляем - потом постим в код 4) C++ way в Rust - наверняка есть, призываю духов D_KEY и OpenGL |
Сообщ.
#11
,
|
|
|
Цитата JoeUser @ 1) Статическая инициализация сверх-большой структуры - приведет в увеличению кода значительно. Скорее, времени компиляции. Ведь код метаклассов исполняется в compile time. Это contsexpr на стеродиах и анаболиках. Добавлено Я, собственно, и выбрал третий путь - утилита будет внутри себя исполнять этот код и продуцировать "исправленный" C++-исходник. |
Сообщ.
#12
,
|
|
|
Цитата Flex Ferrum @ Скорее, времени компиляции. Э нееее ....!!!! Я уже как-то генерировал тест с "а-ля" 4096-вложенными классами. Без специальных ключей - включалось все! Попробуй, чисто ради эксперимента, заинициализировать свой массив (вектор) из 16192 элемента в статике числами Фибоначчи. А потом попробуй динамически. Просто чуйка шепчет - не сильно, не ощутимо быстрее. А вот экзешник - посчитать надо. |
Сообщ.
#13
,
|
|
|
Цитата JoeUser @ Я уже как-то генерировал тест с "а-ля" 4096-вложенными классами. Без специальных ключей - включалось все! Попробуй, чисто ради эксперимента, заинициализировать свой массив (вектор) из 16192 элемента в статике числами Фибоначчи. Тут "немножко" другой подход. Проблема с метапрограммированием на шаблонах в том, что "язык" шаблонов ближе к функциональному, а его тьюринг-полнота основана на рекурсии. Поэтому задачи обработки глубоко вложенных конструкций и прочего таки встаёт. Рано или поздно. Когда ты понимаешь, что двадцати гигабайт памяти тебе уже не хватает для компиляции TensorFlow. C constexpr-вычислениями чуть другое. Компиляторную бомбу всё равно ещё можно сделать, но так или иначе, компилятор уже не только компилятор, но и интерпретатор, который тупо интерпретирует им самим же построенное AST. Поэтому всё становится проще, как с точки зрения программирования, так и с точки зрения компиляции. И эта часть языка (и стандарта) развивается очень активно - с constexpr-функций снимается всё больше ограничений (вспомни, какими они были в 11-ом стандарте), в двадцатом уже обещают строгий constexpr (который constexpr!), выполняющийся либо строго в компайл-тайм, либо не выполняющийся вообще. |
Сообщ.
#14
,
|
|
|
Цитата JoeUser @ Попробуй, чисто ради эксперимента, заинициализировать свой массив (вектор) из 16192 элемента в статике числами Фибоначчи. #include <iostream> int a[16192]; constexpr int fun() { a[0] = 0; a[1] = 1; for (int i = 2; i < 16192; ++i) a[i] = a[i-2] + a[i-1]; return 0; } int x = fun(); int main() { for (auto& i : a) std::cout << i << ' '; } |
Сообщ.
#15
,
|
|
|
Цитата Flex Ferrum @ Тут "немножко" другой подход. Проблема с метапрограммированием на шаблонах в том, что "язык" шаблонов ближе к функциональному, а его тьюринг-полнота основана на рекурсии. Флекс, давай отделять мух от котлет! Я понимаю, движется все. Се ля ви. Но предлагаю очень не забывать о принципе Паретто. Конечно, любые телодвижения в развитии важны! Но соотнеси их к вышеозначенному принципу! ... Вопрос "что, зачем и какими силами" воспроизводится?! Хорошо, если упрощает, а несли нет???! А если это вааще не упало щяс??? |