Split macro parameters at `=` for smart enum implementation - c++

Is there a way to write a macro ENUM such that ENUM(Animal, Dog, Cat = 5, Horse = 2) expands to
enum class Animal { Dog, Cat = 5, Horse = 2 };
template<typename EnumT>
constexpr std::array<std::pair<std::string_view, Animal>, 3> get_enum_map() {
return {{ {"Dog", Animal::Dog} , {"Cat", Animal::Cat} , {"Horse", Animal::Horse} }};
}
Note, the difficulty is that the macro parameters NAME = VAL need to be split at = to obtain NAME without VAL so that we can define get_enum_map() cleanly. Is it possible to do this?
For reference, the below code implements ENUM(Animal, Dog, Cat, Horse) (similar to wise_enum) but without the ability to specify values for the enums. I have seen examples such as ENUM(Animal, Dog, (Cat, 5), (Horse , 2)) where individual parameters are packaged as (NAME, VAL). But I am curious if there is a way to do it via NAME = VAL.
#include <string_view>
#include <array>
#define ENUM_EXPAND(x) x
#define ENUM_COMMA() ,
#define ENUM_STR_CONCAT(a, ...) ENUM_STR_CONCAT_(a, __VA_ARGS__)
#define ENUM_STR_CONCAT_(a, ...) a##__VA_ARGS__
#define ENUM_ARG_COUNT_(_1, _2, _3, _4, _5, X,...) X
#define ENUM_ARG_COUNT(...) ENUM_EXPAND(ENUM_ARG_COUNT_(__VA_ARGS__, 5, 4, 3, 2, 1))
#define ENUM_LOOP_1(f, a, b, c) f(a, c)
#define ENUM_LOOP_2(f, a, b, c, ...) f(a, c) b() ENUM_EXPAND(ENUM_LOOP_1(f, a, b, __VA_ARGS__))
#define ENUM_LOOP_3(f, a, b, c, ...) f(a, c) b() ENUM_EXPAND(ENUM_LOOP_2(f, a, b, __VA_ARGS__))
#define ENUM_LOOP_4(f, a, b, c, ...) f(a, c) b() ENUM_EXPAND(ENUM_LOOP_3(f, a, b, __VA_ARGS__))
#define ENUM_LOOP_5(f, a, b, c, ...) f(a, c) b() ENUM_EXPAND(ENUM_LOOP_4(f, a, b, __VA_ARGS__))
#define ENUM_PAIR(name, x) { #x, name::x}
#define ENUM_IMPL(name, loop_func_name, ...) \
enum class name { __VA_ARGS__ }; template<typename EnumT> \
constexpr std::array<std::pair<std::string_view, name>, ENUM_ARG_COUNT(__VA_ARGS__)> get_enum_map() { \
return {{ ENUM_EXPAND(loop_func_name(ENUM_PAIR, name, ENUM_COMMA, __VA_ARGS__)) }}; }
#define ENUM(name, ...) ENUM_IMPL(name, ENUM_STR_CONCAT(ENUM_LOOP_, ENUM_ARG_COUNT(__VA_ARGS__)), __VA_ARGS__)
ENUM(Animal, Dog, Cat, Horse)
int main() {
constexpr auto arr = get_enum_map<Animal>();
constexpr auto el0 = std::get<0>(arr[1]);
constexpr auto el1 = std::get<1>(arr[1]);
static_assert(el0 == "Cat");
static_assert(el1 == Animal::Cat);
return 0;
}

As pointed out in the comments, there is no way to split macro inputs at a certain char, like =.
However, the better-enums library uses a neat trick by absorbing (or "eating") assignments via an _eat_assign helper class. For example
(EnumType)((better_enums::_eat_assign<EnumType>)EnumType::Name = EnumValue)
reduces to just EnumType::Name, effectively removing the assignment. See _eat_assign definition.

Related

Wrap C++ function with default parameters in C

