有没有更好的方法来进行C风格的错误处理?
|
我正在尝试通过编写一个简单的解析器/编译器来学习C。到目前为止,这是非常令人启发的经历,但是来自C#的强大背景,我在调整方面遇到了一些问题-尤其是由于缺少异常。
现在,我阅读了“更干净”,更优雅,更难以识别,并且我同意该文章中的每个单词;在我的C#代码中,我尽可能避免抛出异常,但是,现在我面临着一个无法抛出异常的世界,我的错误处理完全淹没了我本来应该清晰易懂的代码逻辑。
目前,我正在编写在出现问题时需要快速失败的代码,并且它也有潜在的深层嵌套-我确定了一种错误处理模式,其中\“ Get \”函数在发生错误时返回NULL,其他功能在失败时返回“ 0”。在这两种情况下,失败的函数都会调用
NS_SetError()
,因此所有调用函数需要做的就是清理并在失败时立即返回。
我的问题是,我脑海中涌现出许多“ 2”条语句-它非常重复并且完全掩盖了底层逻辑。我最终为自己创建了一个简单的宏来尝试改善这种情况,例如:
#define NOT_ERROR(X) if ((X) < 0) return -1
int NS_Expression(void)
{
NOT_ERROR(NS_Term());
NOT_ERROR(Emit(\"MOVE D0, D1\\n\"));
if (strcmp(current->str, \"+\") == 0)
{
NOT_ERROR(NS_Add());
}
else if (strcmp(current->str, \"-\") == 0)
{
NOT_ERROR(NS_Subtract());
}
else
{
NS_SetError(\"Expected: operator\");
return -1;
}
return 0;
}
函数NS_Term
,NS_Add
和NS_Subtract
的每一个执行NS_SetError()
并在出现错误的情况下返回-1
-更好,但仍然感觉我在滥用宏,并且不允许进行任何清理(某些函数,特定的返回指针的“ 9”函数更加复杂,需要运行清理代码)。
总的来说,感觉就像是我遗漏了一些东西-尽管事实证明这种方式的错误处理更容易识别,但是在我的许多函数中,我确实很难确定错误是否得到正确处理:
某些函数在出错时返回NULL
某些函数在出现错误时返回< 0
某些功能永远不会产生错误
我的函数执行NS_SetError()
,但其他许多函数则不执行。
有没有更好的方法来构造函数,或者其他人也有这个问题吗?
还有having9函数(返回指向对象的指针)对错误返回error10是一个好主意,还是只是使我的错误处理混乱?
没有找到相关结果
已邀请:
10 个回复
誓猎贰
但是,您会失去一定程度的缩进。 编辑:我想补充一点,我没有反对goto的功能,只是对于发问者的用例,他实际上并不需要它。在某些情况下,使用goto击败裤子是其他任何方法,但这不是其中一种。
仇聘发栖
:
您甚至可以围绕它创建自己的宏,以节省一些键入操作,例如:(我还没有测试过):
总体而言,与琐碎的处理相比,这是一种更清洁,更少冗余的方法:
作为一个额外的提示,我经常使用链接来减少代码中的ѭ21的数量:
由于ѭ23是短路运算符,因此上述公式将替代三个单独的ѭ21。也考虑在
语句中使用链接:
茶鬼失形
语句。这是它使用语言的原因之一。 另一个原因是“ 17”是状态机实现的自然表达。状态机最能代表什么常见的编程任务?词法分析器。有时看一下
的输出。转到。 因此,在我看来,现在是时候让您开始喜欢这种语言语法元素
了。
冲汉
您想返回一个指针?
反正你懂这个意思。 在调用函数方面,我最常看到的模式是使用指向清除标签的goto语句:
桔马牛
,标准C还具有另一种结构来处理出色的流量控制
。它的优点是,与某些人提出的“ 36”相比,您可以更轻松地突破多重嵌套控制语句,并且“ 17”提供的状态指示可以对出错原因进行编码。 另一个问题只是构造的语法。使用不能无意添加的控制语句不是一个好主意。就你而言
从根本上走错了。我会用类似
代替。
痴浪墨
的函数,它是一个负值,通常为
,依此类推... 如果每个可能的返回值也是一个有效值,请使用按引用调用:
检查每个函数的返回值可能看起来很乏味,但这就是在C中处理错误的方式。请考虑函数nc_dial()。它所做的只是检查其参数的有效性,并通过调用getaddrinfo(),socket(),setsockopt(),bind()/ listen()或connect()建立网络连接,最终释放未使用的资源并更新元数据。这可以在大约15行中完成。但是,由于错误检查,该功能有近100行。但这就是C语言中的方式。一旦习惯了,就可以轻松掩盖头脑中的错误检查。 此外,多个ѭ44没错。相反:这通常是谨慎的程序员的标志。谨慎是好事。 最后要说的是:如果有人在用枪指着你的时候不能证明使用它们的合理性,则不要将宏用于定义值。更具体地说,永远不要在宏中使用控制流语句:这使可怜的家伙感到困惑,可怜的家伙不得不在离开公司5年后维护您的代码。 ѭ45没问题。它简单,干净而且显而易见,以至于您无法做得更好。 一旦您放弃了将控制流隐藏在宏中的趋势,实际上就没有理由感到自己丢失了某些东西。
惜堡沁戚
号拳藐孔妇
荤碗
或
这样的错误处理方式-很好,但是考虑了一下,我意识到在很多情况下,我可以通过将函数一分为二来做同样的事情:
现在确实意味着您获得了一个额外的功能,但无论如何我还是更喜欢许多短功能。 根据飞利浦的建议,我还决定也避免使用控制流宏-只要将它们放在一行中,它就很清楚发生了什么。 至少,让我放心的是,我不只是想念一些东西-其他所有人也都有这个问题! :-)
墩瓣茅械