简体   繁体   English

如何定义一个定义调用自身的函数的宏?

[英]How to define a macro that defines a function that calls itself?

I want to make a macro that will define a function that calls that function on a list of objects. 我想创建一个宏来定义一个在对象列表上调用该函数的函数。 It doesn't necessarily have to be a preprocessor macro, but it should work. 它不一定必须是预处理器宏,但它应该工作。

I want to write something like this: 我想写这样的东西:

CALL_ON_ALL(doSomething(int arg1, bool arg2))

And I want it to produce this: 我想要它产生这个:

void doSomething(int arg1, bool arg2) {
    for (int i = 0; i < delegates.size(); i++)
        delegates[i]->doSomething(arg1, arg2);
}

I have something that works: 我有一些有用的东西:

#define CALL_ON_ALL(defSig, callSig) \
void defSig { \
    for (int i = 0; i < delegates.size(); i++) \
        delegates[i]->callSig; \
}

The problem is that I have to write the definition signature and the call signature separately like this: 问题是我必须分别编写定义签名和调用签名,如下所示:

CALL_ON_ALL(doSomething(int arg1, bool arg2), doSomething(arg1, arg2))

Is there a better way to do this? 有一个更好的方法吗?

EDIT 编辑

It doesn't necessarily have to be a preprocessor macro. 它不一定必须是预处理器宏。 Anything that works will do. 任何有效的方法都可以。

Try making the signature out of separate arguments (for two arguments): 尝试使用单独的参数(对于两个参数)进行签名:

#define CALL_ON_ALL2(name, argType1, argName1, argType2, argName2) \
void name( argType1 argName1 , argType2 argName2 ) { \
    for (int i = 0; i < delegates.size(); i++) \
        delegates[0]->name( argName1 , argName2 ); \
}

You can duplicate this for other argument numbers. 您可以将其复制为其他参数编号。

This is a good example for higher order functions, that are functions taking another function as an argument. 这是高阶函数的一个很好的例子,它是将另一个函数作为参数的函数。 In this case, the function to be called on every element. 在这种情况下,要在每个元素上调用函数。

The following defines a higher order function calling f on every element. 下面定义了一个在每个元素上调用f高阶函数。 It requires C++11 for the variadic templates ( args... ). 它需要C ++ 11用于可变参数模板( args... )。 If you don't have C++11 available, you can remove the typename ...Args and work with fixed argument types in the function signature instead. 如果您没有可用的C ++ 11,则可以删除typename ...Args并使用函数签名中的固定参数类型。

template<typename Delegates, typename Function, typename... Args>
void callOnAll(Delegates delegates, Function f, Args... args) {
    for (int i = 0; i < delegates.size(); i++)
        f(delegates[i], args...);
}

Now you can call this using this syntax: 现在您可以使用以下语法调用它:

callOnAll(delegates, std::mem_fun<void,Delegate>(&Delegate::doSomething), 42);

The std::mem_fun thing creates a temporary function object for the member function you want to call for each delegate. std::mem_fun为你想为每个委托调用的成员函数创建一个临时函数对象。 You can also apply other functions, taking a pointer as their first argument. 您还可以应用其他函数,将指针作为其第一个参数。 For example this small lambda function (also only since C++11): 例如,这个小的lambda函数(也仅限于C ++ 11):

callOnAll(delegates, [](Delegate *d){
    d->doSomething(42);
});

which is pretty much the same, just another syntax. 这几乎是一样的,只是另一种语法。

See an example here: 在这里查看示例:


A slightly different version of this code uses a range-based instead of indexed-based for-loop, which looks much cleaner (also requires C++11): 这个代码略有不同的版本使用基于范围而不是基于索引的for循环,它看起来更清晰(也需要C ++ 11):

template<typename Delegates, typename Function, typename... Args>
void callOnAll(Delegates delegates, Function f, Args... args) {
    for(auto d : delegates)
        f(d, args...);
}

For your information, there is std::for_each , which does pretty much what you want, but in a slightly more functional way, as it doesn't take the function parameters by itself, but you provide a lambda function / functor which only takes a pointer to an instance. 为了您的信息,有std::for_each ,它几乎std::for_each您的需求,但功能稍微强一些,因为它本身不需要函数参数,但是您提供的lambda函数/函数只需要指向实例的指针。 Compare the following code to the one above with the lambda function: 使用lambda函数将以下代码与上面的代码进行比较:

