C++ 如何使用可选的格式化消息实现符合标准的assert宏?
使用可选的格式化消息实现符合标准的assert宏的方法是什么 我所拥有的在clang中工作,但是(正确地)触发了C++ 如何使用可选的格式化消息实现符合标准的assert宏?,c++,macros,assert,C++,Macros,Assert,使用可选的格式化消息实现符合标准的assert宏的方法是什么 我所拥有的在clang中工作,但是(正确地)触发了-Wgnu零变量宏参数警告,如果它在没有可选消息的情况下使用宏时打开(例如,通过-Wpedantic) 我们需要最大限度地使用预处理器,以便在不存在其他参数的情况下区分它们。但使用Boost.PP可以做到这一点: #include <boost/preprocessor/variadic/size.hpp> #include <boost/preprocessor/a
-Wgnu零变量宏参数
警告,如果它在没有可选消息的情况下使用宏时打开(例如,通过-Wpedantic
)
我们需要最大限度地使用预处理器,以便在不存在其他参数的情况下区分它们。但使用Boost.PP可以做到这一点:
#include <boost/preprocessor/variadic/size.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/logical/bool.hpp>
#include <boost/preprocessor/cat.hpp>
#define MyAssert(...) BOOST_PP_CAT(MY_ASSERT,BOOST_PP_BOOL(BOOST_PP_SUB(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1)))(__VA_ARGS__)
#define MY_ASSERT0(expr) MY_ASSERT1(expr,)
#define MY_ASSERT1(expression, ...) \
do { \
if(!(expression)) \
{ \
std::printf("Assertion error: " #expression " | " __VA_ARGS__); \
std::abort(); \
} \
} while(0)
这是辩论计数,但有一个转折点。当\uu VA\u ARGS\uu
是单个参数(无额外参数)时,N
解析为0。否则,解析为1。表达式后面最多可以有20个额外参数,任意数量的参数都将解析为相同的1。现在,我们只需将其插入之前使用boost的位置:
#define MyAssert(...) CONCAT(MY_ASSERT, HAS_ARGS(__VA_ARGS__))(__VA_ARGS__)
基本解决方案是使用
我有一个我并不特别自豪的解决方案
我们可以使用以下方法以简单形式获取第一个参数,并将其作为字符串:
#define VA_ARGS_HEAD(N, ...) N
#define VA_ARGS_HEAD_STR(N, ...) #N
请注意,在使用过程中,为了不收到警告,您应该执行VA_ARGS_头(\uu VA_ARGS_,)
(使用额外的,
),以便VA_ARGS_头
永远不会与单个参数一起使用(技巧取自)
我们定义了以下辅助函数:
#include <stdarg.h>
#include <stdio.h>
inline int assertionMessage(bool, const char *fmt, ...)
{
int r;
va_list ap;
va_start(ap, fmt);
r = vprintf(fmt, ap);
va_end(ap);
return r;
}
请注意,assertionMessage
的名称中没有printf
。这是经过深思熟虑的,旨在避免编译器使用额外的“
参数为其调用提供与格式字符串相关的警告。不利的一面是,我们没有得到与格式字符串相关的警告,如果它们有用的话。assert(expression&&“Bad!”)
?@StoryTeller,但这不支持错误消息的内联格式。是的。对。但是,…
不是可选的。“我们需要解决这个问题。”MatthieuBrucher在一些项目中说。无论我现在是否可以在生产中使用,基于C++17的解决方案在任何情况下都会受到欢迎。@DmytroDadyka我有兴趣看到基于它的解决方案。谢谢!可选性不是伪造的,因为有时没有额外有用的上下文添加到失败的表达式中。注意:失败的表达式始终打印在冒号后面。你可能会说可选性是假的,因为我也在打印
——但这可能会被一个空格所取代,或者,如果你愿意的话,制作一个更复杂的宏,只在\uu VA\u ARGS\uuuuu
不是空的情况下才打印它(尽管根据标准,它永远不应该是空的:))哦,是的,我的坏。。。在这种情况下,您可以使用@StoryTeller的技巧将#expression
作为MyAssert
的第二个参数。虽然此解决方案比@yairchu的更复杂,但它的优点是允许使用uuuu属性((uuu格式uu(u printf u,2,3))注释断言消息
)
获取有关传递到断言宏的无效字符串格式和参数组合错误的有用警告。根据@yairchu的回答,添加属性会导致-Wformat extra args
警告,因为额外的“
传递到断言消息中。这在Visual Studio中似乎不正确。传递到我的资产1
的\uu VA\u ARGS\uuu
作为单个参数传递@yairchu-\uuuu VA\u ARGS\uuu
作为剩余令牌(带逗号)的单个集合是标准的。不排除出现MSVC错误的可能性,但您的评论信息不太丰富。@StoryTeller我的意思是所有带有括号的\u VA\u ARGS\u
在我的资产1
中变成表达式
,这里的目的只是让单个参数成为expression
,其余的参数成为MY_ASSERT1
的\uu VA_ARGS\uu
@yairchu-那么,肯定是MSVC的错误。用另一个间接层来解决这个问题可能是可能的,也可能是不可能的。¯\_(ツ)_/¯
#define MyAssert(expression, msg) \
do { \
if(!(expression)) \
{ \
std::cerr << msg; \
abort(); \
} \
} while(0)
MyAssert(true, "message " << variable << " units");
#define VA_ARGS_HEAD(N, ...) N
#define VA_ARGS_HEAD_STR(N, ...) #N
#include <stdarg.h>
#include <stdio.h>
inline int assertionMessage(bool, const char *fmt, ...)
{
int r;
va_list ap;
va_start(ap, fmt);
r = vprintf(fmt, ap);
va_end(ap);
return r;
}
#define MyAssert(...) \
do { \
if(!(VA_ARGS_HEAD(__VA_ARGS__, ))) \
{ \
printf("Assertion error: %s | ", VA_ARGS_HEAD_STR(__VA_ARGS__, )); \
assertionMessage(__VA_ARGS__, ""); \
abort(); \
} \
} while(0)