我经常编写最终成为长序列的代码,例如
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__
是单个值)
- C 不支持递归宏
我研究过一些递归宏黑客 https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms#recursion,但我不喜欢使用那种程度的预处理器魔法——太多的空间可能会出现微妙的错误。我也考虑过使用真实的(可能是可变的)函数,但这需要
- 放弃短路行为
- 将函数作为指针传递,从而标准化它们的签名
这两个看起来都比原始的、显式的代码更糟糕。
我有兴趣听取有关处理此问题的最佳方法的建议。我对许多不同的方法持开放态度,但我的最终目标是避免重复而不损害可读性。
(我想很明显我对这个人的行为感到有些嫉妒||
Ruby 等语言中的运算符)。