Splitting this off from my question regarding appending to CPP macros:
Has anyone here used the Boost.Preprocessor library’s data types to implement something like the X-macro?
Splitting this off from my question regarding appending to CPP macros:
Has anyone here used the Boost.Preprocessor library’s data types to implement something like the X-macro?
I just looked up what an X-Macro is supposed to be and I think I did something like what you're asking for.
What I wanted to do is to easily and quickly support serialisation for a series of fairly similar classes. The problem I had is that I had to convert some runtime information (an int) into a compile time type (a class) to be able to do my serialisation. I could have written a couple of case statements to do the job but that would mean that I have to update several functions each time I wanted to add a class.
To get around this problem, I first defined a sequence of tuples containing the mapping:
#define WIN_MESSAGE_TYPE_SEQ \
((EM_REPLACESEL, em_replacesel))((WM_CHAR, wm_char)) //...
The upper case names are defines that hold an int and the lower case names are classes that I defined somewhere else.
I can then use this sequence in conjunction with some of the Boost preprocessors to generate all sorts of code for me. For example, to get a forward declaration of the classes I can just do this:
#define WIN_MESSAGE_TYPE_BUILD_MACRO(r, _data_, _elem_) \
class BOOST_PP_TUPLE_ELEM(2,1,_elem_);
BOOST_PP_SEQ_FOR_EACH(WIN_MESSAGE_TYPE_BUILD_MACRO, BOOST_PP_NIL, WIN_MESSAGE_TYPE_SEQ)
#undef WIN_MESSAGE_TYPE_BUILD_MACRO
To do the runtime to compile time mapping, I generate a series of case statements like this:
#define WIN_MESSAGE_TYPE_BUILD_MACRO(r, _data_, _elem_) \
case BOOST_PP_TUPLE_ELEM(2,0,_elem_): return win_message_serializer<BOOST_PP_TUPLE_ELEM(2,1,_elem_)>::serialize(msg, o_arch);
template <typename Archive>
void serialize_win_message (p_win_message_base msg, Archive& o_arch) {
message_type_t message_type = msg->type();
switch (message_type) {
// This will generate a series of case statement for each message type that will invoke
// the serializer for the correct types.
BOOST_PP_SEQ_FOR_EACH(WIN_MESSAGE_TYPE_BUILD_MACRO, BOOST_PP_NIL, WIN_MESSAGE_TYPE_SEQ)
default: //...
};
}
#undef WIN_MESSAGE_TYPE_BUILD_MACRO
The whole code involves quite a bit more than this but this should still give you an idea on how to generate code using the Boost preprocessors. In my example I can quickly and easily add serialisation support for a class by simply updating my sequence.
Note that using the Boost preprocessor doesn't produce very readable code so I try to keep the macro used by the for each macro as simple as possible. Also I wouldn't be surprised if someone somewhere has a more elegant solution to this problem. This is just what I came up with for a personal project where I don't mind the extra complexity.