Menu

Обсуждение Рефлексии

Anonymous
2007-02-22
2013-04-18
  • Anonymous

    Anonymous - 2007-02-22

    Основа:

    Невероятно, но факт! Не константные значения в компайл-тайм!
    http://rsdn.ru/Forum/Message.aspx?mid=2337951&only=1

    [Trick] Автоматическое определение смещения
    http://rsdn.ru/Forum/Message.aspx?mid=2340699&only=1

    Re: a.k.a. Compile-Tme Reflection вручную №2
    http://rsdn.ru/Forum/Message.aspx?mid=1861194&only=1

    Требования: (Будут корректироваться)
    http://cpp-object.svn.sourceforge.net/viewvc/cpp-object/doc/Requirements%20-%20Eng.txt?view=markup

     
    • remark

      remark - 2007-02-27

      Привет.

      Я так понял это последний вариант:

      DEF_CLASS0(a)
      public:
          // Methods
          void f()
          {
              i = 1;
          }

          // Variables
          int i;

          // Static Methods
          static void sf()
          {
              si = 1;
          }

          // Static Variables
          static int si;

          // Methods
          DECL_METHODS0(void (this_type::*)())   

          // Variables
          DECL_VARIABLES0(int this_type::*)

          // Static Methods
          DECL_STATIC_METHODS0(void (*)())

          // Static Variables
          DECL_STATIC_VARIABLES0(int*)
      };

      int a::si;

      DEF_METHODS0(a, f)
      DEF_VARIABLES0(a, i)
      DEF_STATIC_METHODS0(a, sf)
      DEF_STATIC_VARIABLES0(a, si)

      Что не очень нравится в таком варианте имхо:
      1. Интрузивность для описываемого класса. По разным причинам это может быть невозможно. Вплоть до того, что класс описан в хидере сторонней библиотеки.
      2. Необходимость дублировать макросы с описанием члена 2 раза - внутри класса и вне класса.
      3. Необходимость дублировать 2 раза описывать объявления всех членов - вначале просто как членов класса, второй раз внутри макросов.

      Как я предлагаю:

      Вначале описывается сам класс как есть:
      class a
      public:
          // Methods
          void f()
          {
              i = 1;
          }

          // Variables
          int i;

          // Static Methods
          static void sf()
          {
              si = 1;
          }

          // Static Variables
          static int si;
      };

      Потом отдельно описание класса:

      #define CLASS_A(a,
        (void(), f), // methods
        (int, i), // variables
        (void(), sf), // static methods
        (int, si) // static var
      )

      Далее с помощью вспомогательных макросов из библиотеки можно сгенерировать нужные вещи:

      MAKE_RT_REFLECTION(CLASS_A) // Этот макрос генерит функции/класс, необходимые для ран-тайм рефлексии

      MAKE_СT_REFLECTION(CLASS_A) // Этот макрос генерит функции/класс, необходимые для компайл-тайм рефлексии

      MAKE_SERIALIZATION(CLASS_A) // Этот макрос генерит функции для сериализации/десериализации

      MAKE_SERIALIZATION_XML(CLASS_A) // Этот макрос генерит функции для XML сериализации/десериализации

      Т.о. пользователь как бы "набирает" нужные ему функциональности, а не нужные не использует. Потом этот набор макросов можно будет легко расширять как внутри библиотеки, там и пользователю при желании.
      Т.о. получается ран-тайм, компайл-тайм и препроцессинг-тайм рефлексия. Имхо это будет очень гибко и мощно.

      Далее нужно сделать макрос, который будет генерить само описание класс, т.е.

      MAKE_CLASS_DEF(CLASS_A)

      Раскрывается в:

      struct a
      {
          void f();
          int i;
          static void sf();
          static int si;
      };

      Т.о. пользователю даётся возможность использовать библиотеку неинтрузивно, когда описание класса уже имеется и менять его нельзя, или в класс хочется добавить некие специфические члены, т.е. написать описание класса руками. И в тоже время даёт возможность обходится вообще без дублирования описания класса и лишней работы. Т.е. для простеньких структур просто пишется описание:

      #define CLASS_PERSON(person,
        (), // methods
        (int, id)(int, age)(string, name), // variables
        (), // static methods
        () // static var
      )

      И далее генерируется описание класса и нужные функциональности:

      MAKE_CLASS_DEF(CLASS_PERSON)
      MAKE_SERIALIZATION(CLASS_PERSON)

      Единственное, что придётся наверное сделать - в класс добавлять некий макрос, если пользователь хочет, что бы библиотека могла получать доступ к приватным членам класса:

      class a
      private: // <-------------------------
          int i;
         
          CPP_OBJECT_ACCESS(); // Тут в класс должен добавляться некий хук, через который библиотека сможет получать доступ к членам... ну или просто класс библиотеки объявляться френдом
      };

      По поводу собственно рефлесии.
      Я считаю, что нужно вынести функции доступа из класс в свободные функции. Т.к., во-первых, зачем их добавлять в класс? только могут быть конфликты с именами, которые уже есть в классе. Во-вторых, опять же проблема с интрузивностью.

      Т.е. не так:

      t.*get_method<T, 0>::value)();

      А как-то так:

      get_method<0>(t)();

      Должно быть:
      Для ран-тайм рефлексии: доступ по имени/номеру
      Для компайл-тайм: доступ по тэгу/номеру
      Для препроцессинг тайм рефлекии - наверное надо сделать некие вспомогательные макросы типа: FOREACH_VAR(body), FOREACH_FUNC(body), GET_VAR_NAME(n), GET_FUNC_NAME(n) и т.д.
      И для всех вариантов - кол-во членов.

      Вобщем я как-то так себе это всё вижу...

       
      • Anonymous

        Anonymous - 2007-03-02

        Я не считаю мой вариант верхом совершенства, это так для разминки я его добавил :)

        Конечно все должно быть неинтрузивно, т.к. некоторые вещи менять нельзя.

        Насчет приватных членов класса , то макрос дополнительный это будет достаточно, т.к. это не часто требуется.

        Интересную тему ты затронул о рефлексии во время препроцессинга :))

        Кстати, а как проверять, что определение класса соответсвует действительности ?

         
        • Sergey Shandar

          Sergey Shandar - 2007-03-04

          >Кстати, а как проверять, что определение класса соответсвует действительности ?

          Лучще не проверять, а генерировать классы и CTTI (compile time type information) из какого нибудь другого языка (например XML). Иначе поростете дефайнами.

           
          • remark

            remark - 2007-03-05

            >>Кстати, а как проверять, что определение класса соответсвует действительности ?

            >Лучще не проверять, а генерировать классы и CTTI (compile time type information) из какого нибудь другого языка (например XML). Иначе поростете дефайнами.

            А как проверять, что XML соответствует описанию класса? :)))
            Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.

             
            • Anonymous

              Anonymous - 2007-03-05

              > А как проверять, что XML соответствует описанию класса? :)))
              Сам интерфейс класса описан в XML, то что там то и есть класс ;)

              > Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.
              А его не должно быть для этого случая ;)

               
            • Maxim Yanchenko

              Maxim Yanchenko - 2008-07-08

              > А как проверять, что XML соответствует описанию класса? :)))
              > Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.

              Можно просто сгенерировать во временный файл и потом сравнить с тем, что было, если есть изменения - скопировать на его место (автоматом обновится время файла и make его подхватит).

               
        • remark

          remark - 2007-03-05

          >Интересную тему ты затронул о рефлексии во время препроцессинга :))

          Да, это интересно - можно сгенерировать, например, прозрачный прокси...

          >Кстати, а как проверять, что определение класса соответсвует действительности ?

          В общем случае никак.
          Естественно будет проверятся, что указанные члены есть в классе. А по поводу отсутствующих членов... можно, конечно, придумать какие-то эвристики, типа проверки полного размера класса и суммы размеров указанных членов, но тут тогда надо будет требовать, что бы члены указывали в порядке следования в описании класса. Или можно создать объект, потом сериализовать его, потом поменять всю память под объектом, и десериализовать его и проверить, что во время десериализации вся память поменялась... но имхо не стоит это делать....
          Идеальный вариант - когда само объявление класса генерируется препроцессором из описания, тут не будет проблемы с расхождением....

           
          • Anonymous

            Anonymous - 2007-03-05

            Ну тут уже XML попроще будет чем препроцессор С++ :-)

             
    • remark

      remark - 2007-02-28

      Ну и я думаю, надо бы, конечно, в первую голову поглядеть другие аналогичные библиотеки. Поглядеть что и как там сделано. Вот на пример та на мейлинг-листе boost'а, которую я присылал. Наверное ещё есть аналогичные.
      Вот, например, ещё библиотека eao197:
      http://eao197.narod.ru/objessty/index.htm
      Ну она правда очень тяжёлая.

       
    • remark

      remark - 2007-03-26

      Надо, наверное, сделать какую-то proof-of-concept версию, что бы было от чего плясать и на чём более конкретные детали обсуждать.

      Ещё есть одна занятная фича, которая могла бы имхо повысить возможности библиотеки. Я как-то применял такую - достаточно удобно - возможность связывать с членами класса произвольные теэги.

      Поясню на примере. Я применял в таком контексте - есть описание неких статусов в неком протоколе. Мы хотим связать с каждым статусом его код в неком другом протоколе, его читаемое название для вывода пользователю и некий флаг. Это выглядит примерно так:

      struct status_tag_t
      {
        string name;
        int code;
        bool some_flag;

        status_tag_t(char const* name, int code, bool some_flag);
      };

      DEFINITION(status, status_tag_t // указываем, тэг какого типа мы будем связывать
        (status_one, ("Status One", 100, true))
        (status_two, ("Status Two", 200, true))
        (status_three, ("Status Three", 300, false))
      )

      И далее в ран-тайм можно получать доступ к этим тэгам:

      void f(status s)
      {
        if (s.get_tag().some_flag)
          ...
        AfxMessageBox("message status " + s.get_tag().name);
      }

      Я делал это для enum'ов, но я думаю для членов класса тоже можно. Например что бы с сущностями программы связать читаемые имена и далее можно сделать автоматическкую генерацию диалогов для отображения/редактирования сущностей.

      Соотв. в компайл-тайм можно связывать такое:

      template<bool f, int i> struct tag_t
      {
          static bool const ff = f;
          static int const ii = i;
      };

      DEFINITION(person
        (age, tag_t<true, 1>) // указываем просто тип, который хотим связать
        (name, tag_t<true, 2>) // указываем просто тип, который хотим связать
        (second_name, tag_t<false, 3>) // указываем просто тип, который хотим связать
      )

       
      • Anonymous

        Anonymous - 2007-07-28

        Вопрос что будем считать за POC ?
        Мне кажется для начала надо подумать как это должно выглядеть чтобы было удобно со всех сторон, а потом уже пытаться думать как мы к этому придем.

         

Log in to post a comment.