避免重复Cerror handling
我经常编写代码,这些代码最终都是长序列
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
宏的function不明显。 它也没有解决重复的问题,只是更容易在一行上编写所有(现在隐式)检查。
我真的想重写这一切,如下所示:
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不支持递归宏
我已经研究了一些递归的宏观黑客攻击 ,但我不喜欢使用那种程度的预处理器魔法 – 太多的空间让某些东西变得微不足道。 我也考虑使用真实(可能是可变参数)函数,但这也需要
- 放弃短路行为
- 将函数作为指针传递,从而规范化它们的签名
并且这两者似乎都比原始的显式代码更糟糕。
我很想听听关于处理这个问题的最佳方法的建议。 我对许多不同的方法持开放态度,但我的最终目标是在不损害可读性的情况下避免重复。
(我想很明显我在Ruby这样的语言中对||
运算符的行为感到羡慕)。
我使用的代码如下:
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()
函数序列,以便于处理错误排序。
另外一个选项:
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;