摆脱丑陋的C结构

时间:2014-05-05 11:19:17

标签: c++ c c++11 macros

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

像(之前):

#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即。一旦用参数调用它,而在其他情况下调用它,感觉就像一个真正的return语句,但我没有找到任何更好的解决方案来解决这个古老的设计。

请注意,由于平台限制,我们有几个限制,但无论如何,我都愿意听到有关这两个的意见:

  • 抛出异常。代码是C和C ++函数的混合,相互调用,编译器类型不支持throw(在语法中接受,但对它没有任何作用,只是一个警告)。此解决方案是在C ++环境中解决此问题的标准方法。
  • C ++ 11的特性,这是一个微小的嵌入式平台,带有一个模糊而古老的“几乎”C ++编译器,它不支持最新的C ++特性。但是为了将来参考,我很好奇C ++ 11提供的内容。
  • 模板魔术。编译器在理解复杂的模板化问题时遇到了问题,但我仍然愿意看到你能提出的任何解决方案。

修改

另外,正如@BlueMoon在推荐中所建议的那样,创建一个全局变量是行不通的,因为在函数链的最开始,调用success变量是一个类的成员变量,并且有几个对象创建此类,每个类都需要报告其成功状态:)

3 个答案:

答案 0 :(得分:5)

这里有混合C和C ++错误处理策略的很大细分:

引用链接的文章,您的选择主要归结为:

  • 从可能失败的函数中返回错误代码。
  • 提供Windows GetLastError()或OpenGL&#39; glGetError()等功能,以检索最近发生的错误代码。
  • 提供包含最新错误的全局(好的,希望是线程局部的)变量,如POSIX&#39; s errno。
  • 提供函数以返回有关错误的更多信息,可能与上述方法之一一起使用,例如POSIX的strerror函数。
  • 允许客户端在发生错误时注册回调,例如GLFW glfwSetErrorCallback
  • 使用特定于操作系统的机制,如结构化异常处理。
  • 将错误写入日志文件,stderr或其他位置。
  • 只是断言()或以其他方式在发生错误时终止程序。

看起来你继承的代码的作者选择了一种相当奇怪的方式,将一个指向布尔[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) ...)

(live at coliru)