我正在使用错误代码来处理我的c ++项目中的错误。问题是如何从一个应该返回一些变量/对象的函数返回错误代码。
考虑一下:
long val = myobject.doSomething();
在这里,myobject是某个类的对象。如果doSomething函数遇到某些错误条件,那么它应该如何通知调用者(不使用异常)。
可能的解决方案:
现在如何通知调用者有关错误的情况?
答案 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}},它可能是整数或更好的东西。
你提供两个功能:
boost::system::error_code
创建的boost::system::system_error
投掷。答案 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中几乎相同。