什么是在C ++中返回错误代码的正确方法

时间:2010-07-01 10:57:02

标签: c++

我正在使用错误代码来处理我的c ++项目中的错误。问题是如何从一个应该返回一些变量/对象的函数返回错误代码。

考虑一下:

long val = myobject.doSomething();

在这里,myobject是某个类的对象。如果doSomething函数遇到某些错误条件,那么它应该如何通知调用者(不使用异常)。

可能的解决方案:

  1. 在类中有一个数据成员(比如err_),可以由调用者检查。但是,在共享同一个对象并调用相同功能的多线程应用程序中,这将是不安全的。
  2. 使用一些全局错误变量,在多线程环境中也是同样的问题。
  3. 现在如何通知调用者有关错误的情况?

13 个答案:

答案 0 :(得分:28)

创建一个名为Maybe的模板,它根据您的返回值类型进行参数化。每当您返回一个值时,请将其包装在此模板中,如下所示:

Maybe<long> result = object.somemethod();

Maybe模板可以通过错误代码(可能是静态方法)进行实例化:

return Maybe<long>::error(code);

但通常会返回值:

Maybe<long> retval;
retval = 15;
return retval;

(当然,你必须覆盖适当的构造函数,赋值运算符等)。

在客户端,您调用一种方法来检查错误。

Maybe<long> result = object.somemethod();
if (result.is_error) 
{ 
    ... handle the error ...
}
else
{
    ... use the result ...
}

同样,您需要使用Maybe<long>定义的相应运算符,只要需要long

这听起来像很多工作,但实际上这项工作只需要制作一个好的,防弹的Maybe模板。您还必须对其进行一些性能调整,以避免令人讨厌的开销。如果要使其更加灵活,可以在返回值类型和错误类型上对其进行参数化。 (这只是复杂性的一个小小的增加。)

答案 1 :(得分:7)

你可能想要像Alexandresu的Expected<T> idiom这样的东西。

答案 2 :(得分:5)

您可以将变量作为参考传递并在其中返回错误代码。

答案 3 :(得分:3)

通常会返回一个返回/错误代码,并使结果具有属性或成员。

int retCode = myobject.doSomething();
if (retCode < 0){ //Or whatever you error convention is
   //Do error handling
}else{
   long val = myobject.result;
}

传入一个设置为返回值的指针也很常见,并返回返回/错误代码。 (见HrQueryAllRows)。

long val = INIT_VAL;
int retCode = myObject.doSomething(&val);

if (retCode < 0){
    //Do error handling
}else{
    //Do something with val...
}

答案 4 :(得分:3)

您有三种选择:

  • 创建一个包含返回值和可能的错误代码的类。

  • 使用类似boost::optional的内容作为返回值,允许 无效回复。

  • 传递对变量的引用并返回任何可能的错误代码 在那之内。

答案 5 :(得分:2)

返回错误句柄。让错误管理器保留错误代码和其他信息(例如ERROR_INVALID_PARAMETER和名称 - 值对,如ParameterName="pszFileName")。可以使用句柄访问此信息。调用者可以针对NO_ERROR_HANDLE检查错误句柄。如果为true,则不会发生错误。调用者可以增加错误信息并将句柄传递给堆栈。 错误管理器可以是进程的一个,也可以是每个线程的一个。

答案 6 :(得分:2)

最常见的做法是返回错误代码

long result;
int error = some_obj.SomeMethod(&result);

或返回一个表示存在错误的值:

long result = some_obj.SomeMethod();
if (result < 0) error = some_obj.GetError();

答案 7 :(得分:2)

您可以使用例外。

答案 8 :(得分:2)

我建议遵循:

class foo {
public:
    long doSomething();
    long doSomething(error_code &e);
};

error_code是哪种类型存有错误。根据{{​​1}},它可能是整数或更好的东西。

你提供两个功能:

  1. 第一个版本会抛出错误,例如从boost::system::error_code创建的boost::system::system_error投掷。
  2. 秒将错误代码返回到e。

答案 9 :(得分:2)

您可以返回std::pair同时包含错误代码(或错误对象)和所需的返回对象。感兴趣的对象需要一个默认的构造函数或值,因此即使遇到错误也可以返回一些东西。

答案 10 :(得分:1)

我看到有很多不错的解决方案,但是如果遇到这种情况,我会以另一种方式解决。

