在C++中使用X宏时处理尾随逗号的最佳方法

wye*_*r33 1 c++ c-preprocessor c++11 c++14

使用X宏时,处理额外尾随逗号的最佳方法是什么?具体来说,我在文件中有以下设置test01.cpp

struct Foo {
    #define X(name,val) int name;
    #include "test01.def"
    #undef X

    Foo() :
        #define X(name,val) name(val),
        #include "test01.def"
        #undef X
    {}
};

int main(){
    Foo foo;
}
Run Code Online (Sandbox Code Playgroud)

test01.def,我有

X(foo,1)
X(bar,23)
Run Code Online (Sandbox Code Playgroud)

由于错误,这不会编译

test01.cpp: In constructor 'Foo::Foo()':
test01.cpp:10:5: error: expected identifier before '{' token
     {}
Run Code Online (Sandbox Code Playgroud)

基本上,在成员初始化列表中的最后一个元素之后有一个尾随逗号.现在,我们可以通过添加虚拟变量来解决这个问题:

struct Foo {
private:
    void * end;
public:
    #define X(name,val) int name;
    #include "test01.def"
    #undef X

    Foo() :
        #define X(name,val) name(val),
        #include "test01.def"
        #undef X
        end(nullptr)
    {}
};

int main(){
    Foo foo;
}
Run Code Online (Sandbox Code Playgroud)

但是,这有点难看.因此,有没有更好的方法来处理成员初始化列表中的尾随逗号?


编辑1

这是另一个仍然有点丑陋的选择:

struct Foo {
    #define X(name,val) int name;
    #include "test01.def"
    #undef X

    Foo() :
        #define X(name,val) name(val),
        #define XLAST(name,val) name(val)
        #include "test01.def"
        #undef XLAST
        #undef X
    {}
};

int main(){
    Foo foo;
}
Run Code Online (Sandbox Code Playgroud)

随着

#ifndef XLAST
    #define XLAST X
    #define CLEANUP
#endif

X(foo,1) 
XLAST(bar,23)

#ifdef CLEANUP
    #undef XLAST
    #undef CLEANUP
#endif
Run Code Online (Sandbox Code Playgroud)

基本上,我们定义宏XLAST来处理最后的逗号.如果我们使用XLAST,我们必须手动取消定义它X,但是我们在没有明确定义它的情况下自动执行此操作.

Pra*_*ian 5

既然你已经标记了这个C++ 14,解决这个问题最简单的方法是使用大括号或相等的初始化程序小号,而不是mem-initializer秒.

struct Foo {
    #define X(name,val) int name = val;
    #include "test01.def"
    #undef X

    // Foo() {} // uncomment if you do not want Foo to be an aggregate
};
Run Code Online (Sandbox Code Playgroud)

如果您想坚持使用预处理器解决方案,可以使用Boost.Preprocessor来执行此操作.您需要更改数据成员定义的格式,以便形成序列.

#define FOO_MEMBERS ((int,i,10)) ((long,j,20))
Run Code Online (Sandbox Code Playgroud)

我添加了指定任意数据类型的功能.

首先让我们声明并初始化这些数据成员

struct Foo
{
    #define OP(s, data, elem) BOOST_PP_TUPLE_ELEM(3, 0, elem) \
                              BOOST_PP_TUPLE_ELEM(3, 1, elem) = \
                              BOOST_PP_TUPLE_ELEM(3, 2, elem);
    BOOST_PP_SEQ_FOR_EACH(OP, , FOO_MEMBERS)
    // expands to 
    //      int i = 10; long j = 20;
    #undef OP

    Foo() = default;  // default constructor
};
Run Code Online (Sandbox Code Playgroud)

BOOST_PP_SEQ_FOR_EACH将扩展OP序列中每个元素的宏FOO_MEMBERS.

BOOST_PP_TUPLE_ELEM只需从其元组参数中提取单个元素.

接下来,让我们给出Foo一个构造函数,它接受与每个数据成员相对应的参数并初始化它.

#define OP(s, data, elem) (BOOST_PP_TUPLE_ELEM(3, 0, elem) BOOST_PP_TUPLE_ELEM(3, 1, elem))
Foo(
    BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(OP, , FOO_MEMBERS))
    // expands to
    //      int i, long j
) :
#undef OP

#define OP(s, data, elem) (BOOST_PP_TUPLE_ELEM(3, 1, elem)(BOOST_PP_TUPLE_ELEM(3, 1, elem)))
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(OP, , FOO_MEMBERS))
// expands to
//      i(i), j(j)
#undef OP
{}
Run Code Online (Sandbox Code Playgroud)

我们用来BOOST_PP_SEQ_ENUM从扩展的结果中生成一个逗号分隔的列表BOOST_PP_SEQ_FOR_EACH.

现场演示