Здравствуйте, szag, Вы писали:
S>S>#define FIELD(TypeID, RealType, name, captionID, ...) \
S>class RealType ## _ ## name : public ORMfield<TypeID, RealType> \
S>{ \
S>public: \
S> explicit RealType ## _ ## name () : ORMfield(#name , captionID) \
S> { \
S> InitValue(FieldValue); \
S> } \
S> \
S> // куча кода опущена \
S> \
S> std::size_t GetSize() const \
S> { \
S> return sizeof( RealType ## _ ## name ) + \
S> MyAlign<RealType ## _ ## name>::Result; \
S> } \
S> \
S> __VA_ARGS__ \
S> \
S>}; \
S> \
S>RealType ## _ ## name name;
S>
Вот! Наконец-то попёрла конкретика!
Только всё равно не понятно, что такое TypeID и зачем он нужен.
Ещё не совсем мне понятно, зачем нужно чтобы на каждое поле был свой класс? Из-за инициализатора только?
IMHO, можно так не делать, а инициализаторы передавать в конструкторе таблицы.
Но я не про это.
Во-первых, даже с классом на каждое поле, конструкцию можно упростить.
Типа написать шаблон
template<typename MDT, typename TypeID, typename TRealType>
class ORMfield {
// тут всё так, как у тебя и есть +
std::size_t GetSize() const;
{ \
return sizeof( RealType ## _ ## name ) + \
MyAlign<RealType ## _ ## name>::Result; \
} \
};
template<typename MDT, typename TypeID, typename TRealType>
std::size_t GetSize();
{
return sizeof( MDT ) + MyAlign<MDT>::Result;
}
Во-вторых, на сколько я понял, место тебе не жаль?
Тогда можно сдать так, что все ORMfield будут выравниваться одинаково. На что-то одно и то же, достаточно большое.
Ну, например, иметь такой вот простенький шаблон:
template<typename T, typename TAlignAs = double> class AlignedData {
union {
TAlignAs dummy;
char buffer[sizeof( T )];
} data;
public:
AlignedData() { new( data.buffer ) T; }
AlignedData( const AlignedData& other ) { new( data.buffer ) T( other.Get() ); }
template<typename T1>
AlignedData( T1 t1 ) { new( data.buffer ) T( t1 ); }
template<typename T1, typename T2>
AlignedData( T1 t1, T2 t2 ) { new( data.buffer ) T( t1, t2 ); }
~AlignedData() { Get().~T(); }
void operator = ( const AlignedData& other ) { Get() = other.Get(); }
T& Get() { return *static_cast<T*>( data.buffer ); }
const T& Get() const { return *static_cast<const T*>( data.buffer ); }
};
И внутри ORMfield хранить все данные в виде AlignedData<RealType>
Тогда на GetSize можно будет просто забить, а сделать у всех ORMfield общую базу, типа ORMfieldBase и заиметь у неё метод virtual const ORMfieldBase* GetNext() const...
S>fields представляет из себя поток дефайнов, вида:
S>S>INT(id, 50, INITVALUE(100))
S>STRING(name, 100, 60, INITVALUE(L"TestDefaultString"))
S>DATE(birthday, 10)
S>TIME(localtime, 31)
S>BOOL(IsOk, 25)
S>DOUBLE(pisun, 45)
S>
Что касается той идеи, что предлагал КодТ, то там всё просто.
Я напишу так, словно у тебя есть только тип int и немного на псевдокоде, а ты обобщишь потом на всё, что тебе надо.
Пишешь, такую систему макросов:
#define EXTRACT_FIELDS_END_OF_LIST_
#define EXTRACT_FIELDS_INT( NAME, TYPE_ID, INIT_VALUE ) \
/* тут собственно кусок твоего старого определение INT */ FIELD( int, NAME, TYPE_ID, INIT_VALUE )\
EXTRACT_FIELDS_##
EXTRACT_FIELDS( FIELDS ) EXTRACT_FIELDS_##FIELDS END_OF_LIST_
// Тут, на самом деле, надо немного схимичить, чтобы FIELDS подставилось раньше, чем развернётся, но я пока не буду
Потом пишешь ещё макросы EXTRACT_INITIALIZATORS_LIST, EXTRACT_TABLE_OF_OFFSETS и т. д. и живёшь себе счастливо без виртуальных функций GetSize
Да, по поводу "схимичить" всё уже схимичено в бусте. Если ты не можешь его заюзать, а IMHO, это жёсткое требованиек клиентам -- доставить буст. Ты можешь выдрать химию оттуда, или попроси, тебе тут помогут.
Вот.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
К>Завтра попробую на BOOST/PP родить рабочий пример.
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/facilities/expand.hpp>
#include <boost/preprocessor/punctuation/paren.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
// T_xxx - макросы для внутреннего использования
// генератор структуры
#define TABLE(name,meta,fields) \
struct name : ORMTableBase { \
meta \
BOOST_PP_SEQ_FOR_EACH(T_APPLY, (T_MEMBER,name), fields) \
static const FieldInfo* reflection() { \
static const FieldInfo refs[] = { \
BOOST_PP_SEQ_FOR_EACH(T_APPLY, (T_REFLECTION,name), fields) \
{} \
}; \
return refs; \
} \
}; \
//ENDMACRO
// колбеки - генераторы элементов
#define T_MEMBER(name,type,var,...) type var;
#define T_REFLECTION(name,type,var,...) { BOOST_PP_STRINGIZE(var), sizeof(type), _offsetof(name,var) },
// T_APPLY - общий колбек (высшего порядка!) для макроса FOR_EACH
// принимает 3 параметра (r,data,elem)
// r - номер следующего элемента, - т.е. 2, 3, 4, и т.д. - бустовские штучки
// data - общий параметр, у нас это (T_MEMBER,name) или (T_REFLECTION,name) - т.е. колбек + имя класса
// elem - элемент серии, т.е. информация об очередном поле (type,var,etc...)
// T_APPLY(r,(macro,name),(type,var,etc...)) --> T_APPLY_( macro, name, type,var,etc... ) -- последняя пачка идёт одним аргументом!
#define T_APPLY(r,mn,fld) \
BOOST_PP_EXPAND( \
T_APPLY_(BOOST_PP_TUPLE_ELEM(2,0,mn), BOOST_PP_TUPLE_ELEM(2,1,mn), T_UNWRAP fld) \
) \
//ENDMACRO
#define T_APPLY_(macro,name,fld) macro BOOST_PP_LPAREN() name,fld ) // --> macro ( name , type,var,etc... )
#define T_UNWRAP(...) __VA_ARGS__ // (x,y,z,t) --> x,y,z,t
// тестируем...
TABLE(foo, hello, ((int,x)) ((char,y)) )
// на выходе получилось
struct foo : ORMTableBase {
hello
int x;
char y;
static const FieldInfo* reflection() {
static const FieldInfo refs[] = {
{ "x", sizeof(int), _offsetof(foo,x) },
{ "y", sizeof(char), _offsetof(foo,y) },
{}
};
return refs;
}
};