На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
Модераторы: Qraizer
Страницы: (4) [1] 2 3 ... Последняя » все  ( Перейти к последнему сообщению )  
> Метаклассы в 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 @
                                Тут "немножко" другой подход. Проблема с метапрограммированием на шаблонах в том, что "язык" шаблонов ближе к функциональному, а его тьюринг-полнота основана на рекурсии.

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

                                Вопрос "что, зачем и какими силами" воспроизводится?!
                                Хорошо, если упрощает, а несли нет???!
                                А если это вааще не упало щяс???
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0908 ]   [ 19 queries used ]   [ Generated: 28.03.24, 20:08 GMT ]