如何检索C99可变宏的最后一个参数?
How can I retrieve the last argument of a C99 variadic macro?
Visual Studio对于失败的static_assert的错误消息完全由错误代码和static_assert的第二个参数组成,没有任何额外的消息表明这是静态断言失败。我想做一个宏来解决这个问题。例如,作为第一次尝试:
#define STATIC_ASSERT(x) static_assert(x, "static assertion failed: " #x)
您遇到的第一个问题是C预处理器不理解< >
是封闭分隔符,这会导致模板的语法错误。
template <typename T, typename U>
auto SafeMultiply(T x, U y) -> decltype(x * y)
{
STATIC_ASSERT(std::is_same<T, U>::value);
STATIC_ASSERT(!std::numeric_limits<T>::is_signed);
if (x > (std::numeric_limits<decltype(x * y)>::max)())
throw std::overflow_error("multiplication overflow");
return x * y;
}
这是非法的,因为在第一个STATIC_ASSERT
中,T和U之间的逗号被解释为分隔两个宏参数,而不是模板参数。C预处理器抛出错误,因为STATIC_ASSERT
宏只接受一个参数。
这个问题的两个主要解决方案是使用双括号和最近使用的可变宏:
// Invoke the macro this way...
STATIC_ASSERT((std::is_same<T, U>::value));
// ...or define it this way:
#define STATIC_ASSERT(...) static_assert((__VA_ARGS__),
"static assertion failed: " #__VA_ARGS__)
后一种解决方案更好,只需要更改宏定义。(在新的定义中,__VA_ARGS__
周围的额外括号是为了在一些奇怪的情况下保持适当的操作顺序。在这个特定的宏中,这可能无关紧要,但在宏定义中的宏参数周围加上括号是一个好习惯。
现在,如果我想改变我的STATIC_ASSERT
宏像标准c++ static_assert
的消息,但添加一个前缀的消息?有点像这样,但支持使用std::is_same<T, U>
而不使用双括号:
// Causes a syntax error :(
#define STATIC_ASSERT(expr, msg) static_assert((expr),
"static assertion failed: " msg)
STATIC_ASSERT(std::is_same<T, U>, "x and y are not of the same type");
如果我能得到可变宏的最后一个参数,它就可以工作了:
// I wish this'd work
#define STATIC_ASSERT(..., msg) static_assert((__VA_ARGS__),
"static assertion failed: " msg)
STATIC_ASSERT(std::is_same<T, U>, "x and y are not of the same type");
但是由于这是不合法的,我如何合法地获得...
宏参数集的最后一个参数?当然,我可以反转参数的顺序,但这就和static_assert
不一样了。
在一般情况下,获取最后一个宏参数没有简单的方法,但是您可以轻松实现一个版本,使参数列表的最后一个元素达到预定的最大值。对于实际代码,64个参数通常就足够了。
您所需要做的就是计算传递的参数的数量,然后从列表中返回元素N-1
:
// count arguments
#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
// utility (concatenation)
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_GET_ELEM(N, ...) M_CONC(M_GET_ELEM_, N)(__VA_ARGS__)
#define M_GET_ELEM_0(_0, ...) _0
#define M_GET_ELEM_1(_0, _1, ...) _1
#define M_GET_ELEM_2(_0, _1, _2, ...) _2
#define M_GET_ELEM_3(_0, _1, _2, _3, ...) _3
#define M_GET_ELEM_4(_0, _1, _2, _3, _4, ...) _4
#define M_GET_ELEM_5(_0, _1, _2, _3, _4, _5, ...) _5
#define M_GET_ELEM_6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define M_GET_ELEM_7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define M_GET_ELEM_8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define M_GET_ELEM_9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define M_GET_ELEM_10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) _10
// Get last argument - placeholder decrements by one
#define M_GET_LAST(...) M_GET_ELEM(M_NARGS(__VA_ARGS__), _, __VA_ARGS__ ,,,,,,,,,,,)
您可以将此扩展到任意大的有限数量,只需几分钟的复制和粘贴。
我有个想法:
#define STATIC_ASSERT(message, ...)
static_assert((__VA_ARGS__), "static assertion failed: " message)
STATIC_ASSERT("x and y must have the same type", std::is_same<T, U>);
- 更改可变参数模板中的最后一个类型
- 如何更改参数包中的最后一个参数
- 模板参数重载,最后一个参数为非类型名
- 此模板函数上的最后一个参数有什么用?
- 如何在参数包不是最后一个参数的C++中编写可变参数模板函数?
- C :为什么它总是首先执行最后一个参数
- std::transform的最后一个参数
- 基于最后一个参数值的构造函数
- 最后一个参数将覆盖 SQLite 绑定中的先前参数
- 通过去掉最后一个参数,将c++中的for(a;b;c)简化为for(x;y;)
- 当可变参数模板不是最后一个参数时,如何重载它们
- 默认参数:只能保留最后一个参数
- 有人能给我解释一下这个函数的最后一个参数吗?
- 如何检测可变模板中的第一个和最后一个参数
- 我如何传递一个直接的4位值到_mm256_blend_pd的最后一个参数
- 如何检索C99可变宏的最后一个参数?
- 非最后一个参数包编译错误
- 获取变量模板的除最后一个参数外的所有参数
- 包扩展不在最后一个参数中的可变参数函数模板
- 忽略最后一个参数的宏