Say I have a function like this defined in a C++ header:
namespace foo {
void bar(int a, int b = 1);
}
and I would like to use this function in C code. One obvious solution would be to define two functions like this:
void foo_bar_1(int a)
{ foo::bar(a, 1); }
void foo_bar_2(int a, int b)
{ foo::bar(a, b); }
These can then easily be included in C code. However, this gets ugly for multiple default parameters, it would be nicer to have a single wrapper function. I thought about doing something like this:
#define _test_foo_numargs(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#define test_foo(...) do { \
if (_test_foo_numargs(__VA_ARGS__) == 1) \
test_foo_1(__VA_ARGS__); \
else if (_test_foo_numargs(__VA_ARGS__) == 2) \
test_foo_2(__VA_ARGS__); \
} while (0)
But that doesn't work because both of the calls to test_foo_1 and test_foo_2 have to be valid in order for this to compile.
Is there a better way to do this?
I will provide my own solution here in case nobody has a better one and someone has the same problem in the future:
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
int _test_foo_1(int a)
{ return a; }
int _test_foo_2(int a, int b)
{ return a + b; }
int _test_foo_va(size_t num_args, ...)
{
va_list args;
va_start(args, num_args);
switch (num_args) {
case 1:
return _test_foo_1(va_arg(args, int));
case 2:
return _test_foo_2(va_arg(args, int), va_arg(args, int));
}
va_end(args);
}
#define _test_foo_numargs(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#define test_foo(...) _test_foo_va(_test_foo_numargs(__VA_ARGS__), __VA_ARGS__)
int main()
{
printf("%d\n", test_foo(1));
printf("%d\n", test_foo(1, 2));
}
This is of course pretty unsafe because it will compile if too little or too many arguments are passed.
You might do the following:
#define TAKE_9(_1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define COUNT(...) TAKE_9(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
#define CONCAT_(A, B) A ## B
#define CONCAT(A, B) CONCAT_(A, B)
#define SELECT(NAME, ...) CONCAT(NAME, COUNT(__VA_ARGS__))(__VA_ARGS__)
#define foo_bar(...) SELECT(foo_bar_, __VA_ARGS__)
Demo
COUNT can be upgraded to handle 0 arguments, if you compiler support it
#define COUNT(...) TAKE_9(__VA_ARGS__ __VA_OPT__(,) 8, 7, 6, 5, 4, 3, 2, 1, 0)
Demo
To avoid to have to write foo_bar_N, you might do:
// ...
#define FUNC_WITH_DEFAULT_ARG_2(func, DEFAULT, ...) TAKE_3(__VA_ARGS__ __VA_OPT__(,) \
func(__VA_ARGS__), \
func(__VA_ARGS__, TAKE_2 DEFAULT), \
func(TAKE_1 DEFAULT, TAKE_2 DEFAULT) )
#define FUNC_WITH_DEFAULT_ARG_3(func, DEFAULT, ...) TAKE_4(__VA_ARGS__ __VA_OPT__(,) \
func(__VA_ARGS__), \
func(__VA_ARGS__, TAKE_2 DEFAULT), \
func(__VA_ARGS__, TAKE_2 DEFAULT, TAKE_3 DEFAULT), \
func(TAKE_1 DEFAULT, TAKE_2 DEFAULT, TAKE_3 DEFAULT) )
// Choose on or other, error message for misuse might differ
// RequiredParameter is just a name for "better" error message when not enough parameter are given
#define foo_bar(...) FUNC_WITH_DEFAULT_ARG_2(foo_bar_impl, (RequiredParameter, 1), __VA_ARGS__)
#define foo_bar2(_1, ...) FUNC_WITH_DEFAULT_ARG_3(foo_bar_impl, (_1, 0), __VA_ARGS__)
void foo_bar_impl(int a, int b)
{ foo::bar(a, b); }
Demo

Passing function parameters to a function through a macro

