使用定义的宏来有条件地返回一个值有一个缺点,即仅仅查看客户端代码可能会在宏点处退出。
我正在考虑的用例是写一个值和错误检查,如下所示:
#define WRITE_CHK(file, param)\
if (!write_that_returns_zero_on_fail(file, param)) {\
handle_error();\
return false;\
}
客户代码:
bool myfunc()
{
...
WRITE_CHK(file, param) // function might return here
...
return true;
}
我很好奇宏(在我的代码中会在很多地方使用)的好处将超过上面提到的缺点。 除了简单地扩展(不使用宏)之外,是否有其他选择?
答案 0 :(得分:15)
标准答案是“不要使用宏”;但这通常略显简单。有时情况下,他们可以大大减少你原本没有的样板冗长。
那么,为什么不将事实编码到宏名称中呢?例如WRITE_OR_RETURN_ON_FAILURE
。它可能略显冗长,但它不太可能让你的代码的读者绊倒。
答案 1 :(得分:3)
隐藏宏内部的控制流并不常用,因此对于必须理解或调试代码的开发人员来说可能会造成混淆。
我建议完全不使用宏,但如果必须使用它们,请确保控制流部分是显式的。
// assume handle_error returns true
#define WRITE_FAILED(file, param)\
(!write_that_returns_zero_on_fail(file, param) && handle_error())
// macro usage
if(WRITE_FAILED(file, param))
{
return;
}
要问自己的另一个问题是 - 为什么要在这里使用宏呢?如果是为了隐藏控制流程那么这不是一个好主意。还有其他原因吗?
答案 2 :(得分:1)
制作一个两步宏:
#define WRITE_CHK_(file, param, HANDLER)\
if (!write_that_returns_zero_on_fail(file, param)) {\
handle_error();\
HANDLER\
}
#define RFALSE return false;
#define WRITE_CHK(file, param) WRITE_CHK_(file, param, RFALSE)
如果您需要在其他地方进行类似的检查(或直接使用WRITE_CHK(file, param, return false)
),您可以制作其他包装。
答案 3 :(得分:-4)
是的,这很糟糕。使用goto
。
#define fail_unless(x) do {if(!(x)) goto fail;} while (0)
int foo()
{
fail_unless( my_write(...) );
fail_unless( bar() );
return 0;
fail:
cleanup();
return -1;
}
编辑:如果您因为不了解while循环在那里做什么而进行了低估,那么您应该问一下。它是一种标准技术,用于确保C宏在任何上下文中充当单个语句。如果你的宏扩展为if语句(就像其他的宏,upvoted答案),那么你会得到意想不到的副作用,例如:
#define DONT_DO_THIS(x) if (!x) { foo; }
if (a) DONT_DO_THIS(x)
else something_else();
你期望在!a
时执行something_else,但宏实际扩展到的是:
if (a)
if (!x) { foo;}
else something_else();
而且,a && x
时会执行something_else,而不是程序员想要的!a
。