我继承了一段(大)代码,它有一个错误跟踪机制,在这里它们将布尔变量传递给它们调用的所有方法,并且在执行的各个阶段出现错误,方法停止并返回,有时是默认的值。
像(之前):
#include <iostream.h>
int fun1(int par1, bool& psuccess)
{
if(par1 == 42) return 43;
psuccess = false;
return -1;
}
int funtoo(int a, bool& psuccess)
{
int t = fun1(a, psuccess);
if(!psuccess)
{
return -1;
}
return 42;
}
void funthree(int b, bool& psuccess)
{
int h = funtoo(b, psuccess);
if(!psuccess)
{
return;
}
cout << "Yuppi" << b;
}
int main()
{
bool success = true;
funthree(43, success);
if(!success)
{
cout<< "Life, universe and everything have no meaning";
}
}
请注意,这是C和C ++代码的混合,与项目的完全一致。
现在,来了一块C
魔法:“有人”在某处定义了一个宏:
#define SUCCES_OR_RETURN if(!psuccess) return
上面的程序变成(AFTER):
#include<iostream.h>
int fun1(int par1, bool& psuccess)
{
if(par1 == 42) return 43;
psuccess = false;
return -1;
}
int funtoo(int a, bool& psuccess)
{
int t = fun1(a, psuccess);
SUCCES_OR_RETURN -1;
return 42;
}
void funthree(int b, bool& psuccess)
{
int h = funtoo(b, psuccess);
SUCCES_OR_RETURN ;
std::cout << "Yuppi" << b;
}
int main()
{
bool success = true;
funthree(43, success);
if(!success)
{
cout<< "Life, universe and everything have no meaning";
}
}
问题:我想知道是否有更好的方法来处理这种错误跟踪,或者我不得不忍受这种情况。我个人不喜欢滥用C
宏SUCCES_OR_RETURN
即。一旦用参数调用它,而在其他情况下调用它,感觉就像一个真正的return
语句,但我没有找到任何更好的解决方案来解决这个古老的设计。
请注意,由于平台限制,我们有几个限制,但无论如何,我都愿意听到有关这两个的意见:
throw
(在语法中接受,但对它没有任何作用,只是一个警告)。此解决方案是在C ++环境中解决此问题的标准方法。修改
另外,正如@BlueMoon在推荐中所建议的那样,创建一个全局变量是行不通的,因为在函数链的最开始,调用success
变量是一个类的成员变量,并且有几个对象创建此类,每个类都需要报告其成功状态:)
答案 0 :(得分:5)
这里有混合C和C ++错误处理策略的很大细分:
引用链接的文章,您的选择主要归结为:
GetLastError()
或OpenGL&#39; glGetError()
等功能,以检索最近发生的错误代码。strerror
函数。glfwSetErrorCallback
。看起来你继承的代码的作者选择了一种相当奇怪的方式,将一个指向布尔[sic]的指针传递给函数使用似乎很不寻常。
这篇文章有一些很好的例子,我个人喜欢这种风格:
libfoo_widget_container_t container = NULL;
libfoo_error_details_t error = NULL;
if (libfoo_create_widgets(12, &container, &error) != libfoo_success) {
printf("Error creating widgets: %s\n", libfoo_error_details_c_str(error));
libfoo_error_details_free(error);
abort(); // goodbye, cruel world!
}
在这里你得到了一些东西,传入指向错误类型的指针,与成功常数的比较(而不是0|1
,C与世界其他地方之间的痛苦二分法!)。
我认为,如果某个函数正在调用goto
,那么使用SUCCES_OR_RETURN
可以更好地实现您的宏是太过分了。不止一次,它可能是一个线索,该功能做得太多了。复杂的清理或返回可能是代码味道,您可以在这里阅读更多http://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c/
答案 1 :(得分:3)
我之前见过这种错误处理方式。我称之为错误 - 不经意的手动伪异常。
代码流大部分都是错误的:您可以使用相同的错误标志连续调用3个函数,然后查看错误标志以查看是否发生了任何错误。
错误标志充当伪异常,一旦设置,我们开始“跳过”正常的代码流,但这是手动完成而不是自动完成。
如果您做某事并且不在乎是否发生错误,您可以放弃产生的错误并继续。
The ICU library以类似的方式处理错误。
在最小化结构差异的同时,更多C ++方法可以修改代码以返回expected
对象。
预计expected<T, Err>
为T
,如果出现问题,则为Err
类型。这可以作为boost::variant
和C ++ 1y std::optional
的混合实现。如果你去重载expected< T, Err > + U
上的大多数算术运算以返回expected< decltype( std::declval<T&>() + std::declval<U>(), Err >
并做了一些小心auto
,你至少可以允许算术表达式来保持它们的结构。然后,您将在事后检查错误。
另一方面,如果错误返回值可根据其类型进行预测,则可以创建一种类型,在转换为给定类型时会生成错误值。修改函数返回void
以返回某种类型的错误对象。现在每个功能都可以
if (berror) return error_flag_value{};
至少可以消除那个奇怪的;
或-1;
问题。
答案 2 :(得分:1)
如果你想要完整的C ++,答案就是改变异常的“无效返回值”......
#include <iostream>
#include <exception>
using std::exception;
struct error : exception { const char* what() const throw() override { return "unsuccessful"; } };
int fun1(int par1) {
if( par1 == 42 ) return 43;
throw error();
}
int funtoo(int a) {
fun1(a);
return 42;
}
void funthree(int b) {
funtoo(b);
std::cout << "Yuppi " << b << "\n";
}
int main() {
try {
funthree(42);
} catch(exception& e) {
std::cout << "Life has no meaning, because " << e.what() << "\n";
}
}
这会打印Yuppi 42
(如果您更改funthree(42)
的{{1}}来电,则会打印funthree(43)
...)