我想创建一个宏来定义一个在对象列表上调用该函数的函数。它不一定是预处理器宏,但它应该可以工作。
我想写这样的东西:
CALL_ON_ALL(doSomething(int arg1, bool arg2))
我希望它能产生这个:
void doSomething(int arg1, bool arg2) {
for (int i = 0; i < delegates.size(); i++)
delegates[i]->doSomething(arg1, arg2);
}
我有一些有用的东西:
#define CALL_ON_ALL(defSig, callSig) \
void defSig { \
for (int i = 0; i < delegates.size(); i++) \
delegates[i]->callSig; \
}
问题在于我必须分别编写定义签名和调用签名:
CALL_ON_ALL(doSomething(int arg1, bool arg2), doSomething(arg1, arg2))
有更好的方法吗?
它不一定是预处理器宏。任何有效的方法都可以。
答案 0 :(得分:5)
尝试使用单独的参数(对于两个参数)进行签名:
#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 ); \
}
您可以将其复制为其他参数编号。
答案 1 :(得分:5)
这是高阶函数的一个很好的例子,它是将另一个函数作为参数的函数。在这种情况下,要在每个元素上调用函数。
以下定义了在每个元素上调用f
的高阶函数。它需要C ++ 11用于可变参数模板(args...
)。如果您没有可用的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...);
}
现在您可以使用以下语法调用它:
callOnAll(delegates, std::mem_fun<void,Delegate>(&Delegate::doSomething), 42);
std::mem_fun
事件为您要为每个委托调用的成员函数创建一个临时函数对象。您还可以应用其他函数,将指针作为其第一个参数。例如,这个小的lambda函数(也只是从C ++ 11开始):
callOnAll(delegates, [](Delegate *d){
d->doSomething(42);
});
几乎是一样的,只是另一种语法。
请参阅此处的示例:
std::mem_fun
)此代码略有不同的版本使用基于范围而非基于索引的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...);
}
为了您的信息,有std::for_each
,它可以完成您想要的功能,但功能稍微强一些,因为它本身不需要函数参数,但是您提供了lambda函数/只接受实例指针的仿函数。将以下代码与lambda函数进行比较:
std::for_each(delegates.begin(), delegates.end(), [](Delegate *d){
d->doSomething(42);
});
唯一的区别是我们必须传递.begin()
和.end()
迭代器,而不只是传递一个容器实例,例如delegates
。但是,还有很多其他algorithms defined in the standard library值得关注!
答案 2 :(得分:2)
我认为这就是你要找的东西:
#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)
这将生成
void doSomething(int arg1, bool arg2) { for (int i = 0; i < delegates.size(); i++) delegates[i]->doSomething(arg1, arg2); }
你可以在这里找到更多关于所有这些“混乱”的信息:Variadic recursive preprocessor macros - is it possible?
答案 3 :(得分:1)
在C中,有可变参数宏,我想这在这里很方便。它们通常不是C ++的一部分(虽然有细微差别),但实现它们的大多数编译器都不会将它们限制为C.
在C ++中,你最好接近模板,尤其是C ++ 11中的可变参数模板(以及完美的转发);但是我会尝试使用预处理器,以获得乐趣和利润哼......
如果我们想要一个真正的C ++ 03预处理器解决方案,那么我们将调用Boost.Preprocessor。真正的踢球者是预处理器序列:一个无限制的(理论上*)元素列表,可以随意操作,这使我们足够接近可变参数宏的便利性。
但在我们深入研究之前,我们应该注意参数的名称是非常不重要的,只有它们的类型才真正重要。
因此我建议使用以下语法:
CALL_ON_ALL(dosomething, (int)(bool))
// (int)(bool) is a preprocessor sequence in Boost.Preprocessor
内脏有点微妙,我不确定第一次尝试是否正确,它可能应该是这样的:
#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)
注意:复杂度并不太高,有{N}个调用BOOST_PP_SEQ_ELEM
本身是线性的,导致二次复杂度。我找不到可以枚举和参数的BOOST_PP_SEQ_*
宏。
*在实践中,文档中有近百个元素的演示,希望它就足够了;)
答案 4 :(得分:1)
首先,在您的类型周围放置括号,以便预处理器可以解析它。所以你会这样打电话给CALL_ON_ALL
:
CALL_ON_ALL(doSomething, (int) arg1, (bool) arg2)
以下是一些将检索类型并剥离类型的宏(您将要命名它们,我只是为了演示而离开命名空间):
#define EAT(...)
#define REM(...) __VA_ARGS__
#define STRIP(x) EAT x
#define PAIR(x) REM x
这些宏的工作原理如下。当您撰写STRIP((int) arg1)
时,它会扩展为arg1
。当你写PAIR((int) arg1)
时,它会扩展为int arg1
。接下来,您将要做的是将这些宏应用于传入的每个参数,因此这里有一个简单的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)
现在看看你如何编写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__)); \
}
注意:这可能不适用于MSVC,因为它们有一个错误的预处理器(尽管有解决方法)。