通用RETURN_IF_FALSE()宏?

时间:2014-04-03 21:18:44

标签: c++ macros

我试图清理包含数百个函数的遗留代码,其中包含如下所示的实体:

void functionWithSideEffect(Foo* foo)
{
    if (foo)
    {
        // Use `foo' to warm the planet
        ...
    }
}

显然,如果前提条件检查失败,默默地失败并不是最好的主意,所以我想将其重构为:

int functionWithSideEffect(Foo* foo)
{
    RETURN_IF_FALSE(foo != NULL, "foo is NULL in functionWithSideEffect!");

    // Use `foo' to warm the planet
    ...
}

以下宏似乎适用于不返回值的函数:

#define RETURN_IF_FALSE(cond, msg) \
  do { \
      if (!(cond)) { \
          LOG("%s\n", (msg)); \
          assert((cond)); \
      } \

      return; \
  } while(0)

它具有以下理想的属性:

  • 使用清晰简洁
  • 无声
  • 它将在调试版本中崩溃,但尝试在发布版本中进行攻击

(不可否认,对于返回void的函数,在发布版本中进行训练可能并不总是令人满意的。“

对于返回值的函数,这个宏可以解决这个问题:

#define RETURN_VALUE_IF_FALSE(cond, msg, retval ) \
  do { \
      if (!(cond)) { \
          LOG("%s\n", (msg)); \
          assert((cond)); \
      } \
      return(retval); \
  } while(0)

我的问题是:是否可以编写单个 RETURN_IF_FALSE宏来处理返回值的void函数函数?我坐下来尝试使用varargs宏进行某些操作,并很快发现我并不擅长编写复杂的宏。我开始使用这个测试程序:

#include <stdio.h>
#include <assert.h>

#define RETURN_IF_FALSE(cond, msg, ... ) \
  do { \
      if (!(cond)) { \
          fprintf(stderr, "%s\n", (msg)); \
          assert((cond)); \
      } \
      return (##__VA_ARGS__); \
  } while(0)


int main()
{
    RETURN_IF_FALSE(1 < 0, "1 is not less than 0!", -1);
    return 0;
}

也许并不奇怪,它产生了以下编译错误:

g++     macro_test.cpp   -o macro_test
macro_test.cpp:10:14: error: pasting "(" and "-" does not give a valid preprocessing token
       return (##__VA_ARGS__); \
              ^
macro_test.cpp:16:5: note: in expansion of macro ‘RETURN_IF_FALSE’
     RETURN_IF_FALSE(1 < 0, "1 is not less than 0!", -1);
     ^

甚至可能用一个宏来覆盖这两种情况吗?我在Linux上使用gcc 4.8.1。 (我可以使用-std=c++11进行编译,如果它有帮助......)

更新:为了实现这个完整的圈子,我最终根据@Turix的回答和@Deduplicator提出的移动{{{ {1}}打电话以避免在阳光灿烂的日子里对条件进行双重评估。情况下:

assert()

(我认为,设置一个&#39;自由形式的消息字符串并不是真正的必要/有用,所以我只是从条件中生成一个罐装的......) / p>

2 个答案:

答案 0 :(得分:2)

只需用return (##__VA_ARGS__);替换宏return __VA_ARGS__ ;的这一部分,我认为它应该做你想要的(假设你传递的返回值不是一个复杂的表达式 - 如果它是,你需要用括号预先包装参数。)

答案 1 :(得分:1)

我让这个工作。

#include <stdio.h>
#define RET_IF_FALSE(x, y, z) if (!x) { printf(y); return z; }


int a(int *p)
{
    RET_IF_FALSE(p, __FUNCTION__, 0);

    return *p;
}

void b(int *p)
{
    RET_IF_FALSE(p, __FUNCTION__, );
}


int main()
{
    int x;

    x = a(&x);
    b(&x);

    x = a(NULL);
    b(NULL);

    return 0;
}

它可能不是带有逗号的最漂亮的解决方案,并且根据gcc的-pedantic选项不符合标准。

使用:

#define RET_IF_FALSE(x, y, ...) if (!x) { printf(y); return __VA_ARGS__; }

其余代码同样适用于gcc with pedantic和-std = c99,以及-cd = c ++ 11 in clang ++和g ++。不确定MS编译器做了什么,因为他们对标准的支持有时候不那么出色(我目前还没有Windows设置进行测试)。