在 C++ 中使用 X 宏时处理尾随逗号的最佳方法
Best way to deal with a trailing comma when using X macros in C++
在使用 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;
}
在test01.def
,我有
X(foo,1)
X(bar,23)
由于错误而无法编译
test01.cpp: In constructor 'Foo::Foo()':
test01.cpp:10:5: error: expected identifier before '{' token
{}
基本上,在成员初始值设定项列表中的最后一个元素之后有一个尾随逗号。现在,我们可以通过添加一个虚拟变量来解决这个问题:
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;
}
然而,这有点丑陋。因此,有没有更好的方法来处理成员初始值设定项列表中的尾随逗号?
编辑 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;
}
连同
#ifndef XLAST
#define XLAST X
#define CLEANUP
#endif
X(foo,1)
XLAST(bar,23)
#ifdef CLEANUP
#undef XLAST
#undef CLEANUP
#endif
基本上,我们定义宏XLAST
来处理最后的逗号。如果我们使用 XLAST
,我们必须像 X
一样手动取消定义它,但如果我们没有明确定义它,我们会自动执行此操作。
既然你已经标记了这个 C++14,解决这个问题的最简单方法是使用 brace-or-equal-initializers 而不是 mem-initializer
s。
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
};
如果您想坚持使用预处理器解决方案,可以使用 Boost.Preprocessor to do this. You'll need to change the format of your data member definitions so it forms a sequence。
#define FOO_MEMBERS ((int,i,10)) ((long,j,20))
我也添加了指定任意数据类型的功能。
首先让我们声明并初始化这些数据成员
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
};
BOOST_PP_SEQ_FOR_EACH 将为序列 FOO_MEMBERS
.
中的每个元素扩展宏 OP
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
{}
我们使用 BOOST_PP_SEQ_ENUM
从 BOOST_PP_SEQ_FOR_EACH
.
扩展的结果生成一个逗号分隔的列表
如果您将 end
声明为 std::nullptr_t
类型,编译器可能会对其进行优化并将其删除。 reader.
的意图很明确
struct Foo {
private:
static std::nullptr_t end;
或者,您可以声明 char end[0];
(这不会使用任何 space),但一些编译器可能会拒绝。
当然,如,您可以使用一些 X-macro 友好的结构来代替。
在使用 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;
}
在test01.def
,我有
X(foo,1)
X(bar,23)
由于错误而无法编译
test01.cpp: In constructor 'Foo::Foo()':
test01.cpp:10:5: error: expected identifier before '{' token
{}
基本上,在成员初始值设定项列表中的最后一个元素之后有一个尾随逗号。现在,我们可以通过添加一个虚拟变量来解决这个问题:
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;
}
然而,这有点丑陋。因此,有没有更好的方法来处理成员初始值设定项列表中的尾随逗号?
编辑 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;
}
连同
#ifndef XLAST
#define XLAST X
#define CLEANUP
#endif
X(foo,1)
XLAST(bar,23)
#ifdef CLEANUP
#undef XLAST
#undef CLEANUP
#endif
基本上,我们定义宏XLAST
来处理最后的逗号。如果我们使用 XLAST
,我们必须像 X
一样手动取消定义它,但如果我们没有明确定义它,我们会自动执行此操作。
既然你已经标记了这个 C++14,解决这个问题的最简单方法是使用 brace-or-equal-initializers 而不是 mem-initializer
s。
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
};
如果您想坚持使用预处理器解决方案,可以使用 Boost.Preprocessor to do this. You'll need to change the format of your data member definitions so it forms a sequence。
#define FOO_MEMBERS ((int,i,10)) ((long,j,20))
我也添加了指定任意数据类型的功能。
首先让我们声明并初始化这些数据成员
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
};
BOOST_PP_SEQ_FOR_EACH 将为序列 FOO_MEMBERS
.
OP
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
{}
我们使用 BOOST_PP_SEQ_ENUM
从 BOOST_PP_SEQ_FOR_EACH
.
如果您将 end
声明为 std::nullptr_t
类型,编译器可能会对其进行优化并将其删除。 reader.
struct Foo {
private:
static std::nullptr_t end;
或者,您可以声明 char end[0];
(这不会使用任何 space),但一些编译器可能会拒绝。
当然,如