[Boost-users] Preprocessor sequence of "pairs"

25 views
Skip to first unread message

dris...@cs.wisc.edu

unread,
Sep 26, 2012, 1:52:24 AM9/26/12
to boost...@lists.boost.org
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 <boost/preprocessor/seq/for_each.hpp>

#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.)


_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Nathan Ridge

unread,
Sep 26, 2012, 3:44:54 AM9/26/12
to Boost Mailing List

> What I want to do is something like the following:
>
> #include <boost/preprocessor/seq/for_each.hpp>
>
> #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

Viatchesla...@h-d-gmbh.de

unread,
Sep 26, 2012, 4:01:18 AM9/26/12
to boost...@lists.boost.org
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

paul Fultz

unread,
Sep 26, 2012, 12:10:32 PM9/26/12
to boost...@lists.boost.org


>
> 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
Reply all
Reply to author
Forward
0 new messages