摆脱一个丑陋的C结构
Getting rid of an ugly C construct
我继承了一段(大)代码,该代码具有错误跟踪机制,其中它们将布尔变量传递给它们调用的所有方法,并且在执行的各个阶段出现错误时,该方法将停止并返回,有时是默认值。
类似(BEFORE):
#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
ie。一旦带参数调用它,在其他情况下不带参数调用它,感觉就像一个真正的return
语句,但是我没有找到这个古老设计的更好的解决方案。
请注意,由于平台的限制,我们有一些限制,但无论如何,我愿意听到关于这两个的意见:
- 抛出异常。代码是C和c++函数相互调用的混合物,编译器不支持
throw
(在语法中接受但不处理它,只是一个警告)。这个解决方案是c++环境中解决这个问题的标准方法。 - c++ 11特性,这是一个很小的嵌入式平台,带有一个模糊而古老的"几乎"c++编译器,它不支持最新的c++特性。但是为了将来的参考,我很好奇是否有c++ 11提供的任何东西。
- 模板魔法。编译器在理解复杂的模板问题上有问题,但我再次愿意看到你能想出的任何解决方案。
编辑
另外,正如@BlueMoon在推荐中建议的那样,创建一个全局变量是不工作的,因为在函数链的一开始调用success
变量是一个类的成员变量,并且创建了这个类的几个对象,每个对象都需要报告其成功状态:)
这里有一个混合C和c++错误处理策略的大分解:
- http://blog.sduto.it/2014/05/a-c-error-handling-style-that-plays.html
要引用链接的文章,您的选择主要归结为:
- 从可能失败的函数返回错误代码。
- 提供一个类似Windows的
GetLastError()
或OpenGL的glGetError()
的函数来检索最近发生的错误代码 - 提供一个全局(好吧,希望是线程本地)变量,包含最近的错误,如POSIX的errno。
- 提供一个函数返回有关错误的更多信息,可能与上述方法之一结合使用,如POSIX的
strerror
函数。 - 允许客户端在发生错误时注册回调,如GLFW的
glfwSetErrorCallback
。 - 使用特定于操作系统的机制,如结构化异常处理。
- 将错误写入日志文件、标准错误或其他地方。
- 当出现错误时,只需assert()或以其他方式终止程序。
似乎你继承的代码的作者选择了一种相当奇怪的方式,传递一个指针到布尔值[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: %sn", 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/
我以前见过这种错误处理方式。我称之为错误无关的手动伪异常。
代码流基本上是错误无关的:您可以在一行中调用具有相同错误标志的3个函数,然后查看错误标志以查看是否发生了任何错误。
错误标志作为一个伪异常,一旦设置,我们开始"跳过"正常的代码流,但这是手动完成的,而不是自动完成的。
如果你做某件事而不关心是否发生错误,你可以直接删除产生的错误,然后继续。
ICU库以类似的方式处理错误。
在最小化结构差异的同时,一种更符合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;
问题。
如果您想使用完整的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)
更改为funthree(43)
,则打印Life has no meaning, because unsuccessful
…)
- 在 c++ 中拥有一组结构的正确方法是什么?
- 在头文件和 cpp 文件中使用一次 #pragma 时出现结构重定义错误
- 如何使用结构和指针推动和弹出一堆双打
- C++,您能否设计一种数据结构,将指针保存在连续内存中并且不会使它们失效?
- 使用一组结构,避免在一组结构中出现重复的结构
- 一种有效的数据结构,用于按 ID 访问和查找加权随机项
- 我想直接在结构中插入,但没有一种方法可以正确避免填充问题
- C++,从文件读取到结构,然后读取到向量(结构被推入向量太多次,而不仅仅是一次)
- 我该如何平衡一棵退化的树?C++数据结构
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 当我在结构中包含多个数组时,我的程序会跳过一堆代码
- 在 c++ 中将一种结构类型分配给另一种类型
- c++ 是否提供了一种使整个结构常量(不可修改)的方法?
- (C++)如何修改/使用数据结构,以便我们可以一次又一次地使用它们?
- 动态分配一维结构数组:两种方法
- cpp 中是否存在一种数据结构,可以轻松地提供一种基于已存在的实例构建新结构的方法
- 类中的结构不是一种类型
- 错误将结构复制到同一类型的另一种
- 在 typedef 结构中声明一个数组
- 将一个结构与一组模式相匹配