На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
Модераторы: Qraizer
  
> Метаклассы в C++ , Мечта или реальность?
    Если кто помнит (а кто не помнит - тем напомню) в прошлом году на конфах некто Герб Саттер взорвал C++-общественность докладами про метаклассы:
    https://www.youtube.com/watch?v=6nsyX37nsRs

    Если вкратце, программист описывает некий метакласс (например, Value или BitFields), в нём определяет определяет констрейнты, связанные с этим метаклассом, какие методы/поля надо ещё добавить, а потом компилятор соответствующим образом модифицирует AST для экземпляров этого метакласса:
    Прикреплённая картинка
    Прикреплённая картинка


    Подробности описаны здесь.

    Всё бы ничего, но в стандарт это затащат не раньше 23-го года. А то и 26-го. До этого ещё static reflection должны принять и всё такое. В общем, долго ещё ждать. А вот пощупать уже сейчас хочется, поэтому решил я из своего автопрограммиста сделать автометапрограммиста. Чтобы мог похожую функциональность предоставить в рамках актуальных стандартов всем желающим, а не только тем, кто сидит на кастомных сборках clang'а. Пока предполагаю, что код, который нужно будет непосредственно писать разработчику, будет выглядеть как-то так:

    ExpandedWrap disabled
      // Объявление метакласса
      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);
      };
      Скрытый текст
      Flex Ferrum, :lol:

      Ой, Флекс, прямо де жа вю. Недавно закончил читать доку по Расту - в аккурат концепция трэйтов тамошних. Один в один!
      Так, для справки напомню, ну и в меру моей "одноразовой" компетенции ... В Расте многие концепции ООП решаются совсем иначе классики С++/OOP Pascal/etc

      1) Структуры - хранят только данные, наследования данных нет (но есть композиция и агрегация)
      2) Реализации - хранят код "методов" для обработки данных смежной структуры
      3) Трэйты - хранят "поведение", иными словами декларацию обязательно реализованных методов для отдельного поведения
      4) Реализация трэйтов - понятное дело, реализация методов трэйта для структуры
      6) Трэйты-объекты - предназначены для обеспечения динамической диспетчеризации (пока я механизм не осознал, прочел чисто концепцию).Типа Box<T:Display> обозначает ссылку на любой тип, реализующий трэйт Display

      Получается что? :lol: Что в С++ к 23-26 году таки затащят то, что сейчас есть в Расте??? :lol:

      D_KEY, OpenGL, плс, правьте - если я где-то ашыпся! :-?
        JoeUser, ну, судя чисто по тому, что ты описал - это не совсем метаклассы (в понимании Саттера). Идея Саттера - "прокачать" шаблоны, концепты и статический рефлекшн (генеративный) так, чтобы из C++-кода можно было управлять генерацией AST. Это больше похоже на макросы из Nemerle.

        Добавлено
        То есть, скажем, пишешь ты очередную либу типа Dependency Ijection. Создаёшь метакласс Component, в нём описываешь - как генерируется метаинформация о связывании компонента, какие дополнительные методы в реализацию компонента вдвигаются и т. п. Или, положим, пишешь метакласс SerializableStruct, который добавляет в структуру методы сериализации/десериализации.
          Цитата Flex Ferrum @
          Создаёшь метакласс Component, в нём описываешь - как генерируется метаинформация о связывании компонента, какие дополнительные методы в реализацию компонента вдвигаются и т. п.

          Честно говоря - слишком размыто. Пока не отвечу, пока не понимаю чего надо.

          Цитата Flex Ferrum @
          Или, положим, пишешь метакласс SerializableStruct, который добавляет в структуру методы сериализации/десериализации.

          В Расте пишется трэйт Serializе. Который определяет обязательные методы serialize()/deserialize(). Как только будет произведена имплементация данного трэйта для хотя бы для одной структуры - мы можем использовать "контейнеры", к примеру, в которых будут использованы объекты, поддерживающие поведение"Serialize". Дальше уже компайлер определяет - можем ли мы заполнять контейнер конкретным классом, либо у него не хватает реализации (методов) для конкретного поведения Serializе.

          Пока - все ровное, не :lol:

          Добавлено
          PS: Ни кто не против, что я сюда частично обсуждение ЯП Rust приплел? :-?
            Цитата JoeUser @
            В Расте пишется трэйт Serializе. Который определяет обязательные методы serialize()/deserialize(). Как только будет произведена имплементация данного трэйта для хотя бы для одной структуры - мы можем использовать "контейнеры", к примеру, в которых будут использованы объекты, поддерживающие поведение"Serialize".

            А трейт для структуры ты реализуешь сам (ручками) или можешь обобщённый код на основе reflection'а написать?
              Цитата Flex Ferrum @
              А трейт для структуры ты реализуешь сам (ручками) или можешь обобщённый код на основе reflection'а написать?

              Боюсь ошибиться (надо папиков Раста спрашивать). Но в Расте гораздо больше чем мне казалось - является шаблонами. Даже элементарное объявление "let mut str = "string" - является шаблоном. Чтобы ответить однозначно - приведи код на С++, если C++ не умеет - приведи псевдомашинный код чего хотелось.

              Пока могу сказать одно ... Трэйты, объекты-трэйты, и трэйт-имплементации в Расте - параметризуются.
                Пример... Да вот хотя бы в моём посте на картинке.
                  Флекс, там слишком много буквоф!!! :(
                  Смысла прописывать операции, близкие по действию - не вижу.
                  Более того, это забивает основной вопрос! Типа, чонадо?!
                  Прошу "синтетический" пример, где есть только реализация "вопрошаемого" и ничего более.

                  Добавлено
                  И еще момент - на сколько я понял, искать аналоги "решений" для Раста, базируясь на идеологии С++ - это неправильно.
                  Правильно искать решения задачи на этих ЯП - пусть и чаще совсем непохожие друг на друга.
                    Цитата JoeUser @
                    Более того, это забивает основной вопрос! Типа, чонадо?!

                    Ну смотри. Ты описываешь структуру:
                    ExpandedWrap disabled
                      FlagType {auto in, out, trunk, create, open};

                    Дальше описываешь метакласс, который делает примерно следующее:
                    ExpandedWrap disabled
                      $class FlagType
                      {
                          constexpr
                          {
                                 int curValue = 1;
                                 for (auto m : FlagType.members)
                                 {
                                       m.SetValue(curValue);
                                       curValue <<= 1;
                                 }
                            }
                      };

                    То есть код метакласса обходит все мемберы "своего" класса и каждому присваивает значение.
                      Flex Ferrum, увы, в силу моих теперешних знаний, аналогов не вижу.
                      И тем не менее есть моменты:

                      1) Статическая инициализация сверх-большой структуры - приведет в увеличению кода значительно.
                      2) Динамическая инициализация сверх-большой структуры - приведет к небольшому проседанию "старта" при условии быстрых вычислений.
                      3) Можно пойти путями "bison/flex", сперва вычисляем - потом постим в код
                      4) C++ way в Rust - наверняка есть, призываю духов D_KEY и OpenGL
                        Цитата JoeUser @
                        1) Статическая инициализация сверх-большой структуры - приведет в увеличению кода значительно.

                        Скорее, времени компиляции. Ведь код метаклассов исполняется в compile time. Это contsexpr на стеродиах и анаболиках.

                        Добавлено
                        Я, собственно, и выбрал третий путь - утилита будет внутри себя исполнять этот код и продуцировать "исправленный" C++-исходник.
                          Цитата Flex Ferrum @
                          Скорее, времени компиляции.

                          Э нееее ....!!!! :lol:
                          Я уже как-то генерировал тест с "а-ля" 4096-вложенными классами. Без специальных ключей - включалось все!
                          Попробуй, чисто ради эксперимента, заинициализировать свой массив (вектор) из 16192 элемента в статике числами Фибоначчи.
                          А потом попробуй динамически. Просто чуйка шепчет - не сильно, не ощутимо быстрее. А вот экзешник - посчитать надо.
                            Цитата JoeUser @
                            Я уже как-то генерировал тест с "а-ля" 4096-вложенными классами. Без специальных ключей - включалось все!
                            Попробуй, чисто ради эксперимента, заинициализировать свой массив (вектор) из 16192 элемента в статике числами Фибоначчи.

                            Тут "немножко" другой подход. Проблема с метапрограммированием на шаблонах в том, что "язык" шаблонов ближе к функциональному, а его тьюринг-полнота основана на рекурсии. Поэтому задачи обработки глубоко вложенных конструкций и прочего таки встаёт. Рано или поздно. Когда ты понимаешь, что двадцати гигабайт памяти тебе уже не хватает для компиляции TensorFlow. :D C constexpr-вычислениями чуть другое. Компиляторную бомбу всё равно ещё можно сделать, но так или иначе, компилятор уже не только компилятор, но и интерпретатор, который тупо интерпретирует им самим же построенное AST. Поэтому всё становится проще, как с точки зрения программирования, так и с точки зрения компиляции. И эта часть языка (и стандарта) развивается очень активно - с constexpr-функций снимается всё больше ограничений (вспомни, какими они были в 11-ом стандарте), в двадцатом уже обещают строгий constexpr (который constexpr!), выполняющийся либо строго в компайл-тайм, либо не выполняющийся вообще.
                              Цитата JoeUser @
                              Попробуй, чисто ради эксперимента, заинициализировать свой массив (вектор) из 16192 элемента в статике числами Фибоначчи.
                              ExpandedWrap disabled
                                #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 << ' ';
                                }
                              Вообще без напряга. Там переполнения после 47-го элемента, естественно, не суть.
                                Цитата Flex Ferrum @
                                Тут "немножко" другой подход. Проблема с метапрограммированием на шаблонах в том, что "язык" шаблонов ближе к функциональному, а его тьюринг-полнота основана на рекурсии.

                                Флекс, давай отделять мух от котлет! Я понимаю, движется все. Се ля ви. Но предлагаю очень не забывать о принципе Паретто. Конечно, любые телодвижения в развитии важны! Но соотнеси их к вышеозначенному принципу! ...

                                Вопрос "что, зачем и какими силами" воспроизводится?!
                                Хорошо, если упрощает, а несли нет???!
                                А если это вааще не упало щяс???
                                  Цитата JoeUser @
                                  Вопрос "что, зачем и какими силами" воспроизводится?!
                                  Хорошо, если упрощает, а несли нет???!
                                  А если это вааще не упало щяс???

                                  Тут вот какое дело. Если для решения довольно простых задач (скажем, пресловутого конвертора строк в enum'ы) надо городить огород со сторонними утилитами - тут, эммм..., что-то не так в консерватории. В языке, в смысле. Метаклассы закрывают эту "дыру" и позволяют более гибко управлять процессом компиляции (в смысле, вмешиваться в работу компилятора) легальным способом получая нужные бенифиты. И это - здорово!

                                  Добавлено
                                  В любом случае: не попробуешь - не узнаешь. Вот я и хочу попробовать. Впрочем, уже сейчас (на этапе предварительного анализа подхода и реализации) вижу интересные вопросы, на которые в пропозале нет ответа...
                                    Цитата Qraizer @
                                    Вообще без напряга. Там переполнения после 47-го элемента, естественно, не суть.

                                    Да, а про constexpr я и забыл :blush:
                                      Вид метаклассов, близкий к финальному.
                                      ExpandedWrap disabled
                                        METACLASS_DECL(Interface)
                                        {
                                            static void GenerateDecl()
                                            {
                                                compiler.require($Interface.variables().empty(), "Interface may not contain data members");
                                         
                                                for (auto& f : $Interface.functions())
                                                {
                                                    compiler.require(!f.is_copy() && !f.is_move(), "Interface can't contain copy or move constructor");
                                                    if (!f.has_access())
                                                        f.make_public();
                                         
                                                    compiler.require(f.is_public(), "Inteface function must be public");
                                                    f.make_pure_virtual();
                                                }
                                            }
                                        };
                                         
                                        METACLASS_INST(Interface, TestIface)
                                        {
                                        public:
                                            void TestMethod1();
                                            std::string TestMethod2(int param) const;
                                        };

                                      Осталось только интерпретатор всего этого хозяйства напедалить.
                                        Цитата Flex Ferrum @
                                        Вид метаклассов, близкий к финальному.

                                        Это откуда инфа?
                                        Флекс, давай представим что метаклассы реализованы и самодостаточны. Приведи, пожалуйста, пример - где они "решают". Где реально без них содом и гомора? Чисто чтобы осознать глубину глубин.
                                          Цитата JoeUser @
                                          Это откуда инфа?

                                          Это от меня. В рамках моей реализации. :) Ну а примеры... Вот, скажем, задачка с visitor'ом:
                                          ExpandedWrap disabled
                                            // Базовый класс для visitable-классов
                                            template<typename V, typename R = void>
                                            struct VisitableBase
                                            {
                                                virtual R Visit(V* v) = 0;
                                            };
                                             
                                            template<typename V, typename R = void>
                                            $class Visitable
                                            {
                                                constexpr
                                                {
                                                     // Добавляем метод Visit в визитируемый класс
                                                     -> R Visit(V* v) override {return v->Visit(*this);}
                                             
                                                     // Добавляем наследование
                                                     $Visitable.add_parent(VisitableBase<V, R>);
                                                }
                                            };
                                             
                                            class SampleVisitor;
                                            Visitable<SampleVisitor> Foo
                                            {
                                            public:
                                            // Тут ничего лишнего не пишем
                                            };
                                             
                                            Visitable<SampleVisitor> Bar
                                            {
                                            public:
                                            // Тут ничего лишнего не пишем
                                            };
                                             
                                            // Объявляем класс-визитор
                                            class SampleVisitor
                                            {
                                            public:
                                                void Visit(const Foo&) {std::cout << "SampleVisitor::Visit(Foo)\n";}
                                                void Visit(const Bar&) {std::cout << "SampleVisitor::Visit(Bar)\n";}
                                            };


                                          В данном случае метакласс Visitable приведёт объявления классов Foo и Bar к виду, пригодному к обходу заданным визитором.
                                            Flex Ferrum, ты меня наталкиваешь на мысль, а-ля moc Qt. Сам посуди. С++ явно пока несовершенен. Берем язык сверхвысогого уровня (хотя можно и тот же C++), строим информационную модель, реализовываем ее в рамках языка программирования более низкого уровня. Профит!!!

                                            Кстати! Некое подобие сказанного уже в стадии реализации - Haxe. Понимаю, все не так и не то. Но я говорю про концепцию. ИМХО право на жысть имеет.

                                            Добавлено
                                            ЗЫ: Чего действительно в haxe не хватает - так это ... увы мета :-?
                                              Цитата JoeUser @
                                              ты меня наталкиваешь на мысль, а-ля moc Qt. Сам посуди. С++ явно пока несовершенен. Берем язык сверхвысогого уровня (хотя можно и тот же C++), строим информационную модель, реализовываем ее в рамках языка программирования более низкого уровня. Профит!!!

                                              Ну, эммм... Именно это я и нацелился написать. Утилиту, которая внутри себя интерпретирует часть C++-текста и генерирует на его основе новый текст. То есть, скажем, результатом обработки кода из этого коммента: Метаклассы в C++ (сообщение #3775986) будет:
                                              ExpandedWrap disabled
                                                class TestIface
                                                {
                                                public:
                                                    virtual void TestMethod1() = 0;
                                                    virtual std::string TestMethod2(int param) const = 0;
                                                };

                                              Но это - самые простейшие преобразования. Можно добавить наследование от некоего IObject, скажем. Или сделать так, чтобы методы были преобразованы так:
                                              ExpandedWrap disabled
                                                class TestIface
                                                {
                                                public:
                                                    virtual result_t TestMethod1() nothrow = 0;
                                                    virtual result_t TestMethod2(int param, std::string& returnVal) const nothrow = 0;
                                                };

                                              Причём, замена одного другим может быть сделана в одном лишь месте - в определении метакласса.

                                              Добавлено
                                              Или, скажем, определяешь ты метакласс Component:
                                              ExpandedWrap disabled
                                                template<typename ImplIfaces, typename RequireIfaces, typename HiddenIfaces = List<>>
                                                $class Component
                                                {
                                                // Реализацию сходу не напишу
                                                };

                                              Потом пишешь:
                                              ExpandedWrap disabled
                                                $Component<List<I1, I2>, List<I3, I4>> SomeComponent
                                                {
                                                //..
                                                };

                                              А он тебе генерирует:
                                              ExpandedWrap disabled
                                                class SomeComponent : public I1, public I2, public ComponentBase
                                                {
                                                public:
                                                    Component(I3*, I4*) {}
                                                 
                                                    enum {ComponentId = 0xffffff;}
                                                    const std::string& GetComponentName() {return "SomeComponent";}
                                                    
                                                    // Реализация
                                                };
                                              Сообщение отредактировано: Flex Ferrum -
                                                Цитата Flex Ferrum @
                                                Ну, эммм... Именно это я и нацелился написать. Утилиту, которая внутри себя интерпретирует часть C++-текста и генерирует на его основе новый текст

                                                Хм :scratch: Идея оч-оч интересная. Но не покидает ощущение де жа вю!!!

                                                Чисто про хронологии ....

                                                1993 год. Прихожу я на фирму новичком. Сам из себя такой матеро-запаскалевший, ориентированный чисто объектно. За плечами asm-либа работы с окнами в режиме CGA/EGA/VGA, даже "снег" снимал на CGA на основе обратного хода луча ... А мне нач отдела заявляет - все херня, учи Clarion. Я туда, я сюда, да как так, да я ... проиграл я ему на простейшей задаче, вчистую!!! Сел и выучил посля Clarion.

                                                Чем интересен язык? Язык шаблонов шаблонов ... шаблонов. Каркас очередного модуля собирается единожды, определяются entry сниппетов, а вот там уже дальше ад и израиль. Если честно, по IDE клариона скучаю.

                                                2018 год. Познакомился с языком шаблонов Blade. Тоже свой цымус. Понятко что, для чего, и как. Реализация приятная и красивая.

                                                Лично мое имховое резюме: я против усложнения Стандарта языка C++. Но я строго "за" за создание мета-систем генерации кода, реализации метасистем на основе паттернов проектирования. Вот как-то так :-?
                                                  Я для себя тоже такой язык нашёл - Jinja2. Даже его порт с питона на С++ сделал. Но... Мы должны пойти глубже. :)
                                                    Цитата Flex Ferrum @
                                                    Мы должны пойти глубже.

                                                    ИМХО, нужно начинать с ТУ, делать ТЗ, и только потом прогать и докать сделанное.
                                                      Цитата JoeUser @
                                                      Цитата Flex Ferrum @
                                                      Мы должны пойти глубже.

                                                      ИМХО, нужно начинать с ТУ, делать ТЗ, и только потом прогать и докать сделанное.

                                                      Так в первом посте ссылка на пропозал соответствующий. :)
                                                        Не хотел я в первый релиз Jinja2 затаскивать макросы. Но без них будет определённо тоскливо...
                                                        user posted image

                                                        А такого меташаблонного кода в реализации автопрограммиста будет в достатке...
                                                          Цитата Flex Ferrum @
                                                          в прошлом году на конфах некто Герб Саттер взорвал C++-общественность докладами про метаклассы

                                                          Это те метаклассы, которые, как минимум в CLOS с 1994-го? Да и в Qt они вроде давно есть. В чём взрыв-то?
                                                            Цитата korvin @
                                                            Это те метаклассы, которые, как минимум в CLOS с 1994-го? Да и в Qt они вроде давно есть. В чём взрыв-то?

                                                            Про CLOS не в курсе, а в Qt я про такое не слышал.
                                                              В общем, оно начинает потихоньку дышать:
                                                              Для:
                                                              ExpandedWrap disabled
                                                                METACLASS_DECL(Interface)
                                                                {
                                                                    static void GenerateDecl()
                                                                    {
                                                                        compiler.message("Hello world from metaprogrammer!");
                                                                        compiler.require($Interface.variables().empty(), "Interface may not contain data members");
                                                                 
                                                                        for (auto& f : $Interface.functions())
                                                                        {
                                                                            compiler.require(f.is_implicit() || (!f.is_copy_ctor() && !f.is_move_ctor()), "Interface can't contain copy or move constructor");
                                                                            if (!f.has_access())
                                                                                f.make_public();
                                                                 
                                                                            compiler.require(f.is_public(), "Inteface function must be public");
                                                                            f.make_pure_virtual();
                                                                        }
                                                                    }
                                                                 
                                                                 
                                                                    static int GenerateImpl()
                                                                    {
                                                                        int a = 0;
                                                                        return a + 2;
                                                                    }
                                                                };
                                                                 
                                                                METACLASS_INST(Interface, TestIface)
                                                                {
                                                                public:
                                                                    void TestMethod1();
                                                                    std::string TestMethod2(int param) const;
                                                                };
                                                                 
                                                                METACLASS_INST(Interface, BadTestIface)
                                                                {
                                                                public:
                                                                    void TestMethod1();
                                                                    std::string TestMethod2(int param) const;
                                                                 
                                                                private:
                                                                    int m_val;
                                                                };

                                                              На консоли можно увидеть:
                                                              ExpandedWrap disabled
                                                                ### Trying to call 'meta::CompilerImpl::message/void message(const char *msg)'
                                                                ###### Hello world from metaprogrammer!
                                                                ### Trying to call 'meta::CompilerImpl::require/void require(bool, const char *message)'
                                                                ...
                                                                ### Trying to call 'meta::CompilerImpl::message/void message(const char *msg)'
                                                                ###### Hello world from metaprogrammer!
                                                                ### Trying to call 'meta::CompilerImpl::require/void require(bool, const char *message)'
                                                                ###### Interface may not contain data members


                                                              Добавлено
                                                              Теперь борюсь с range-for.
                                                                Flex Ferrum, глянь плс бегло эту реализацию макросов. Просто дай оценку. Интересно :)
                                                                  Ну куда-то в эту сторону, да. Только для нативного C++.
                                                                    Цитата Flex Ferrum @
                                                                    Только для нативного C++.

                                                                    Да, Haxe - считай метаязык. Потом из него можно C++ код получить, как вариант.
                                                                      Цитата Flex Ferrum @
                                                                      в Qt я про такое не слышал.

                                                                      Как-то так, но в подробности я не вдавался.
                                                                        Цитата korvin @
                                                                        Цитата Flex Ferrum @
                                                                        в Qt я про такое не слышал.

                                                                        Как-то так, но в подробности я не вдавался.

                                                                        Не, это совсем другое. В Qt moc экспозит метаинформацию в райнтайм. Метаклассы же, о которых здесь идёт речь, работают с метаинформацией в compile-time. И могут модифицировать AST.
                                                                          Итак, первые шаги автопрограммиста по полю метаклассов. Сегодня таки получился end-to-end-тест. Итак.
                                                                          Объявление метакласса:
                                                                          ExpandedWrap disabled
                                                                            METACLASS_DECL(Interface)
                                                                            {
                                                                                static void GenerateDecl()
                                                                                {
                                                                                    compiler.message("Hello world from metaprogrammer!");
                                                                                    compiler.require($Interface.variables().empty(), "Interface may not contain data members");
                                                                             
                                                                                    for (auto& f : $Interface.functions())
                                                                                    {
                                                                                        compiler.require(f.is_public(), "Inteface function must be public");
                                                                                        f.make_pure_virtual();
                                                                                    }
                                                                                }
                                                                            };

                                                                          Его инстанс:
                                                                          ExpandedWrap disabled
                                                                            METACLASS_INST(Interface, TestIface)
                                                                            {
                                                                            public:
                                                                                void TestMethod1();
                                                                                std::string TestMethod2(int param) const;
                                                                            };

                                                                          Что генерирует автопрограммист в соответствии с определением того и другого:
                                                                          ExpandedWrap disabled
                                                                            class TestIface {
                                                                            public:
                                                                              virtual void TestMethod1() = 0;
                                                                              virtual std::string TestMethod2(int param) const = 0;
                                                                             
                                                                            protected:
                                                                            private:
                                                                            };

                                                                          Ну и как это потом используется в коде:
                                                                          ExpandedWrap disabled
                                                                            class TestIfaceImpl : public TestIface
                                                                            {
                                                                                // TestIface interface
                                                                            public:
                                                                                void TestMethod1() override;
                                                                                std::string TestMethod2(int param) const override;
                                                                            };
                                                                             
                                                                            void TestIfaceImpl::TestMethod1()
                                                                            {
                                                                            }
                                                                             
                                                                            std::string TestIfaceImpl::TestMethod2(int) const
                                                                            {
                                                                                return "Hello World!";
                                                                            }

                                                                          Всё настолько прозрачно, насколько это вообще возможно.
                                                                            Продолжаем экзерсизы перед выступлением на CoreHard++. Потихоньку привожу в чувство такой вот код:
                                                                            user posted image
                                                                              В общем, в итоге оно заработало. Итак, что мы имеем.

                                                                              1. Два метакласса, описывающие, что именно нужно добавить в класс, чтобы он был сериализуемым с помощью буста:
                                                                              ExpandedWrap disabled
                                                                                $_metaclass(BoostSerializable)
                                                                                {
                                                                                    static void GenerateDecl()
                                                                                    {
                                                                                        $_inject(public) [&, name="serialize"](const auto& ar, unsigned int ver) -> void
                                                                                        {
                                                                                            $_constexpr for (auto& v : $BoostSerializable.variables())
                                                                                                $_inject(_) ar & $_v(v.name());
                                                                                        };
                                                                                    }
                                                                                };
                                                                                 
                                                                                $_metaclass(BoostSerializableSplitted)
                                                                                {
                                                                                    static void GenerateDecl()
                                                                                    {
                                                                                        $_inject(public) [&, name="load"](const auto& ar, unsigned int ver) -> void
                                                                                        {
                                                                                            $_constexpr for (auto& v : $BoostSerializableSplitted.variables())
                                                                                                $_inject(_) ar << $_v(v.name());
                                                                                        };
                                                                                 
                                                                                        $_inject(public) [&, name="save"](const auto& ar, unsigned int ver) -> void
                                                                                        {
                                                                                            $_constexpr for (auto& v : $BoostSerializableSplitted.variables())
                                                                                                $_inject(_) ar >> $_v(v.name());
                                                                                        };
                                                                                 
                                                                                        $_inject(public) [&, name="serialize"](auto& ar, const unsigned int ver) -> void
                                                                                        {
                                                                                            boost::serialization::split_member(ar, $_str(*this), ver);
                                                                                        };
                                                                                    }
                                                                                };


                                                                              Дальше объявляем классы, как обычные структуры. Ну... Почти обычные:
                                                                              ExpandedWrap disabled
                                                                                $_struct(TestStruct, BoostSerializable)
                                                                                {
                                                                                    int a;
                                                                                    std::string b;
                                                                                };
                                                                                 
                                                                                $_struct(TestStruct1, BoostSerializableSplitted)
                                                                                {
                                                                                    int a;
                                                                                    std::string b;
                                                                                };


                                                                              Метапрограммист делает "вжух" и на выходе получается:
                                                                              ExpandedWrap disabled
                                                                                class TestStruct {
                                                                                public:
                                                                                  template <typename T0> void serialize(const T0 &ar, unsigned int ver) {
                                                                                    ar &a;
                                                                                    ar &b;
                                                                                    ;
                                                                                  }
                                                                                  int a;
                                                                                  std::string b;
                                                                                 
                                                                                protected:
                                                                                private:
                                                                                };
                                                                                 
                                                                                class TestStruct1 {
                                                                                public:
                                                                                  template <typename T0> void load(const T0 &ar, unsigned int ver) {
                                                                                    ar << a;
                                                                                    ar << b;
                                                                                    ;
                                                                                  }
                                                                                  template <typename T0> void save(const T0 &ar, unsigned int ver) {
                                                                                    ar >> a;
                                                                                    ar >> b;
                                                                                    ;
                                                                                  }
                                                                                  template <typename T0> void serialize(T0 &ar, const unsigned int ver) {
                                                                                    boost::serialization::split_member(ar, *this, ver);
                                                                                  }
                                                                                  int a;
                                                                                  std::string b;
                                                                                 
                                                                                protected:
                                                                                private:
                                                                                };


                                                                              Добавлено
                                                                              Такое вот средство копипасты. Причём, замечу, там ошибка в объявлении типа параметра ar. Изменяем пару мест в объявлении метаклассов:
                                                                              ExpandedWrap disabled
                                                                                $_metaclass(BoostSerializable)
                                                                                {
                                                                                    static void GenerateDecl()
                                                                                    {
                                                                                        $_inject(public) [&, name="serialize"](auto& ar, unsigned int ver) -> void
                                                                                        {
                                                                                            $_constexpr for (auto& v : $BoostSerializable.variables())
                                                                                                $_inject(_) ar & $_v(v.name());
                                                                                        };
                                                                                    }
                                                                                };
                                                                                 
                                                                                $_metaclass(BoostSerializableSplitted)
                                                                                {
                                                                                    static void GenerateDecl()
                                                                                    {
                                                                                        $_inject(public) [&, name="load"](auto& ar, unsigned int ver) -> void
                                                                                        {
                                                                                            $_constexpr for (auto& v : $BoostSerializableSplitted.variables())
                                                                                                $_inject(_) ar << $_v(v.name());
                                                                                        };
                                                                                 
                                                                                        $_inject(public) [&, name="save"](auto& ar, unsigned int ver) -> void
                                                                                        {
                                                                                            $_constexpr for (auto& v : $BoostSerializableSplitted.variables())
                                                                                                $_inject(_) ar >> $_v(v.name());
                                                                                        };
                                                                                 
                                                                                        $_inject(public) [&, name="serialize"](auto& ar, const unsigned int ver) -> void
                                                                                        {
                                                                                            boost::serialization::split_member(ar, $_str(*this), ver);
                                                                                        };
                                                                                    }
                                                                                };


                                                                              Вжух!
                                                                              ExpandedWrap disabled
                                                                                class TestStruct {
                                                                                public:
                                                                                  template <typename T0> void serialize(T0 &ar, unsigned int ver) {
                                                                                    ar &a;
                                                                                    ar &b;
                                                                                    ;
                                                                                  }
                                                                                  int a;
                                                                                  std::string b;
                                                                                 
                                                                                protected:
                                                                                private:
                                                                                };
                                                                                 
                                                                                class TestStruct1 {
                                                                                public:
                                                                                  template <typename T0> void load(T0 &ar, unsigned int ver) {
                                                                                    ar << a;
                                                                                    ar << b;
                                                                                    ;
                                                                                  }
                                                                                  template <typename T0> void save(T0 &ar, unsigned int ver) {
                                                                                    ar >> a;
                                                                                    ar >> b;
                                                                                    ;
                                                                                  }
                                                                                  template <typename T0> void serialize(T0 &ar, const unsigned int ver) {
                                                                                    boost::serialization::split_member(ar, *this, ver);
                                                                                  }
                                                                                  int a;
                                                                                  std::string b;
                                                                                 
                                                                                protected:
                                                                                private:
                                                                                };
                                                                                С++ Russia на подходе, тулза развивается. Приросла таким вот функционалом:

                                                                                1. Делаем метакласс, который генерирует visitor для заданного набора типов:
                                                                                user posted image

                                                                                2. Делаем инстанс этого метакласса. Всё просто:
                                                                                user posted image

                                                                                3. И получаем на выходе хорошенький такой классец-визитор с нужными методами:
                                                                                user posted image

                                                                                4. Теперь мы лёгким движением руки добавляем к инстансу ещё метакласс Interface:
                                                                                user posted image

                                                                                5. И инстанс превращается... Превращается инстанс... В элегантный интерфейсный класс визитора:
                                                                                user posted image

                                                                                Вуаля!
                                                                                  А по иерархиям классов можно ходить? Чтоб получить src...dst в отношении предок...потомок.

                                                                                  Добавлено
                                                                                  Хотя... а вдруг один ко многим...
                                                                                    Цитата Qraizer @
                                                                                    А по иерархиям классов можно ходить? Чтоб получить src...dst в отношении предок...потомок.

                                                                                    Можно (в идеале). Текущая реализация этого пока не поддерживает.

                                                                                    Добавлено
                                                                                    О, да, кстати. Твои мультиметоды в этом стиле реализуются более чем тривиально. :)
                                                                                      Ну вот я и попытался. Пока можно лишь частично, генерируя visit-методы. Также к сожалению не получилось разделить объявление и определение visit-метода.
                                                                                        Цитата Qraizer @
                                                                                        Пока можно лишь частично, генерируя visit-методы.

                                                                                        Угу. А ты прям на автопрограммисте играешься?

                                                                                        Добавлено
                                                                                        Цитата Qraizer @
                                                                                        Также к сожалению не получилось разделить объявление и определение visit-метода.

                                                                                        Будет позже.
                                                                                          Не. Просто пробую хотя бы представить себе, как бы это выглядело. Нужно генерить объявления
                                                                                          ExpandedWrap disabled
                                                                                            virtual typename Proto::ret_type Accept(typename Proto::template getArg<PList>::type &a);
                                                                                          в нужных классах, определения
                                                                                          ExpandedWrap disabled
                                                                                            typename Proto::ret_type Concrete::Accept(typename Proto::template getArg<PList>::type &a){ return static_cast<MultiMethods_Visitor::details::Acceptor<Concrete, typename Proto::ret_type>&>(a).Accept(this); }
                                                                                          и списки
                                                                                          ExpandedWrap disabled
                                                                                            typedef MultiMethods_Visitor::MakeTList</*...*/> PList;
                                                                                          в нужных местах единиц трансляции. Пока не вижу явных возможностей для реализации.

                                                                                          Добавлено
                                                                                          Для RTTI нужно только последнее.

                                                                                          Добавлено
                                                                                          Впрочем, имея последнее, первое два, наверное, реально пропустить через итераторы.
                                                                                          Сообщение отредактировано: Qraizer -
                                                                                            А, понял. Смотри, как это может выглядеть:

                                                                                            ExpandedWrap disabled
                                                                                              template<typename ... Types>
                                                                                              inline void Multimethod(meta::ClassInfo dst, const meta::ClassInfo& src)
                                                                                              {
                                                                                                  meta::ClassInfo dispIface;
                                                                                               
                                                                                                  // Add extra 'Visit' methods to dst dependent on specific type from 'Types'
                                                                                                  for (auto& t : t_$(Types ...))
                                                                                                      $_inject_v(dispIface, public) [name="Dispatch", is_pure=true](const $_t(t)* obj) {}
                                                                                               
                                                                                                  $_inject_v(dst, public) dispIface;
                                                                                               
                                                                                                  meta::ClassInfo dispImpl;
                                                                                                  dispImpl.add_base(dispIface);
                                                                                                  auto tplParam = dispImpl.add_template_type_param("T");
                                                                                               
                                                                                                  $_inject_v(dispImpl, private) { const $_t(tplParam)* m_leftObj;}
                                                                                               
                                                                                                  for (auto& t : t_$(Types ...))
                                                                                                  {
                                                                                                      $_inject_v(dispIface, public) [name="Dispatch"](const $_t(t)* obj) {$_str(Call)($_str(m_leftObj), obj);};
                                                                                                      $_inject_v(dst) [name="Call"](auto* lhs, auto* rhs) {
                                                                                                            $_str(lhs)->$_mem("Invoke")();
                                                                                                      };
                                                                                                      // ... и всё в том же духе
                                                                                                   }
                                                                                               
                                                                                                  $_inject_v(dst, public) dispImpl;
                                                                                              }
                                                                                              Парни, я осознаю, что для малоподготовленного (ну как для мня) - вышеприведенное есть лютая дичь.
                                                                                              Приведите примеры, плс, получаемого профита ... ну или обозначьте границы, когда он действительно может появиться.
                                                                                                Цитата JoeUser @
                                                                                                Парни, я осознаю, что для малоподготовленного (ну как для мня) - вышеприведенное есть лютая дичь.

                                                                                                Если делать это "старым" способом (и заглянуть под капот) - дичь будет не менее лютая.
                                                                                                Цитата JoeUser @
                                                                                                Приведите примеры, плс, получаемого профита ...

                                                                                                Получаемый профит в том, что производимые манипуляции с типами - более явные и (главное) в императивном, а не функциональном стиле. Попробуй реализовать тот же визитор на "обычном" C++ - и поймёшь, о чём речь. :)
                                                                                                  Если я правильно распознал происходящее, что совсем не факт, то это не совсем то. Смотри.
                                                                                                  Ты в вариадик Types интегрируешь интерфейс dispIface. Это архитектурная ошибка, т.к. создаёт зависимость классов пользователя от класса абстрактного диспетчера. Это плохо, т.к. диспетчер создаёт интерфейс визитора в процессе своего инстанцирования, поэтому эта зависимость ведёт к циклу. Вместо этого подразумевается, что визит-метод документируется базовым классом в иерархии, и производные его лишь перекрывают, диспетчер же просто зовёт метод базового класса, а не какого-то интерфейса.
                                                                                                  Далее. Свои внутренние классы акцепторов диспетчер генерирует сам, метаклассы ему для этого не нужны. Поэтому цикл по вариадику по сути лишний. Его следовало бы делать по списку классов в иерархии. Вот его бы получить... К тому же это решение ограничено двупараметрическим мультиметодом, тогда как у меня ещё в C++03 были обобщённые n-параметрические. По списку параметров диспетчер тоже итерируется классически.
                                                                                                  Сообщение отредактировано: Qraizer -
                                                                                                    Ну, я пытался на ходу что-то путное выдумать.
                                                                                                    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                                                                    0 пользователей:


                                                                                                    Рейтинг@Mail.ru
                                                                                                    [ Script execution time: 0,1844 ]   [ 19 queries used ]   [ Generated: 29.03.24, 04:53 GMT ]