避免重复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;