Pregunta

I want to create a define to parse function signature and using Boost Preprocessor create something like this:

MY_DEFINE std::string fun(int t, float b)
{

or at least:

MY_DEFINE(std::string)(fun)(int t, float b)
{

that would generate:

class fun_in
{
    int t;
    float b;
}

class fun_out
{
    std::string value;
}

void my_fun_wrapper(int t, float b)
{
}

std::string fun(int t, float b)
{
    my_fun_wrapper(t, b);

for each function with that define.

Is it possible to create such define wrapper for function of N incoming arguments and any return type via Boost Preprocessor?

¿Fue útil?

Solución

Well, the preprocessor can't parse tokens without pre-telling it. So you will need to use a lot more parenthesis instead. Here's what it would look like:

DEFINE( (std::string)(fun)((int) a, (float) b) )
{
    return "Hello World!";
}

Here's how to create the macro using boost(I assume you are familiar with its preprocessor library). First is to define some macros for handling parenthesis, because boost doesn't handle sequences with commas at all:

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

Next, you need to handle the args in three different ways. First, is to output them as member variable(like int a; float b;). Then as functions arguments(like (int a, float b)). Then finally as forward arguments to pass along to the other function(like (a, b)).

#define DETAIL_DEFINE_MEMBERS_EACH(r, data, x) PAIR(x);
#define DETAIL_DEFINE_ARGS_EACH(r, data, i, x) BOOST_PP_COMMA_IF(i) PAIR(x)
#define DETAIL_DEFINE_FORWARD_EACH(r, data, i, x) BOOST_PP_COMMA_IF(i) STRIP(x)

#define DETAIL_DEFINE_MEMBERS(args) BOOST_PP_SEQ_FOR_EACH(DETAIL_DEFINE_MEMBERS_EACH, data, BOOST_PP_VARIADIC_TO_SEQ args)
#define DETAIL_DEFINE_ARGS(args) BOOST_PP_SEQ_FOR_EACH_I(DETAIL_DEFINE_ARGS_EACH, data, BOOST_PP_VARIADIC_TO_SEQ args)
#define DETAIL_DEFINE_FORWARD(args) BOOST_PP_SEQ_FOR_EACH_I(DETAIL_DEFINE_FORWARD_EACH, data, BOOST_PP_VARIADIC_TO_SEQ args)

Next we create a DETAIL_DEFINE macro that take three parameters. The first is the name of the function, the arguments, and then the return value. This will produce the classes and functions, like you want:

#define DETAIL_DEFINE(name, args, ...) \
struct BOOST_PP_CAT(name, _in) \
{ \
    DETAIL_DEFINE_MEMBERS(args) \
}; \
struct BOOST_PP_CAT(name, _out) \
{ \
    __VA_ARGS__ value; \
}; \
__VA_ARGS__ BOOST_PP_CAT(name, _impl) DETAIL_DEFINE_ARGS(args) ; \
__VA_ARGS__ name DETAIL_DEFINE_ARGS(args) \
{ \
    return BOOST_PP_CAT(name, _impl) DETAIL_DEFINE_FORWARD(args); \
} \
__VA_ARGS__ BOOST_PP_CAT(name, _impl) DETAIL_DEFINE_ARGS(args)

Finally, the DEFINE macro will parse out all the parenthesis and pass them to the DETAIL_DEFINE macro:

#define DEFINE(x) DETAIL_DEFINE(TYPEOF(STRIP(x)), (TYPEOF(STRIP(STRIP(x)))), TYPEOF(x))

So now when you write:

DEFINE( (std::string)(fun)((int) a, (float) b) )
{
    return "Hello World!";
}

It should output:

struct fun_in
{
    int a;
    float b;
};
struct fun_out
{
    std::string value;
};
std::string fun_impl(int a, float b);
std::string fun(int a, float b)
{
    return fun_impl(a, b);
}
std::string fun_impl(int a, float b)
{
    return "Hello World!";
}

Note that this won't work in MSVC, there are workarounds though. Also, you need to compile with the -DBOOST_PP_VARIADICS=1.

Otros consejos

What was the purpose of the last comma in this macro ?

#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)

I think it has an extra comma at the end , and can be coded as

#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x)

checking how it will work without last comma in this macro

1.) first argument in DETAIL_DEFINE

STRIP( (std::string)(fun)((int) a, (float) b) ) --> (fun)((int) a, (float) b)

TYPEOF( (fun)((int)a, (float) b)) ) -->  fun

2.) second argument in DETAIL_DEFINE

STRIP( STRIP( (std::string)(fun)((int)a, (float) b) ) ) --> ( ( int ) a , ( float ) b )

TYPEOF ( ( ( int ) a , ( float ) b ) ) --> ( int ) a , ( float ) b

3.) third argument in DETAIL_DEFINE

TYPEOF ( (std::string )( fun )(( int ) a , ( float ) b) ) --> std::string

result :

DETAIL_DEFINE ( fun , (( int ) a , ( float ) b), std::string )

BTW, for those with old boost without BOOST_PP_VARIADIC_TO_SEQ use BOOST_PP_TUPLE_TO_SEQ like this :

#define BOOST_PP_VARIADIC_TO_SEQ(...) BOOST_PP_TUPLE_TO_SEQ(PP_NARG(__VA_ARGS__) , (__VA_ARGS__))

And PP_NARG you can find anywhere by googling , here it is :

//Original Author: Unknown, but well recognized recursive variadic macro
#define PP_NARG(...)        PP_NARG_IMPL(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_IMPL(...)   PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top