摆脱一个丑陋的C结构

Getting rid of an ugly C construct

本文关键字:结构 脱一      更新时间:2023-10-16

我继承了一段(大)代码,该代码具有错误跟踪机制,其中它们将布尔变量传递给它们调用的所有方法,并且在执行的各个阶段出现错误时,该方法将停止并返回,有时是默认值。

类似(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";
    }
}

问题:我想知道是否有更好的方法来处理这种错误跟踪,或者我必须忍受这种情况。我个人不喜欢滥用CSUCCES_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…)

(live at coliru)