std::for_each(delegates.begin(), delegates.end(), [](Delegate *d){
    d->doSomething(42);
});

The only difference is that we have to pass .begin() and .end() iterators instead of just one single container instance, like delegates . 唯一的区别是我们必须传递.begin().end()迭代器,而不是只传递一个容器实例,比如delegates However, there are a lot of other algorithms defined in the standard library which are worth looking at! 但是, 标准库中定义了许多其他算法值得关注!

I think this is what you are looking for: 我认为这就是你要找的东西:

#define _NUM_ARGS2(X,X64,X63,X62,X61,X60,X59,X58,X57,X56,X55,X54,X53,X52,X51,X50,X49,X48,X47,X46,X45,X44,X43,X42,X41,X40,X39,X38,X37,X36,X35,X34,X33,X32,X31,X30,X29,X28,X27,X26,X25,X24,X23,X22,X21,X20,X19,X18,X17,X16,X15,X14,X13,X12,X11,X10,X9,X8,X7,X6,X5,X4,X3,X2,X1,N,...) N
#define NUM_ARGS(...) _NUM_ARGS2(0, ##__VA_ARGS__ ,64,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)

#define MAKE_PARAMS_0()
#define MAKE_PARAMS_1(type) type arg1
#define MAKE_PARAMS_2(type1, type2) type1 arg1, type2 arg2
#define MAKE_PARAMS_3(type1, type2, type3) type1 arg1, type2 arg2, type3 arg3
//.. add as many MAKE_PARAMS_* as you need

#define MAKE_PARAMS_N(N, ...) MAKE_PARAMS_##N(__VA_ARGS__)
#define MAKE_PARAMS_FORCE_N(N, ...) MAKE_PARAMS_N(N, __VA_ARGS__)
#define MAKE_PARAMS(...) MAKE_PARAMS_FORCE_N(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)


#define MAKE_ARGS_0()
#define MAKE_ARGS_1(type) arg1
#define MAKE_ARGS_2(t1, t2) arg1, arg2
#define MAKE_ARGS_3(t1, t2, t3) arg1, arg2, arg3
//.. add as many MAKE_ARGS_* as you have MAKE_PARAMS_*

#define MAKE_ARGS_N(N, ...) MAKE_ARGS_##N(__VA_ARGS__)
#define MAKE_ARGS_FORCE_N(N, ...) MAKE_ARGS_N(N, __VA_ARGS__)
#define MAKE_ARGS(...) MAKE_ARGS_FORCE_N(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)

#define CALL_ON_ALL(fun, ...) \
    void fun(MAKE_PARAMS(__VA_ARGS__)) { \
        for (int i = 0; i < delegates.size(); i++) \
            delegates[i]->fun(MAKE_ARGS(__VA_ARGS__)); \
    }

CALL_ON_ALL(doSomething, int, bool)

this will generate 这将产生

void doSomething(int arg1, bool arg2) { for (int i = 0; i < delegates.size(); i++) delegates[i]->doSomething(arg1, arg2); }

You can find more info how all this "mess" works here: Variadic recursive preprocessor macros - is it possible? 你可以在这里找到更多关于所有这些“混乱”如何工作的信息: Variadic递归预处理器宏 - 它可能吗?

In C, there are variadic argument macros, which would be quite convenient here I suppose. 在C中,有可变参数宏,这在我想是非常方便的。 They are not normally part of C++ though (subtle difference), though most compilers implementing them do not restrict them to C. 它们通常不是C ++的一部分(虽然有细微差别),但实现它们的大多数编译器都不会将它们限制为C.

In C++ you are better off reaching toward templates, especially with variadic templates in C++11 (and perfect forwarding); 在C ++中,您最好使用模板,特别是使用C ++ 11中的可变参数模板(以及完美转发); however I will try to use the preprocessor, for fun and profit hum... 但是我会尝试使用预处理器,以获得乐趣和 利润 ......

If we want a real C++03 preprocessor solution, then we shall call upon Boost.Preprocessor. 如果我们想要一个真正的C ++ 03预处理器解决方案,那么我们将调用Boost.Preprocessor。 The real kicker is the Preprocessor Sequence: an unbounded (in theory*) list of elements that can be manipulated at will, which gets us close enough to the convenience of variadic macros. 真正的踢球者是预处理器序列:一个无限制的(理论上*)元素列表,可以随意操作,这使我们足够接近可变宏的方便性。

