C++ 如何编写c++;使用不同数量的信息参数断言宏?
我正在尝试编写一个宏C++ 如何编写c++;使用不同数量的信息参数断言宏?,c++,c++11,assert,variadic-templates,variadic-macros,C++,C++11,Assert,Variadic Templates,Variadic Macros,我正在尝试编写一个宏dbgasert,类似于标准的assert。除了assert所做的之外,我还想dbgassert打印任意数量的附加参数(包含调试信息) 到目前为止,我已经在下面列出了,这是改编自。但我的代码中存在变量模板或宏的问题。如果我至少使用了一个附加参数(OK行),那么dbgassert将按预期工作。但如果我没有给出额外的参数,编译就会失败(问题行) 我有一些可变模板编程的经验(比如如何打印元组),但我以前从未使用过可变宏 请解释一下写这个可变参数宏组合的正确方法是什么 顺便问一下,有
dbgasert
,类似于标准的assert
。除了assert
所做的之外,我还想dbgassert
打印任意数量的附加参数(包含调试信息)
到目前为止,我已经在下面列出了,这是改编自。但我的代码中存在变量模板或宏的问题。如果我至少使用了一个附加参数(OK行),那么dbgassert
将按预期工作。但如果我没有给出额外的参数,编译就会失败(问题行)
我有一些可变模板编程的经验(比如如何打印元组),但我以前从未使用过可变宏
请解释一下写这个可变参数宏组合的正确方法是什么
顺便问一下,有人能解释一下宏中的#EX
魔力吗?它显示了表达式并在gcc4.8.1上对我有效。它是否得到普遍支持
谢谢
代码:
//更正了每个注释的保留标识符问题和假设问题
#包括
#包括
使用名称空间std;
模板
void realdbgassert(常量字符*msg,常量字符*file,int行,Args…Args){
不能你必须写函数的内容\u assert
-如果你指定它是extern
,你应该将包含函数定义的文件附加到编译过程中。如果你不知道如何编写多文件程序,我真的帮不了你。你假设assert
是im通过调用\uu assert
来实现。这很可能是一个特定实现的工作方式,但在一般情况下肯定不能依赖
相反,请执行以下操作:测试您的条件并在出现故障时将诊断信息发送到标准错误,然后调用std::abort
您的问题是\uuu VA\u ARGS\uuuuu>的值,该值在问题情况下为空。因此,当预处理器展开realdbgassert(#EX,uu文件,u行,u VA\u ARGS_uuu)
,结果是一个未完成的参数列表realdbgassert(“1>2”,“foo.c”,42,)
。请注意,由于\uu VA\u ARGS\uuuuuu>的空扩展,参数列表未正确终止
要解决这个问题,您需要使用某种技巧。最好的解决方案是,调整环境,使\uu VA\u ARGS\uuu
包含最后一个无条件参数,并在函数调用结束时将其与可选参数一起传递。这是最好的,因为它是标准C
我知道的另一个修复是对该语言的gcc扩展:有关更多详细信息,请参见gcc文档页,但您可以通过在\uu VA_ARGS\uuu
前面添加一个双精度\u35;
来修复宏:
#define dbgassert(EX,...) \
(void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, ## __VA_ARGS__),0))
附言:
#
是预处理器的字符串化操作符:它将宏参数的值转换为字符串文字,即粘贴“1>2”
而不是粘贴<1>2”
放置标记粘贴操作符(##)在\uuuu VA\u ARGS\uuuu
之前。如果\uuu VA\u ARGS\uuu
为空,则删除\uuuu VA\u ARGS\uuuu
之前的逗号
您的宏将是:
#define dbgassert(EX,...) \
(void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, ##__VA_ARGS__),0))
请注意,令牌粘贴是GNU CPP扩展,正如正确提到的其他海报之一(请参阅)
MS编译器(在VS2010下测试)不需要标记粘贴,如果\uuu VA\u ARGS\uuu
为空,它只需删除尾随逗号。请参见:作为说明,除了@cmaster和@ds27680的解决方案外,我还找到了另一种解决尾随逗号问题的方法。由于使用\uu VA\u ARGS\uu
会导致额外的逗号,我可以打包\uu VA\u ARGS__
转换为std::tuple
或函数调用,并使用tuple/result作为实函数的参数。现在空\uuuu VA\u ARGS\uu
不会有问题,因为它被压缩为一个有效值(即空元组或空函数的返回值)。我想这在代码中比较长,但在不涉及##
的情况下更便于移植
上述两种情况分别显示在下面代码中的dbgassert
和dbgassert1
宏中
#include <cassert>
#include <iostream>
#include <tuple>
using namespace std;
template <typename ...Args>
string print_tuple(tuple<Args...> tp) {
return ""; //print the tuple...
}
template <typename ...Args>
void realdbgassert(tuple<Args...> info,const char *msg, const char *file, int line) {
cout << "Assertion failed! \nFile " << file << ", Line " << line << endl
<< " Expression: " << msg << endl
<< " Info: " << print_tuple(info) << endl;
std::abort();
}
#define dbgassert(EX,...) \
(void)((EX) || (realdbgassert (std::tie(__VA_ARGS__),#EX,__FILE__, __LINE__),0))
void realdbgassert1(string info,const char *msg, const char *file, int line) {
cout << "Assertion failed! \nFile " << file << ", Line " << line << endl
<< " Expression: " << msg << endl
<< " Info: " << info << endl;
std::abort();
}
template <typename ...Args>
string print_info(Args ... args) {
return ""; //print stuff
}
#define dbgassert1(EX,...) \
(void)((EX) || (realdbgassert1 (print_info(__VA_ARGS__),#EX,__FILE__, __LINE__),0))
int main() {
dbgassert(1>2,"right","yes"); //OK
dbgassert(1>2,"right"); //OK
dbgassert(1>2); //OK now
dbgassert1(1>2); //OK too
}
#包括
#包括
#包括
使用名称空间std;
模板
字符串打印元组(元组tp){
返回“”;//打印元组。。。
}
模板
void realdbgassert(元组信息,常量字符*消息,常量字符*文件,int行){
您是否正在使用。无论如何,什么是\u assert
?当然不是标准函数。或者您只是试图使用您的实现没有的实现细节?../ccZ47ZOL.o:test7.cpp:.text$\u Z14\u realdbgassertipkceevs1\u S1\u iDpT\uz14\u realdbgassertipkceevs1\u iDpT]+0x65):未定义的对
@断言的引用。嗯,你期望得到什么?你还没有提供\uu断言
的定义。或者你是说你的标准库实现提供了这样一个函数?我想\uu断言
是断言
函数背后的函数。我在这里看到了它:我想是看台ard assert不能是实函数,而且在assert
宏后面必须有实函数,对吗?因为assert
报告问题行,如果assert
是实函数,它会报告
中的行号。没有标准处理程序(即实assert函数)吗在标准中?谢谢。但是我对变量宏还有其他问题。请看更新。@TingL看到我在手边努力写一个答案,结果发现你比我快。很好的解释。你得到了我的支持。我已经在考虑将无条件参数包装到一个对象中,以便它们可以隐式传递这样的变量成员函数包装器
#define dbgassert(EX,...) \
(void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, ##__VA_ARGS__),0))
#include <cassert>
#include <iostream>
#include <tuple>
using namespace std;
template <typename ...Args>
string print_tuple(tuple<Args...> tp) {
return ""; //print the tuple...
}
template <typename ...Args>
void realdbgassert(tuple<Args...> info,const char *msg, const char *file, int line) {
cout << "Assertion failed! \nFile " << file << ", Line " << line << endl
<< " Expression: " << msg << endl
<< " Info: " << print_tuple(info) << endl;
std::abort();
}
#define dbgassert(EX,...) \
(void)((EX) || (realdbgassert (std::tie(__VA_ARGS__),#EX,__FILE__, __LINE__),0))
void realdbgassert1(string info,const char *msg, const char *file, int line) {
cout << "Assertion failed! \nFile " << file << ", Line " << line << endl
<< " Expression: " << msg << endl
<< " Info: " << info << endl;
std::abort();
}
template <typename ...Args>
string print_info(Args ... args) {
return ""; //print stuff
}
#define dbgassert1(EX,...) \
(void)((EX) || (realdbgassert1 (print_info(__VA_ARGS__),#EX,__FILE__, __LINE__),0))
int main() {
dbgassert(1>2,"right","yes"); //OK
dbgassert(1>2,"right"); //OK
dbgassert(1>2); //OK now
dbgassert1(1>2); //OK too
}