auto doSomething()
{        
    // calculations
    return std::make_pair(error_code, value)
}

int main()
{
    auto result = doSomething();
    if (!result.first)
    {
        std::cout << result.second;
    }
    else
    {
        std::cout << "Something went wrong: " << result.second;
    }
}

对我来说,这是一个干净的解决方案,而不是通过引用bool作为参考。 c ++ 14支持auto返回类型推导

答案 11 :(得分:0)

定义文件中的所有错误代码。根据错误类别,您可以返回错误代码,调用者可以决定出错的地方,调用者可以返回自己的错误代码。

例如

#define FILE_ERROR        1
#define SANITY_ERROR      2

int WriteToFile(char* Data, int iErrorCode)
{
   char* FileName;

  if (!FileOpen(FileName, &iErrorCode))
  {
     //Analyze error code and make decision on what to ignore or terminate
     iErrorCode = FILE_ERROR;
     return 0;
  }

}

int FileOpen(char* FileName, int* iErrorCode)
{

    if (FileName == null)
     {
       iErrorCode = SANITY_ERROR;
      return 0;
    }

    ///// next code blocks
    return 1;
}

答案 12 :(得分:0)

我找到了一种新的方法。这是非标准的,这是一种全新的方法。 因此,请考虑谨慎使用此方法。

使用以下头文件:

SetError.h:

#include <string> // for string class 



#ifndef SET_ERROR_IS_DEFINED
#define SET_ERROR_IS_DEFINED

class Error {

public:
    int code = 0;
    std::string errorMessage;
    std::string fileName;
    std::string functionName;

    Error() {}

    Error(int _errorCode, std::string _functionName = "", std::string _errorMessage = "", std::string _fileName = "")
    {
        code = _errorCode;
        functionName = _functionName;
        errorMessage = _errorMessage;
        fileName = _fileName;
    }
};

#if defined(_DEBUG) || !defined(NDEBUG) 
#define ___try { _ERROR.code = 0; bool __valid_try_mode_declared; 
#define ___success }
#define SetError(pErrorData) __valid_try_mode_declared = true; _ERROR = *pErrorData; delete pErrorData;
#else
#define ___try { _ERROR.code = 0;
#define ___success }
#define SetError(pErrorData) _ERROR = *pErrorData; delete pErrorData; 
#endif

#endif

inline Error _ERROR;

包括所有软件。

使用方法示例:

Main.cpp:

#include "SetError.h"
#include <iostream>


bool SomeFunction(int value) ___try; 
{ 


    if (value < 0) {
        SetError(new Error(10, "SomeFunction", "Some error", "File main.cpp"));
        return false;
    }

    return true;
} ___success; // You mast to warp the function with both ___try and ___success
// These keywords must be at the start and the end of the function!




int main()
{
    using namespace std;

    bool output = SomeFunction(-1);

    if (_ERROR.code != 0) { // This is how you check the error code. using the global _ERROR object
        cout << "error code: " << _ERROR.code << ", from function: " 
            << _ERROR.functionName << ", from file: " << _ERROR.fileName;
    }

    cout << endl << "Founction returned: " << output << endl;

    return 1;
}

如果某些函数在另一个线程中运行,则这些函数需要在名称空间中,然后您可以执行以下操作:

namespace FunctionsInSomeThread
{
    #include "SetError.h"

    bool SomeFunc1() ___try;
    {
        SetError(new Error(5, "SomeFunction2", "Some error from another thread", "File main.cpp"))
        return true;
    } ___success;

    bool SomeFunc2() ___try;
    {
        SetError(new Error(5, "SomeFunction2", "Some error from another thread", "File main.cpp"))
            return true;
    } ___success;

}

要访问_Error,您需要添加线程的名称空间

if (FunctionsInSomeThread::_ERROR.code != 0)
{
    // Error handling
}

或者如果它在同一个命名空间中,则无需在之前添加FunctionsInSomeThread ::。

其背后的想法是,不能仅使用___success;关键字来扭曲函数。您将得到编译错误。因此,开发人员将永远不会从其他函数返回旧的错误代码。

如果您在代码块的末尾写了___success;,则还必须在开始时也写___try;! 如果SetError___try;中没有包装的宏,则也无法使用。

这个想法来自您对此有想法的AutoIt语言: https://www.autoitscript.com/autoit3/docs/functions/SetError.htm

因此,如果使用此标头,则在C中几乎相同。