C++ 泛型返回\u IF\u FALSE()宏?
我试图清理一些遗留代码,其中包含数百个函数,其主体如下所示:C++ 泛型返回\u IF\u FALSE()宏?,c++,macros,C++,Macros,我试图清理一些遗留代码,其中包含数百个函数,其主体如下所示: void functionWithSideEffect(Foo* foo) { if (foo) { // Use `foo' to warm the planet ... } } 显然,如果先决条件检查失败,则默认失败不是最好的主意,因此我想将其重构为: int functionWithSideEffect(Foo* foo) { RETURN_IF_FALSE(fo
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\u 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;
}
(我认为允许设置“自由形式”消息字符串并不是那么必要/有用,所以我只是根据条件生成了一个罐装的消息字符串…只需替换宏
返回(###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;
}
对于其余的代码,同样适用于pedantic和-std=c99的gcc,以及在clang++和g++中使用-std=c++11的gcc。不确定MS编译器的功能是什么,因为它们对标准的支持有时不那么出色(而且我目前还没有Windows设置可供测试)。为什么要这样做?如果指针为null,您将获得未定义的行为。如果您想让它尝试并继续运行,抛出一个异常,然后其他东西可以捕获它并尝试下一个操作。为什么要使用串联运算符##
?如果是先决条件错误,只需中止/终止即可。不要回电话。调用代码已损坏。提示:只计算cond
一次。为什么不在if(foo)
语句中添加一个else
子句,并对失败发出噪音?哇。真不敢相信我没试过,谢谢你的建议,它看起来很好用。如果我发现任何问题,我会在这里发表另一条评论,但现在,这让我得到了我想要的。此外,不要忘记执行重复数据消除者在其上述评论中建议的操作。您只需要计算cond
一次。因此,将宏更改为仅assert(0)代码>如果执行if分支。我将assert()
向上移动,以避免在晴天情况下对条件进行两次计算,但将条件保留在适当位置,以便在条件为false
时,控制台中失败的断言的输出更容易理解。(无论如何,如果我们即将退出,我并不担心冗余的计算,但将其从主代码路径中移除确实是一个好主意。)
#define RETURN_IF_FALSE(cond, ... ) \
do { \
if (!(cond)) { \
const char* msg = \
"Pre-condition '" #cond "' not met, returning " #__VA_ARGS__ "..."; \
LOG("%s\n", msg); \
assert((cond)); \
return __VA_ARGS__; \
} \
} while(0)
#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;
}
#define RET_IF_FALSE(x, y, ...) if (!x) { printf(y); return __VA_ARGS__; }