I want to create a macro in which one of the parameters is the parameters for a function used within the macro.
#define Macro(PERAMS, OTHER) \
functionBeingUsed(PERAMS); \
OTHER;
Macro(1,2,3,4, int i = 0);
As you can see you can see the commas are being used by both the macro and the function. which results in broken code.
I was wondering if there was a way to achieve the result that I need so the code can be interpreted like the following.
Macro((1,2,3,4), int i = 0);
Please note I am not using C++11.
Whenever you need to do anything nontrivial with the preprocessor, you should immediately jump to the Boost.PP library. Here, the BOOST_PP_REMOVE_PARENS macro seems relevant:
#define Macro(PARAMS, OTHER) \
functionBeingUsed(BOOST_PP_REMOVE_PARENS(PARAMS)); \
OTHER;
Macro((1,2,3,4), int i = 0);
Alternatively, you could just write functionBeingUsed PARAMS and let the passed parentheses be used.
It is not possible to do what you have described. However, you can do something similar with variadic macros. You would need to put the PARAMS part at the end and it would look like the following:
#define Macro(OTHER, ...) \
functionBeingUsed(__VA_ARGS__); \
OTHER;
Macro(int i = 0, 1, 2, 3, 4);
I believe the only answer is to use multiple macros
#define MacroBegin \
\\PreFunction
#define MacroEnd \
\\PostFunction
MacroBegin
functionBeingCalled(1,2,3,4);
MacroEnd
To differentiate which commas belong to the macro itself and which belong to the function being called, you have to wrap the function parameters in parenthesis, eg:
Macro((1,2,3,4), int i = 0);
You would then have to remove the parenthesis from the function call inside the macro, since they will be coming from the macro parameter instead, eg:
#define Macro(PERAMS, OTHER) \
functionBeingUsed PERAMS; \
OTHER;
So, something like this:
Macro((1,2,3,4), int i = 0);
Will resolve to this:
functionBeingUsed (1,2,3,4);
int i = 0;
Just remember the extra parenthesis even if the function being called doesn't take any input parameters:
Macro((), ...);
functionBeingUsed ();
...;
To keep your syntax, you may do something like:
#define COUNT_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define COUNT(...) COUNT_N(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
// Warning: COUNT() return 1 (as COUNT(A)) :-/
#define IDENTITY(N) N
#define APPLY(macro, ...) IDENTITY(macro(__VA_ARGS__))
#define F_1(a) functionBeingUsed(); a;
#define F_2(a, b) functionBeingUsed(a); b;
#define F_3(a, b, c) functionBeingUsed(a, b); c;
#define F_4(a, b, c, d) functionBeingUsed(a, b, c); d;
#define F_5(a, b, c, d, e) functionBeingUsed(a, b, c, d); e;
#define F_6(a, b, c, d, e, f) functionBeingUsed(a, b, c, d, e); f;
#define F_7(a, b, c, d, e, f, g) functionBeingUsed(a, b, c, d, e, f); g;
#define F_8(a, b, c, d, e, f, g, h) functionBeingUsed(a, b, c, d, e, f, g); h;
#define DISPATCH(N) F_ ## N
#define Macro(...) IDENTITY(APPLY(DISPATCH, COUNT(__VA_ARGS__)))(__VA_ARGS__)
Demo
but simpler to change your syntax to:
#define Macro(PARAMS, OTHER) \
functionBeingUsed PARAMS; \
OTHER;
Macro((1,2,3,4), int i = 0);

C++ or macro magic to generate method and forward arguments

