在类名列表上迭代预处理器宏

时间:2014-08-21 13:52:22

标签: c++ macros

我想在类名列表上运行宏,以避免复制/粘贴错误和麻烦。

想象一下,作为一个简单的例子,我的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 )

我已经看过可变参数宏但是,如果我理解了这个概念,你必须定义与最终参数数量一样多的宏;在我的情况下,这根本不可行。

这有可能吗?

感谢您提供任何帮助和/或反馈。

5 个答案:

答案 0 :(得分:10)

如果您对拥有一个类列表感到满意,可以使用以下技巧:

#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

答案 1 :(得分:2)

您可以使用可变参数函数模板:

#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 C1\n"; } };
struct C2 { static void allocate() { std::cout << "allocated C2\n"; } };
struct C3 { static void allocate() { std::cout << "allocated C3\n"; } };

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();
}

答案 2 :(得分:1)

使用Boost.Preprocessor

#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 )

答案 3 :(得分:1)

要略微提高answer given by LindyLancer建议X-macros,您可以拥有单独的文件

 // 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"

答案 4 :(得分:0)

您可以定义迭代的通用宏,但它的一次性定义很难看。这是因为每个参数都需要一个宏定义,最多可达编译器支持的最大嵌套级别数(我相信最小值至少为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)