Что не очень нравится в таком варианте имхо:
1. Интрузивность для описываемого класса. По разным причинам это может быть невозможно. Вплоть до того, что класс описан в хидере сторонней библиотеки.
2. Необходимость дублировать макросы с описанием члена 2 раза - внутри класса и вне класса.
3. Необходимость дублировать 2 раза описывать объявления всех членов - вначале просто как членов класса, второй раз внутри макросов.
Как я предлагаю:
Вначале описывается сам класс как есть:
class a
public:
// Methods
void f()
{
i = 1;
}
Далее с помощью вспомогательных макросов из библиотеки можно сгенерировать нужные вещи:
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;
};
Т.о. пользователю даётся возможность использовать библиотеку неинтрузивно, когда описание класса уже имеется и менять его нельзя, или в класс хочется добавить некие специфические члены, т.е. написать описание класса руками. И в тоже время даёт возможность обходится вообще без дублирования описания класса и лишней работы. Т.е. для простеньких структур просто пишется описание:
Единственное, что придётся наверное сделать - в класс добавлять некий макрос, если пользователь хочет, что бы библиотека могла получать доступ к приватным членам класса:
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) и т.д.
И для всех вариантов - кол-во членов.
Вобщем я как-то так себе это всё вижу...
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2007-03-02
Я не считаю мой вариант верхом совершенства, это так для разминки я его добавил :)
Конечно все должно быть неинтрузивно, т.к. некоторые вещи менять нельзя.
Насчет приватных членов класса , то макрос дополнительный это будет достаточно, т.к. это не часто требуется.
Интересную тему ты затронул о рефлексии во время препроцессинга :))
Кстати, а как проверять, что определение класса соответсвует действительности ?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
>Кстати, а как проверять, что определение класса соответсвует действительности ?
Лучще не проверять, а генерировать классы и CTTI (compile time type information) из какого нибудь другого языка (например XML). Иначе поростете дефайнами.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
>>Кстати, а как проверять, что определение класса соответсвует действительности ?
>Лучще не проверять, а генерировать классы и CTTI (compile time type information) из какого нибудь другого языка (например XML). Иначе поростете дефайнами.
А как проверять, что XML соответствует описанию класса? :)))
Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2007-03-05
> А как проверять, что XML соответствует описанию класса? :)))
Сам интерфейс класса описан в XML, то что там то и есть класс ;)
> Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.
А его не должно быть для этого случая ;)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
> А как проверять, что XML соответствует описанию класса? :)))
> Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.
Можно просто сгенерировать во временный файл и потом сравнить с тем, что было, если есть изменения - скопировать на его место (автоматом обновится время файла и make его подхватит).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
>Интересную тему ты затронул о рефлексии во время препроцессинга :))
Да, это интересно - можно сгенерировать, например, прозрачный прокси...
>Кстати, а как проверять, что определение класса соответсвует действительности ?
В общем случае никак.
Естественно будет проверятся, что указанные члены есть в классе. А по поводу отсутствующих членов... можно, конечно, придумать какие-то эвристики, типа проверки полного размера класса и суммы размеров указанных членов, но тут тогда надо будет требовать, что бы члены указывали в порядке следования в описании класса. Или можно создать объект, потом сериализовать его, потом поменять всю память под объектом, и десериализовать его и проверить, что во время десериализации вся память поменялась... но имхо не стоит это делать....
Идеальный вариант - когда само объявление класса генерируется препроцессором из описания, тут не будет проблемы с расхождением....
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2007-03-05
Ну тут уже XML попроще будет чем препроцессор С++ :-)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Ну и я думаю, надо бы, конечно, в первую голову поглядеть другие аналогичные библиотеки. Поглядеть что и как там сделано. Вот на пример та на мейлинг-листе boost'а, которую я присылал. Наверное ещё есть аналогичные.
Вот, например, ещё библиотека eao197: http://eao197.narod.ru/objessty/index.htm
Ну она правда очень тяжёлая.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Надо, наверное, сделать какую-то 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);
};
И далее в ран-тайм можно получать доступ к этим тэгам:
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>) // указываем просто тип, который хотим связать
)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2007-07-28
Вопрос что будем считать за POC ?
Мне кажется для начала надо подумать как это должно выглядеть чтобы было удобно со всех сторон, а потом уже пытаться думать как мы к этому придем.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Основа:
Невероятно, но факт! Не константные значения в компайл-тайм!
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
Привет.
Я так понял это последний вариант:
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) и т.д.
И для всех вариантов - кол-во членов.
Вобщем я как-то так себе это всё вижу...
Я не считаю мой вариант верхом совершенства, это так для разминки я его добавил :)
Конечно все должно быть неинтрузивно, т.к. некоторые вещи менять нельзя.
Насчет приватных членов класса , то макрос дополнительный это будет достаточно, т.к. это не часто требуется.
Интересную тему ты затронул о рефлексии во время препроцессинга :))
Кстати, а как проверять, что определение класса соответсвует действительности ?
>Кстати, а как проверять, что определение класса соответсвует действительности ?
Лучще не проверять, а генерировать классы и CTTI (compile time type information) из какого нибудь другого языка (например XML). Иначе поростете дефайнами.
>>Кстати, а как проверять, что определение класса соответсвует действительности ?
>Лучще не проверять, а генерировать классы и CTTI (compile time type information) из какого нибудь другого языка (например XML). Иначе поростете дефайнами.
А как проверять, что XML соответствует описанию класса? :)))
Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.
> А как проверять, что XML соответствует описанию класса? :)))
Сам интерфейс класса описан в XML, то что там то и есть класс ;)
> Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.
А его не должно быть для этого случая ;)
> А как проверять, что XML соответствует описанию класса? :)))
> Описание самого класса нельзя генерировать (по крайней мере всегда), т.к. оно уже может быть.
Можно просто сгенерировать во временный файл и потом сравнить с тем, что было, если есть изменения - скопировать на его место (автоматом обновится время файла и make его подхватит).
>Интересную тему ты затронул о рефлексии во время препроцессинга :))
Да, это интересно - можно сгенерировать, например, прозрачный прокси...
>Кстати, а как проверять, что определение класса соответсвует действительности ?
В общем случае никак.
Естественно будет проверятся, что указанные члены есть в классе. А по поводу отсутствующих членов... можно, конечно, придумать какие-то эвристики, типа проверки полного размера класса и суммы размеров указанных членов, но тут тогда надо будет требовать, что бы члены указывали в порядке следования в описании класса. Или можно создать объект, потом сериализовать его, потом поменять всю память под объектом, и десериализовать его и проверить, что во время десериализации вся память поменялась... но имхо не стоит это делать....
Идеальный вариант - когда само объявление класса генерируется препроцессором из описания, тут не будет проблемы с расхождением....
Ну тут уже XML попроще будет чем препроцессор С++ :-)
Ну и я думаю, надо бы, конечно, в первую голову поглядеть другие аналогичные библиотеки. Поглядеть что и как там сделано. Вот на пример та на мейлинг-листе boost'а, которую я присылал. Наверное ещё есть аналогичные.
Вот, например, ещё библиотека eao197:
http://eao197.narod.ru/objessty/index.htm
Ну она правда очень тяжёлая.
Надо, наверное, сделать какую-то 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>) // указываем просто тип, который хотим связать
)
Вопрос что будем считать за POC ?
Мне кажется для начала надо подумать как это должно выглядеть чтобы было удобно со всех сторон, а потом уже пытаться думать как мы к этому придем.