Preprocessor sequence of "pairs"
I have a question about the use of the preprocessor library. I'll describe
my ultimate goal in a moment, but I've simplified my first problem to the
following. Also, I know what I'm doing looks a lot at first glance like
Fusion's ADAPT_STRUCT, but I took a quick look at that documentation and I
don't think it matches. However, if you disagree with me and think it
does, or can suggest a totally different solution, feel free to describe
how.
== My problem ==
I don't like C's struct syntax, so I'm trying to define my own.
(</sarcasm> Really for purposes of this section I'm just doing this to
figure out all the steps I'll need.)
What I want to do is something like the following:
#include
What I want to do is something like the following:
#include
#define DEFINE_FIELD(r, os, field_pair) [....]
#define DEFINE_STRUCT(struct_name, fields) \ struct struct_name { \ BOOST_PP_SEQ_FOR_EACH(DEFINE_FIELD, ~, fields) \ }
DEFINE_STRUCT(mystruct, (int, x)(int, y)(double, z))
but this doesn't work, because the sequence is made up of pairs instead of single preprocessor arguments. (GCC complains about "macro "BOOST_PP_SEQ_SIZE_0" passed 2 arguments, but takes just 1" and same for PP_EXPR_IIF_0.)
I can get it to work by double-parenthesizing the stuff in the sequence (saying "((int, x))((int, y))((double, z))") then defining DEFINE_FIELD as follows:
#define DEFINE_FIELD_REAL(type, name) type name; #define DEFINE_FIELD(r, os, field_pair) DEFINE_FIELD_REAL field_pair
but this is less than optimal. Is there a way that I can do this easily, or would I effectively have to re-implement SEQ_FOR_EACH? (And how hard would that be if it's necessary?)
Given INPUT of the form (a1, b1)(a2, b2)(a3, b3)...(an, bn) you can transform it into OUTPUT of the form ((a1, b1))((a2, b2))((a3, b3))...((an, bn)) as follows: #define ADD_PAREN_1(A, B) ((A, B)) ADD_PAREN_2 #define ADD_PAREN_2(A, B) ((A, B)) ADD_PAREN_1 #define ADD_PAREN_1_END #define ADD_PAREN_2_END #define OUTPUT BOOST_PP_CAT(ADD_PAREN_1 INPUT,_END) You can then process the double-parenthesized sequence with SEQ_FOR_EACH like you want. Regards, Nate
On Wed, 26 Sep 2012 07:52:24 +0200,
I have a question about the use of the preprocessor library. I'll describe my ultimate goal in a moment, but I've simplified my first problem to the following. Also, I know what I'm doing looks a lot at first glance like Fusion's ADAPT_STRUCT, but I took a quick look at that documentation and I don't think it matches. However, if you disagree with me and think it does, or can suggest a totally different solution, feel free to describe how.
== My problem ==
I don't like C's struct syntax, so I'm trying to define my own. (</sarcasm> Really for purposes of this section I'm just doing this to figure out all the steps I'll need.)
What I want to do is something like the following:
#include
#define DEFINE_FIELD(r, os, field_pair) [....]
#define DEFINE_STRUCT(struct_name, fields) \ struct struct_name { \ BOOST_PP_SEQ_FOR_EACH(DEFINE_FIELD, ~, fields) \ }
DEFINE_STRUCT(mystruct, (int, x)(int, y)(double, z))
but this doesn't work, because the sequence is made up of pairs instead of single preprocessor arguments. (GCC complains about "macro "BOOST_PP_SEQ_SIZE_0" passed 2 arguments, but takes just 1" and same for PP_EXPR_IIF_0.)
I can get it to work by double-parenthesizing the stuff in the sequence (saying "((int, x))((int, y))((double, z))") then defining DEFINE_FIELD as follows:
#define DEFINE_FIELD_REAL(type, name) type name; #define DEFINE_FIELD(r, os, field_pair) DEFINE_FIELD_REAL field_pair
but this is less than optimal. Is there a way that I can do this easily, or would I effectively have to re-implement SEQ_FOR_EACH? (And how hard would that be if it's necessary?)
== What I'm actually trying to do ==
What I'm actually doing is trying to write a macro that will define a function like the following:
DEFINE_SERIALIZED_STRUCT(mystruct, (int, x)(int, y)(double, z)) | V struct mystruct { int x; int y; double z; }; void serialize(ostream & os, mystruct const & s) { os << "x: " << s.x << "\n"; os << "y: " << s.y << "\n"; os << "z: " << s.z << "\n"; }
(I didn't see a way to recover the field names from a Fusion-adapted struct -- from what I can tell, it more just seems to be a way to iterate over the fields.)
Hi, the trick /I looked from fusion:)/ is to wrap every sequence member in an additional () before passing it to the main macro: #define MY_SEQUENCE_CREATOR_0(...) \ ((__VA_ARGS__)) MY_SEQUENCE_CREATOR_1 #define MY_SEQUENCE_CREATOR_1(...) \ ((__VA_ARGS__)) MY_SEQUENCE_CREATOR_0 #define MY_SEQUENCE_CREATOR_0_END #define MY_SEQUENCE_CREATOR_1_END And then you wrap call to your DEFINE_STRUCT: #define _DEFINE_STRUCT(struct_name, fields) \ struct struct_name { \ BOOST_PP_SEQ_FOR_EACH(DEFINE_FIELD, ~, fields) \ } #define DEFINE_FIELD(r, data, field_tuple) \ V_BOOST_PP_TUPLE_ELEM(0, field_tuple) V_BOOST_PP_TUPLE_ELEM(1, field_tuple); #define DEFINE_STRUCT(struct_name, fields) \ _DEFINE_STRUCT(struct_name, \ BOOST_PP_CAT(MY_SEQUENCE_CREATOR_0 fields,_END)) \ If I dont mistake it basically transforms DEFINE_STRUCT(mystruct, (int, x)(int, y)(double, z)) into _DEFINE_STRUCT(mystruct, ((int, x))((int, y))((double, z))) so you get a single *tuple* as a parameter to your DEFINE_FIELD. Eventually you can call another macro with this tuple, but I am not sure whether this will be preprocessed as expected. I had troubles with it, so I used V_BOOST_PP_TUPLE_ELEM (appeared first in boost 1.49) to extract tuple elements in-place and it worked for me. // I've heard there are some nasty troubles with msvc and variadic macros, so you have an additional quest if you need to support msvc -- Slava
What I'm actually doing is trying to write a macro that will define a function like the following:
DEFINE_SERIALIZED_STRUCT(mystruct, (int, x)(int, y)(double, z)) | V struct mystruct { int x; int y; double z; }; void serialize(ostream & os, mystruct const & s) { os << "x: " << s.x << "\n"; os << "y: " << s.y << "\n"; os << "z: " << s.z << "\n"; }
(I didn't see a way to recover the field names from a Fusion-adapted struct -- from what I can tell, it more just seems to be a way to iterate over the fields.)
See this stackoverflow answer: http://stackoverflow.com/a/11744832/375343 It shows how to create reflectable fields in a class(or struct). It uses what is called "typed expressions" in the preprocessor(which is just an expression that puts the type in parenthesis). The macros shown for typed expressions(the `TYPEOF`, `STRIP`, and `PAIR` macros), however, won't work on msvc, but there are workarounds. Let me know if you are using msvc and I can send them to you. So in your example, you could define your struct like this: struct mystruct { REFLECTABLE ( (int) x, (int) y, (double) z ) }; Then write your serialization like this, which works for any struct that has reflectable fields: struct serialize_visitor { ostream & os; serialize_visitor(ostream & os) : os(os) {} template<class FieldData> void operator()(FieldData f) { os << f.name() << ": " << f.get() << std::endl; } }; template<class T> void serialize(ostream & os, T & x) { visit_each(x, serialize_visitor(os)); } Which I believe is much better than generating serialization code with the preprocessor, because it can be a pain trying to debug serialization code inside of a macro. Paul
participants (4)
-
driscoll@cs.wisc.edu
-
Nathan Ridge
-
paul Fultz
-
Viatcheslav.Sysoltsev@h-d-gmbh.de