我正在通过编写一个简单的解析器/编译器来学习C.到目前为止,它是一个非常有启发性的经验,不过来自C#的强大背景我正在调整一些问题 – 特别是缺乏例外.
现在我已经读了Cleaner,more elegant,and harder to recognize,我同意那篇文章中的每一个字;在我的C#代码中,我尽可能地避免抛出异常,但是现在我遇到了一个我无法抛出异常的世界,我的错误处理完全颠覆了我的代码的干净和易于阅读的逻辑.
目前,我正在编写需要快速失败的代码,如果有问题,并且还可能深入嵌套 – 我已经解决了一个错误处理模式,“Get”函数在错误上返回NULL,其他函数返回-1失败.在这两种情况下,失败的函数调用NS_SetError(),所以所有调用函数需要做的是清理并立即返回失败.
我的问题是if(Action()< 0)的数字返回-1;我的声明正在做我的头 - 这是非常重复的,完全掩盖了潜在的逻辑.我最终创造了一个简单的宏来尝试改善情况,例如:
#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,但它仍然感觉像我滥用宏,不允许任何清理(某些功能,特别是获取返回指针的函数,更复杂,需要清理代码才能运行).
总的来说,它只是觉得我失去了一些东西 – 尽管事实上,这种错误处理方式据说更容易识别,在我的很多功能中,我真的很难确定是否正确处理错误:
>某些函数在错误上返回NULL
>一些函数返回< 0出错
>某些功能不会产生错误
>我的函数做一个NS_SetError(),但是很多其他函数都没有.
解决方法
一种用于清理的技术是使用永远不会实际迭代的while循环.它让你goto不使用goto.
#define NOT_ERROR(x) if ((x) < 0) break; #define NOT_NULL(x) if ((x) == NULL) break; // Initialise things that may need to be cleaned up here. char* somePtr = NULL; do { NOT_NULL(somePtr = malloc(1024)); NOT_ERROR(something(somePtr)); NOT_ERROR(somethingElse(somePtr)); // etc // if you get here everything's ok. return somePtr; } while (0); // Something went wrong so clean-up. free(somePtr); return NULL;
你失去一定程度的缩进.
编辑:我想补充一点,我没有反对goto,只是对于问题的用例,他并不需要它.有些情况下,使用goto将裤子从任何其他方法打败,但这不是其中之一.