我经常编写的代码最终是长序列,如
int error;
error = do_something();
if (error) {
return error;
}
error = do_something_else(with, some, args);
if (error) {
return error;
}
error = do_something_yet_again();
if (error) {
return error;
}
return 0;
我正在寻找一种更清晰的方式来写这个,这在某种程度上避免了重复的相同检查。到目前为止,我已经编写了一个ERROR_OR
宏,其工作方式类似于
#define ERROR_OR(origerr, newerr) \
({ \
int __error_or_origerr = (origerr); \
(__error_or_origerr != 0) \
? __error_or_origerr \
: (newerr); \
})
允许原始代码变为类似
int error = 0;
error = ERROR_OR(error, do_something());
error = ERROR_OR(error, do_something_else(with, some, args));
error = ERROR_OR(error, do_something_yet_again());
return error;
这(在我看来)有点清洁。它也不太容易理解,因为除非您阅读其文档和/或实现,否则ERROR_PRESERVE
宏的功能不明显。它也没有解决重复的问题,只是更容易在一行上编写所有(现在隐式)检查。
我真的想重写这一切,如下所示:
return ERROR_SHORT_CIRCUIT(
do_something(),
do_something_else(with, some, args),
do_something_yet_again()
);
假设的ERROR_SHORT_CIRCUIT
宏
最后一个条件是我的短路与||
运算符的直接使用不同 - 因为这将评估为1而不是误差值。
我最初的写作尝试如下:
#define ERROR_SHORT_CIRCUIT(firsterr, ...) \
({ \
int __error_ss_firsterr = (firsterr); \
(__error_ss_firsterr != ERROR_NONE) \
? __error_ss_firsterr \
: ERROR_SHORT_CIRCUIT(__VA_ARGS__); \
})
这有两个明显的问题:
__VA_ARGS__
是单个值时)我已经研究了一些recursive macro hacks,但我不喜欢使用那种程度的预处理器魔法 - 太多的空间让某些东西变得微不足道。我也考虑过使用真实的(可能是可变的)函数,但这需要
并且这两者似乎都比原始的显式代码更糟糕。
我很想听听有关处理此问题的最佳方法的建议。我对许多不同的方法持开放态度,但我的最终目标是在不损害可读性的情况下避免重复。
(我认为很明显我对像Ruby这样的语言中||
运算符的行为感到羡慕。)
答案 0 :(得分:4)
我使用的代码如下:
if ((error = do_something()) != 0 ||
(error = do_something_else(with, some, args)) != 0 ||
(error = do_something_yet_again()) != 0)
return error;
return 0;
它是完全定义的,因为每个||
运算符之前都有序列点。它并不真正需要一个宏。它只会在您在函数调用之间分配资源或执行其他操作时遇到问题,但这与示例代码显示的不同。至少90%的战斗是创建do_something_or_other()
函数序列,以便于处理错误排序。
答案 1 :(得分:2)
另一种选择:
int error = 0;
do {
// Note: extra parens suppress assignment-as-conditional warning
if ((error = do_something())) break;
if ((error = do_something_else())) break;
if ((error = do_yet_another_thing())) break;
error = do_a_final_thing();
} while (0);
return error;