[英]Accessing variable values within a macro
前段时间,我为c和c ++程序制作了这个漂亮的断言宏
#define ASSERT(truthy, message) \
if (!(truthy)) \
{\
cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << endl;\
}
Scatter ASSERT会在整个代码中调用它,并且只要truthy
值不真实,它就会发出警告! 在开发过程中非常方便,以提醒您潜在的错误。
前
ASSERT(filesFound > 0, "Couldn't find any files, check your path!");
当filesFound为0时,宏将打印出来
找不到任何文件,检查你的路径! 在文件openFiles.c的第27行。 检查是filesFound> 0
现在我想要它打印,给我更多相关信息,是传递到truthy
参数的任何变量的值。 像这样
找不到任何文件,检查你的路径! 在文件openFiles.c的第27行。 检查是filesFound> 0,filesFound是0
我想知道,这似乎是类似lisp的领域,是否有任何黑魔法c预处理可用于评估变量和函数的值,而不评估truthy
声明?
我假设很失望。
我一直使用的另一种解决方案是在宏中支持varargs,然后强制断言用户指定相关的消息/变量 - 每次都是一些额外的工作,但从好的方面来说,你可以完全得到您想要的格式,并包含“truthy”位中没有的信息,例如:
#define ASSERT(truthy, message, ...) \
if (!(truthy)) \
{\
MyAssertHandler(__LINE__, __FILE__, #truthy, message, ##__VA_ARGS__);
}
那么你的处理程序只是一个相当标准的var-arg函数,它可以使用例如vsnprintf生成消息并输出它,例如我的头顶:
void MyAssertHandler(int line, const char* file, const char* expressionStr, const char* format, ...)
{
// Note: You probably want to use vsnprintf instead to first generate
// the message and then add extra info (line, filename, etc.) to
// the actual output
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
// Log to bug database, DebugBreak() if a debugger is attached, etc.
}
用法:
ASSERT(IsBlah(), "BlahBlah: x = %.2f, name = %s", GetX(), GetName());
我无法想象这样做的方法......除了传递另一个参数
#define ASSERT_PARAM(truthy, message, param) \
if (!(truthy)) \
{\
cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << ", value was " << param << endl;\
}
你会这样使用它:
ASSERT_PARAM(filesFound > 0, "Couldn't find any files, check your path!", filesFound);
得到:
Couldn't find any files, check your path! on line 27 in file openFiles.c. Check was filesFound > 0, value was 0
你想要做的事听起来非常复杂。 我担心在C ++中它是不可能的。
从技术上讲,您正在评估的是bool表达式,因此只要断言失败,您就可以将其传递给解析器。 解析器然后将构建表达式树,获取叶子(表达式的元素)并返回它们。 然后应打印出返回的值。 为此,您需要支持C ++ AFAIK实际上不支持的反射。
也许不是梦想的解决方案,但您可以将整个语句传递给宏。
#define ASSERT(trusty, action) if (!trusty) { action }
ASSERT(trusty, cout << a << b;)
ASSERT(trusty, printf("%d, %f\n", a, b);)
我想你可以分裂的truthy
像他们这样做的第一个答案表达在这里 ,然后你大概可以打印个人价值。 但我不确定它是否确实有效。
然后可以使用可变参数模板函数来进行打印
也许你可以妥协,只允许在断言表达式中使用2个变量和1个运算符? 如果是这样,您可以制作这样的临时解决方案:
#include <iostream>
#include <string>
#define STRINGIFY(x) #x
#define BIN_ASSERT(obj1, op, obj2, msg) \
if(!(obj1 op obj2)) \
{ \
std::cout << msg << " on line " << __LINE__ \
<< " in file " << __FILE__ \
<< "." << std::endl \
<< "Check was " \
<< STRINGIFY(obj1) STRINGIFY(op) STRINGIFY(obj2) \
<< "." << std::endl \
<< "Operator " << #obj1 << ": " << obj1 \
<< "." << std::endl \
<< "Operator " << #obj2 << ": " << obj2 \
<< "." << std::endl; \
}
int main (void)
{
int x = 2;
int y = 3;
std::string s1 = "hello";
std::string s2 = "world";
BIN_ASSERT(1, +, -1, "Value zero"); std::cout << std::endl;
BIN_ASSERT(x, ==, y, "Numbers not equal"); std::cout << std::endl;
BIN_ASSERT(s1, ==, s2, "Strings not equal"); std::cout << std::endl;
}
输出:
Value zero on line 30 in file test.c.
Check was 1+-1.
Operator 1: 1.
Operator -1: -1.
Numbers not equal on line 31 in file test.c.
Check was x==y.
Operator x: 2.
Operator y: 3.
Strings not equal on line 32 in file test.c.
Check was s1==s2.
Operator s1: hello.
Operator s2: world.
我想知道宏是否接收消息真的很有用。 失败的断言是向开发人员发出的一条消息,即代码中存在导致异常行为或将程序置于不可接受状态的错误。 用户与它的关系较少(如果他们甚至可以访问源代码)。
下面的代码定义了一个ASSERT
宏,它接受一个布尔表达式,对其进行求值并输出一条信息性消息。 该消息包含您在断言失败时要求检查的值。
宏,就像标准的assert()
宏(在<cassert>
)继续调用abort()
(来自<cstdlib>
)来导致程序异常终止。 这就是你想要的,因为程序进入了一个不知道还要做什么的状态。
我在这里使用std::printf()
为了简洁。 你做任何你想做的事。
#include <cstdlib>
#include <cstdio>
#define ASSERT(value, inspect) \
if (!(value)) { \
std::printf("ASSERTION FAILED: '%s', %s is %d: %s@%s:%d\n", #value, \
#inspect, inspect, __func__, __FILE__, __LINE__); \
abort(); \
}
int foo() { return 42; }
int main()
{
// ...
ASSERT(foo() - 40 == 1, foo());
//...
}
程序运行:
$ ./a.out
ASSERTION FAILED: 'foo() - 40 == 1', foo() is 42: main@prog.cc:16
Abort
如果不向宏添加更多参数,则无法完全按照您的要求执行操作。 在某些时候,你必须停下来意识到你花时间创建一个你不想看到的文本字符串。
您需要构建表达式“grabber”/ builder。
宏将变成如下:
#define ASSERT_PARAM(truthy, message, param) \
if (!(truthy)) \
{\
Grabber g;
g << #truthy; // grab expression as string
g % truthy; // grab expression and values
cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << ", value was " << param << endl;\
cout << g; \
}
Grabber做什么?
这是一堆疯狂的C ++,它构建了一个表达式。 它会使每个操作员超载以“抓住”操作员的参数。 每个操作员都返回一个对抓取器的引用,因此它可以抓住下一个操作符。 即
Grabber g;
g % filesFound > 0;
由于%(和*和/)具有高优先级,因此上述解析如下:
((g % filesFound) > 0)
如果template<typename T> Grabber::operator%(T const & val)
只记录(或打印)传入的值(即filesFound),并且 - 重要的是 - 返回自身(g)以使其成为下一个表达式的一部分:即它变为g> 0.导致template<typename T> Grabber::operator>(T const & val)
被调用,并且> 0被记录。
然后cout << g
可以把所有被抓住的东西吐出来。
如上所述“有可能 - Catch库可以做到这一点。但它真是太难了”。
PS你应该把你的宏包裹在一个do ...而0这样:
#define ASSERT_PARAM(truthy, message, param) \
do \
{ \
if (!(truthy)) \
{\
cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << ", value was " << param << endl;\
cout << g; \
} \
} while (0)
你目前所拥有的意味着这是有效的代码:
ASSERT(foo != 0)
else
{
}
这不是有效的代码:
if (foo != nullptr)
ASSERT(foo->bar != nullptr);
else
x = 10;
令人惊讶的是,我之前解决了类似的问题,但我不确定它是否可以在这种情况下帮助你。
最初的解决方案是Andrei Alexandrescu在文章“ 增强断言”中提出的,毫无疑问,它依赖于一些宏观技巧。
这个惊人的设施可以用作以下内容:
string s1, s2;
...
SMART_ASSERT(s1.empty() && s2.empty())(s1)(s2);
如果出现问题,将显示该消息
Assertion failed in matrix.cpp: 879412:
Expression: 's1.empty() && s2.empty()'
Values: s1 = "Wake up, Neo"
s2 = "It's time to reload."
需要注意的是,SMART_ASSERT理论上可以捕获无限变量。
有关实施细节,请查看文章。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.