I would like to create a magical macro, or anything, that would generate a something like this:
MAGICAL_MACRO(return_type, method_name, ...)
should work like this:
MAGICAL_MACRO(void, Foo, int a, int b)
->
virtual void Foo(int a, int b)
{
_obj->Foo(a, b);
}
Is this possible? I am afraid it is not.
Two questions: Are you open to a slightly different syntax for the arguments of MAGIC_MACRO? And can you use the Boost.Preprocessor header-only library?
If both answers are "yes", I have a solution for you:
#define MAGICAL_MACRO(Type, Name, ...) \
virtual Type Name(MAGICAL_GENERATE_PARAMETERS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))) {\
_obj->Name(MAGICAL_GENERATE_ARGUMENTS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))); \
}
#define MAGICAL_GENERATE_PARAMETERS(Args) \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MAGICAL_MAKE_PARAMETER, %%, Args))
#define MAGICAL_GENERATE_ARGUMENTS(Args) \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MAGICAL_MAKE_ARGUMENT, %%, Args))
#define MAGICAL_MAKE_PARAMETER(s, Unused, Arg) \
BOOST_PP_TUPLE_ELEM(2, 0, Arg) BOOST_PP_TUPLE_ELEM(2, 1, Arg)
#define MAGICAL_MAKE_ARGUMENT(s, Unused, Arg) \
BOOST_PP_TUPLE_ELEM(2, 1, Arg)
Usage looks like this:
MAGICAL_MACRO(void, Foo, (int, a), (int, b))
[Live example]
The %% used in the macro definitions is just my way of indicating "this value is not used." You could use pretty much anything else there (unless it contains a comma).
The above solution will work as long as the types involved are not spelled with a comma. If they are, introduce a type alias for them (typedef or using). Note that it is possible to get around this within the preprocessor magic itself, but it complicates already ugly code.
If you don't mind changing the syntax for the macro arguments, you could use following trick which abuses declaration syntax:
#define MAGICAL_MACRO(return_type, method_name, ...) \
virtual return_type method_name(__VA_ARGS__)
{ \
_obj->method_name(__VA_ARGS__); \
}
MAGICAL_MACRO(void, foo, int(a), int(b))
That will expand to:
virtual void foo(int(a), int(b))
{
_obj->foo(int(a), int(b));
}
Where void func(int(a), int(b)) is completely equivalent to void func(int a, int b).
The extra casts (or constructor calls depending on argument types) are ugly, but both GCC and Clang (with -O0) seem to ignore them not only for primitive types/PODs, but also for non-POD classes even if their copy constructors have side effects:
#include <iostream>
struct A
{
int x;
A(int value) : x(value) {}
A(const A &o)
{
x = o.x;
std::cout << "copy";
}
};
void func(A a)
{
std::cout << a.x << '\n';
}
void func1(A a)
{
func(a);
}
void func2(A a)
{
func(A(a));
}
int main()
{
func1(1); // prints `copy1`
func2(2); // prints `copy2`
}
The code below is working for what you've asked for with up to 1024 arguments and without using additional stuff like boost. It defines an EVAL(...) and also a MAP(m, first, ...) macro to do recursion and to use for each iteration the macro m with the next parameter first.
It is mostly copied from C Pre-Processor Magic. It is also great explained there. You can also download these helper macros like EVAL(...) at this git repository, there are also a lot of explanation in the actual code. It is variadic so it takes the number of arguments you want.
But I changed the FIRST and the SECOND macro as it uses a Gnu extension like it is in the source I've copied it from.
To split arguments like int a into int and a I used this answer from SO.
Your macro will be:
#define MAGICAL_MACRO(return_type, method_name, ...) \
virtual return_type method_name(__VA_ARGS__) \
{ \
return _obj->method_name(EVAL(MAP(TYPE_NAME, __VA_ARGS__))); \
}
Examples and limitations:
MAGICAL_MACRO(void, FOO, int a, double b, char c);
--> virtual void FOO(int a, double b, char c) { return _obj->FOO(a , b , c); };
MAGICAL_MACRO(int, FOO, int a, double b, char c);
--> virtual int FOO(int a, double b, char c) { return _obj->FOO(a , b , c); } ;
MAGICAL_MACRO(void, FOO, int* a, double* b, char* c);
--> virtual void* FOO(int* a, double* b, char* c) { return _obj->FOO(* a , * b , * c); };
/* maybe not what you want: pointer are dereferenced */
All the other macros needed, note that type splitting need to be defined per macro here:
/* Define all types here */
#define SPLIT_int int COMMA
#define SPLIT_char char COMMA
#define SPLIT_float float COMMA
#define SPLIT_double double COMMA
#define FIRST_(a, ...) a
#define SECOND_(a, b, ...) b
#define FIRST(...) FIRST_(__VA_ARGS__,)
#define SECOND(...) SECOND_(__VA_ARGS__,)
#define EMPTY()
#define EVAL(...) EVAL1024(__VA_ARGS__)
#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) __VA_ARGS__
#define DEFER1(m) m EMPTY()
#define DEFER2(m) m EMPTY EMPTY()()
#define DEFER3(m) m EMPTY EMPTY EMPTY()()()
#define DEFER4(m) m EMPTY EMPTY EMPTY EMPTY()()()()
#define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
#define PROBE() ~, 1
#define CAT(a,b) a ## b
#define NOT(x) IS_PROBE(CAT(_NOT_, x))
#define _NOT_0 PROBE()
#define BOOL(x) NOT(NOT(x))
#define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
#define _IF_ELSE(condition) CAT(_IF_, condition)
#define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
#define _IF_0(...) _IF_0_ELSE
#define _IF_1_ELSE(...)
#define _IF_0_ELSE(...) __VA_ARGS__
#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
#define _END_OF_ARGUMENTS_() 0
#define MAP(m, first, ...) \
m(first) \
IF_ELSE(HAS_ARGS(__VA_ARGS__))( \
COMMA DEFER2(_MAP)()(m, __VA_ARGS__) \
)( \
/* Do nothing, just terminate */ \
)
#define _MAP() MAP
#define COMMA ,
#define CALL(A,B) A B
#define SPLIT(D) EVAL1(CAT(SPLIT_, D))
#define TYPE_NAME(D) CALL(SECOND,(SPLIT(D)))

Compile time generated block in C++

