Generic RETURN_IF_FALSE() macro?

Generic RETURN_IF_FALSE() macro?

本文关键字:macro FALSE RETURN IF Generic      更新时间:2023-10-16

我正在尝试清理一些包含数百个函数的遗留代码,其主体如下所示:

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("%sn", (msg)); 
          assert((cond)); 
      } 
      return; 
  } while(0)

它具有以下理想的特性:

  • 使用清晰简洁
  • 不会默默地失败
  • 它将在调试版本中崩溃,但尝试在发布版本中继续前进

(诚然,对于返回void的函数,在发布版本中继续前进可能并不总是"可取的"。

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

#define RETURN_VALUE_IF_FALSE(cond, msg, retval ) 
  do { 
      if (!(cond)) { 
          LOG("%sn", (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, "%sn", (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的建议最终实现的实现,将assert()调用上移以避免在"晴天"情况下对条件进行双重评估:

#define RETURN_IF_FALSE(cond, ... ) 
  do { 
      if (!(cond)) { 
          const char* msg = 
              "Pre-condition '" #cond "' not met, returning " #__VA_ARGS__ "..."; 
          LOG("%sn", msg); 
          assert((cond)); 
          return __VA_ARGS__; 
      } 
  } while(0)

(我决定允许设置"自由格式"消息字符串并不是那么必要/有用,所以我只是从条件中生成了一个罐装消息字符串......

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

我让它工作了。

#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__; }

对于其余代码,同样适用于具有 pedantic 和 -std=c99 的 gcc,以及 clang++ 和 g++ 中的 -std=C++11。不确定 MS 编译器是做什么的,因为它们对标准的支持有时不太出色(而且我目前还没有 Windows 设置进行测试)。