访问宏中的变量值

时间:2015-09-25 09:51:52

标签: c++ macros

前段时间,我为c和c ++程序制作了这个漂亮的断言宏

#define ASSERT(truthy, message) \
     if (!(truthy)) \
     {\
         cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << endl;\
     }

在整个代码中调用ASSERT调用,只要truthy值不真实,它就会发出警告!在开发过程中非常方便,以提醒您潜在的错误。

ASSERT(filesFound > 0, "Couldn't find any files, check your path!");

当filesFound为0时,宏将打印出来

  

无法找到任何文件,请查看您的路径!在第27行的文件中   openFiles.c。检查是filesFound&gt; 0

现在,想要打印它,给我更多相关信息,是传递到truthy参数的任何变量的值。喜欢这个

  

无法找到任何文件,请查看您的路径!在第27行的文件中   openFiles.c。检查是filesFound&gt; 0,filesFound为0

这似乎是类似lisp的领域,我想,是否有任何黑魔法预处理我可以用它来评估变量和函数的值,而不评估truthy语句?

我假设很失望。

9 个答案:

答案 0 :(得分:1)

我一直使用的另一种解决方案是支持宏中的varargs,然后强制断言用户指定相关的消息/变量 - 每次都需要额外的工作,但是正面,你可以准确地获得你想要的格式,并包括&#34; truthy&#34;中没有的信息。比特,例如:

#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());

答案 1 :(得分:0)

我无法想象这样做的方法......除了传递另一个参数

^[\w\s\-.\@#\$%!\?\(\)]*$

你会这样使用它:

#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);

答案 2 :(得分:0)

你想要做的事听起来很复杂。我在C ++中害怕它是不可能的。

从技术上讲,您正在评估的是bool表达式,因此只要断言失败,您就可以将其传递给解析器。解析器然后将构建表达式树,获取叶子(表达式的元素)并返回它们。然后应打印出返回的值。为此,您需要支持C ++ AFAIK实际上不支持的反射。

答案 3 :(得分:0)

也许不是梦想的解决方案,但你可以将整个陈述传递给宏。

#define ASSERT(trusty, action) if (!trusty) { action }

ASSERT(trusty, cout << a << b;)
ASSERT(trusty, printf("%d, %f\n", a, b);)

答案 4 :(得分:0)

我认为您可以像在第一个回答here中那样分割truthy表达式,然后您可以打印单个值。但我不确定它是否确实有效。

然后可以使用可变参数模板函数

来进行打印

答案 5 :(得分:0)

也许你可以妥协,只允许在断言表达式中使用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.

答案 6 :(得分:0)

我想知道让宏接收消息真的很有用。失败的断言是向开发人员发出的一条消息,即代码中存在导致异常行为或将程序置于不可接受状态的错误。用户没那么做(如果他们甚至可以访问源代码)。

下面的代码定义了一个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

如果不向宏添加更多参数,就无法完全按 的要求进行操作。在某些时候,你必须停下来意识到你花时间创建一个你不想看到的文本字符串。

答案 7 :(得分:0)

你需要建立一个表达式&#39; grabber&#39; / 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 ++,它构建了一个表达式。它会超载每个运算符以“抓取”#39;操作员的参数。每个操作员都返回一个对抓取器的引用,因此它可以抓住下一个操作符。即

Grabber g;
g % filesFound > 0;

由于%(和*和/)具有高优先级,因此上述解析如下:

((g % filesFound) > 0)

如果template<typename T> Grabber::operator%(T const & val)只记录(或打印)传入的值(即filesFound),并且 - 重要的是 - 返回自身(g)以使其成为下一个表达式的一部分:即它变为g&gt; 0.导致template<typename T> Grabber::operator>(T const & val)被调用,&gt; 0将被录制。

然后cout << g可以发出一切抓住的东西。

如上所述&#34;有可能 - Catch库可以做到。但这太难了了#34;。

P.S。你应该把你的宏包装在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;

答案 8 :(得分:0)

令人惊讶的是,我之前解决了类似的问题,但我不确定在这种情况下它是否可以帮到你。

最初的解决方案是由Andrei Alexandrescu在文章Enhancing Assertions中提出的,毫无疑问,依赖于一些宏观技巧。

这个神奇的设施可以用作以下内容:

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理论上可以捕获无限变量。

有关实施细节,请查看文章。