I have a struct in my server that I would like to generate it with macro or template in C++ as it has a lot of redundant things:
struct MyBlock {
void Merge(const MyBlock& from) {
if (apple.HasData()) {
apple.Merge(from.apple);
}
if (banana.HasData()) {
banana.Merge(from.banana());
}
...
}
void Clear() {
apple.Clear();
banana.Clear();
...
}
void Update(const SimpleBlock& simple_block) {
if (simple_block.apple.Updated()) {
apple.Add(simple_block.apple);
}
if (simple_block.banana.Updated()) {
banana.Add(simple_block.banana);
}
...
}
Fruit apple;
Fruit banana;
Animal dog;
Animal cat;
...
}
struct SimpleBlock {
SimpleFruit apple;
SimpleFruit banana;
SimpleAnimal dog;
SimpleAnimal cat;
...;
}
I would like to define more variables in the two blocks like apple and dog. I would also like to define more pairs of such blocks. But it involves a lot of trivial work. So my question is how we can use macro, template or some other C++ features including C++11 to generate these blocks in compile time?
The reason why I don't use collections to store those variable is because MyBlock struct would be passed as a parameter in another template class which would dynamically allocate and release this block in run time. It is actually a thread local block that would be aggregated periodically.
Straightforward enough with preprocessor list iteration:
#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_ID(...) __VA_ARGS__
#define M_LEFT(L, R) L
#define M_RIGHT(L, R) R
#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__)
#define M_FOR_EACH_0(ACTN, E) E
#define M_FOR_EACH_1(ACTN, E) ACTN(E)
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__)
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__)
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__)
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__)
//.. extend this to higher numbers with some copy&paste
#define MYBLOCK(...) struct MyBlock { \
void Merge(const MyBlock& from) { \
M_FOR_EACH(BLOCK_MERGE, __VA_ARGS__) \
} \
void Clear() { \
M_FOR_EACH(BLOCK_CLEAR, __VA_ARGS__) \
} \
void Update(const SimpleBlock& simple_block) { \
M_FOR_EACH(BLOCK_UPDATE, __VA_ARGS__) \
} \
M_FOR_EACH(BLOCK_FIELD, __VA_ARGS__) \
}
#define BLOCK_MERGE(F) if (M_ID(M_RIGHT F).HasData()) { \
M_ID(M_RIGHT F).Merge(from.M_ID(M_RIGHT F)); \
}
#define BLOCK_CLEAR(F) M_ID(M_RIGHT F).Clear;
#define BLOCK_UPDATE(F) if (simple_block.M_ID(M_RIGHT F).Updated()) { \
M_ID(M_RIGHT F).Add(simple_block.M_ID(M_RIGHT F)); \
}
#define BLOCK_FIELD(F) M_ID(M_LEFT F) M_ID(M_RIGHT F);
#define SIMPLEBLOCK(...) struct SimpleBlock { M_FOR_EACH(SIMPLE_DECL, __VA_ARGS__) }
#define SIMPLE_DECL(F) M_CONC(Simple, M_ID(M_LEFT F)) M_ID(M_RIGHT F);
#define FIELDS (Fruit, apple),(Fruit,banana),(Animal,dog),(Animal,cat)
MYBLOCK(FIELDS);
SIMPLEBLOCK(FIELDS);
Add the necessary further member variables to FIELDS in the existing format, and they will be added to the structs emitted by MYBLOCK and SIMPLEBLOCK. (Remember to extend M_FOR_EACH with more iterations... easy to to with a few ctrl+c,ctrl+v.)
template <typename SimpleT>
class BlockTemplate
{
public:
void Merge(const BlockTemplate& from) {
if (HasData()) {
Merge(from.simpleData);
}
}
void Update(const SimpleT& simple_block) {
if (simple_block.Updated()) {
Add(simple_block.data);
}
}
protected:
SimpleT simpleData;
};
Now, you can create objects of type BlockTemplate<SimpleFruit>, BlockTemplate<SimpleAnimal> etc. You could also store pointers to all these BlockTemplate objects in a container after having BlockTemplate inherit from an abstract type. Or, better yet, use the new-fangled type-erasure methods - boost::type_erasure::any for example.
EDIT : If you don't want to use the container that way, you could also make BlockTemplate variadic and store a tuple of different(type-wise) SimpleT objects and modify the Merge and Update functions accordingly. The problem with this is that it becomes much harder to track your SimpleT objects - std::tuple doesn't allow you to give names. You would be referring to the values as get<N>(tupleData).
The description of why you don't use a collection sounds like some optimization thing. Have you measured?
Anyway, one simple solution is store pointers to the objects in a collection.
Then you can iterate over the collection.

How to get function signature via preprocessor define written before it?

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?
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.
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