在类名列表上迭代预处理器宏
Iterate a preprocessor macro on a list of class names
我想在类名列表上运行宏,以避免复制/粘贴错误和麻烦。
想象一下,作为一个简单的例子,我的SDK中的每个类在使用之前都需要调用一个静态分配方法。因此,每次添加新类时,都必须在初始化时手动添加以下行:
MyNewClass::allocate();
对于初始化和销毁,我也需要做同样的事情。
所以,而不是每次手工这样做,我想知道是否有一种方法来编写一个列表的所有我的类名的地方,然后定义一个宏调用相应的方法在列表中的每个类。
#define ALLOCATE( TheClass ) TheClass ## ::allocate();
但是不只是传递TheClass
作为参数,我想传递我的类列表。通过调用:
ALLOCATE_ALL( ClassA, ClassB, ClassC )
将展开为:
ClassA::allocate();
ClassB::allocate();
ClassC::allocate();
最终,我希望能够定义一个类列表,并有多个宏迭代它。
ALLOCATE_ALL( MyClassList )
INIT_ALL( MyClassList )
DESTROY_ALL( MyClassList )
我已经看了一下可变宏,但是,如果我正确理解了这个概念,你必须定义和最终参数数量一样多的宏;这在我的情况下是不可行的。
这可能吗?
感谢您的帮助和/或反馈。
如果您对只有一个类列表感到满意,您可以使用以下技巧:
#define MY_CLASSES X(ClassA) X(ClassB) X(ClassC)
然后你可以这样做:
#define X(a) a::allocate();
MY_CLASSES
#undef X
要做其他事情,你可以这样做:
#define X(a) a::init();
MY_CLASSES
#undef X
您可以使用可变函数模板:
#include <iostream>
// Thanks to Jarod42 for improving it !
template <class... TClasses>
void allocateAll() {
std::initializer_list<int>{(TClasses::allocate(), void(), 0)...};
}
struct C1 { static void allocate() { std::cout << "allocated C1n"; } };
struct C2 { static void allocate() { std::cout << "allocated C2n"; } };
struct C3 { static void allocate() { std::cout << "allocated C3n"; } };
int main()
{
allocateAll<C1, C2, C3>();
return 0;
}
输出:
allocated C1
allocated C2
allocated C3
调用函数的顺序与传递类的顺序相同。
你也可以集中类列表:
// Use a template instead of a function so you can typedef it
template <class... TClasses>
struct ClassesList {
static void allocateAll() {
std::initializer_list<int>{(TClasses::allocate(), void(), 0)...};
}
// Add any other utilities here
private:
ClassesList();
};
// Declare your list
using MyClassesList = ClassesList<C1, C2, C3>;
int main(int, char**) {
// Just as before
MyClassesList::allocateAll();
}
要稍微改进LindyLancer建议的x -宏的答案,您可以有一个单独的文件
// file my_classes.def
#ifndef MY_CLASS
#error MY_CLASS should be defined before including my_classes.def
MY_CLASS(ClassA)
MY_CLASS(ClassB)
/// etc...
#undef MY_CLASS
那么你要把包含几次,例如
#define MY_CLASS(Classname) class Classname;
#include "my_classes.def"
之后,在一些初始化器(或您的main
)中
#define MY_CLASS(Classname) Classname::init();
#include "my_classes.def"
使用Boost。预处理:
#include <boost/preprocessor/seq/for_each.hpp>
#define MyClassList
(ClassA)
(ClassB)
(ClassC)
#define ALLOCATE_ALL( R, DATA, ELEM )
ELEM :: allocate();
#define INIT_ALL( R, DATA, ELEM )
ELEM :: init();
//...
BOOST_PP_SEQ_FOR_EACH( ALLOCATE_ALL, _, MyClassList )
BOOST_PP_SEQ_FOR_EACH( INIT_ALL, _, MyClassList )
您可以定义一个迭代的泛型宏,但是它的一次性定义是丑陋的。这是因为您确实需要为每个参数定义一个宏,直至编译器支持的最大嵌套级别(我认为最小值至少为63,但GCC仅受可用内存的限制)。但由于它是通用的,你可能会发现它的其他用途。
对于最多5个,可能的实现是:
#define M_ITER(M, ...)
M_ITER_(__VA_ARGS__, _5, _4, _3, _2, _1)(M, __VA_ARGS__)
#define M_ITER_(_1, _2, _3, _4, _5, X, ...) M_ITER ## X
#define M_ITER_1(M, X) M(X)
#define M_ITER_2(M, X, ...) M(X) M_ITER_1(M, __VA_ARGS__)
#define M_ITER_3(M, X, ...) M(X) M_ITER_2(M, __VA_ARGS__)
#define M_ITER_4(M, X, ...) M(X) M_ITER_3(M, __VA_ARGS__)
#define M_ITER_5(M, X, ...) M(X) M_ITER_4(M, __VA_ARGS__)
这就是BOOST_PP_SEQ_FOR_EACH
的基本实现方式。
然后,你可以像这样使用它:
#define ALLOCATE_ALL(...) M_ITER(ALLOCATE, __VA_ARGS__)
#define INIT_ALL(...) M_ITER(INIT, __VA_ARGS__)
#define DESTROY_ALL(...) M_ITER(DESTROY, __VA_ARGS__)
ALLOCATE_ALL(ClassA, ClassB, ClassC)
INIT_ALL(ClassA, ClassB, ClassC)
DESTROY_ALL(ClassA, ClassB, ClassC)
Lindydancer接受的答案对我来说是不满意的,因为它依赖于反复重新定义X
宏,这在使用上似乎很神秘,可能导致碰撞。
相反,我更喜欢接受宏的名称作为参数的变体:
#define FOR_EACH_CLASS(macro)
macro(ClassA)
macro(ClassB)
macro(ClassC)
可以这样使用:
#define CALL_ALLOCATE(classname) classname::allocate();
FOR_EACH_CLASS(CALL_ALLOCATE)
#undef CALL_ALLOCATE /* optional undef */
有一个相关问题的另一个答案:X-Macros的实际使用,提供了更多的技术示例。(请注意,在该问题中,可接受的答案再次依赖于使用特定名称重新定义宏,而我链接的那个宏接受宏名称作为参数。)
- 错误:无效的预处理指令 #i 的意思是 #if?
- C++预处理会生成变量成员、资源库和映射
- 使用预处理指令检查是否包含标头?
- 预处理的 C/C++ 文件是否特定于计算机?
- 使用 GCC 对 C 文件进行部分预处理(不删除 "define" 指令)
- 在 CPLEX 中求解线性规划,无需剪切和预处理
- CPP -D 选项,用于预处理 Fortran 代码
- 错误:粘贴"tmp_UINT"和"+"未提供有效的预处理令牌
- 任务计划程序库的预处理不起作用 - 多定义错误
- Eclipse 问题 - 编译期间不考虑 .c 和 .cpp 文件中定义的预处理
- 使用python预处理后,C++(opencv)中的垫子类型数据与image_to_array相同
- Howo 使用 cl 预处理为 masm 组装生成一个单独的文件
- 我有一个预处理的 C/C++ 源文件 (cacti.i).如何从这个 .i 文件生成可执行二进制文件,以便我可以像 ./
- 如何使用Visual Studio C/C++编译器(cl.exe)来预处理我的objective-C代码
- 是具有预处理前分支实现的结构违反ODR
- 与不完整的Cholesky预处理的共轭梯度返回特征库的意外错误
- Visual Studio C - 无法输出预处理文件
- 在海湾合作委员会中加快宏观预处理的任何方法
- Xmllint未设置,而在路径中找不到XMLLINT;跳过XML预处理
- 当头文件被预处理时是否有一个预处理器选项显示?