But before we delve in there, we should remark that the names of the arguments are quite unimportant, and that only their type really matters. 但在我们深入研究之前,我们应该注意参数的名称是非常不重要的,只有它们的类型才真正重要。

I thus propose the following syntax: 因此,我提出以下语法:

CALL_ON_ALL(dosomething, (int)(bool))

// (int)(bool) is a preprocessor sequence in Boost.Preprocessor

The innards are a bit more subtle, and I am not sure to get them right on first try, it should probably go something like: 内脏有点微妙,我不确定第一次尝试是否正确,它可能应该是这样的:

#define CALL_ON_ALL(Name_, Args_)                                               \
    void Name_ ( BOOST_PP_ENUM( BOOST_PP_SEQ_SIZE(Args_), ARG_ENUM, Args_) ) {  \
        for (size_t i = 0, max = delegates.size(); i != max; ++i) {             \
            delegates[i]->                                                      \
                Name_ ( BOOST_PP_ENUM_PARAMS( BOOST_PP_SEQ_SIZE(Args_), arg );  \
        }                                                                       \
    }

#define ARG_ENUM(z, n, data)                                                    \
    BOOST_PP_SEQ_ELEM(data, n) BOOST_PP_CAT(arg, n)

Note: the complexity is not too terrific, there are N calls to BOOST_PP_SEQ_ELEM which is itself linear, leading to a quadratic complexity. 注意:复杂性不是太大,有N个调用BOOST_PP_SEQ_ELEM本身是线性的,导致二次复杂度。 I could not find a BOOST_PP_SEQ_* macro that could enumerate the arguments and place commas between them. 我找不到可以枚举参数在它们之间放置逗号的BOOST_PP_SEQ_*宏。

*In practice, there is a demo with close to a hundred elements in the docs, hope it suffice ;) *在实践中,文档中有近百个元素的演示,希望它足够;)

First, place parenthesis around your types, so the preprocessor can parse it. 首先,在您的类型周围放置括号,以便预处理器可以解析它。 So you will call CALL_ON_ALL like this: 所以你会像这样调用CALL_ON_ALL

CALL_ON_ALL(doSomething, (int) arg1, (bool) arg2)

Here are some macros that will retrieve the type and strip off the type(you will want to namespace them, I am leaving off the namespace just for demonstration): 下面是一些宏将检索类型并剥离类型(您将要命名它们,我只是为了演示而离开命名空间):

#define EAT(...)
#define REM(...) __VA_ARGS__
#define STRIP(x) EAT x
#define PAIR(x) REM x

These macros work like this. 这些宏的工作原理如下。 When you write STRIP((int) arg1) it will expand to arg1 . 当你编写STRIP((int) arg1) ,它将扩展为arg1 And when you write PAIR((int) arg1) it will expand to int arg1 . 当你编写PAIR((int) arg1) ,它将扩展为int arg1 Now next, you will want to do is to apply these macros to each argument that is passed in, so here is a simple APPLY macro that will let you do that for up to 8 arguments: 接下来,您将要做的是将这些宏应用于传入的每个参数,因此这里有一个简单的APPLY宏,它允许您为最多8个参数执行此操作:

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)

/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) CAT(APPLY_, NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
#define APPLY_1(m, x1) m(x1)
#define APPLY_2(m, x1, x2) m(x1), m(x2)
#define APPLY_3(m, x1, x2, x3) m(x1), m(x2), m(x3)
#define APPLY_4(m, x1, x2, x3, x4) m(x1), m(x2), m(x3), m(x4)
#define APPLY_5(m, x1, x2, x3, x4, x5) m(x1), m(x2), m(x3), m(x4), m(x5)
#define APPLY_6(m, x1, x2, x3, x4, x5, x6) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6)
#define APPLY_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7)
#define APPLY_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8)

Now heres how you can write your CALL_ON_ALL macro: 现在,您将了解如何编写CALL_ON_ALL宏:

#define CALL_ON_ALL(func, ...) \
void func(APPLY(PAIR, __VA_ARGS__)) { \
    for (int i = 0; i < delegates.size(); i++) \
        delegates[i]->func(APPLY(STRIP, __VA_ARGS__)); \
}

Note: This probably won't work in MSVC, since they have a buggy preprocessor(although there are workarounds). 注意:这可能不适用于MSVC,因为它们有一个错误的预处理器(尽管有解决方法)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM