首页 > 解决方案 > 在 C++ 中创建自己的错误处理机制

问题描述

我想创建一个类和枚举来处理我的项目中的错误。到目前为止,我正在按照以下方式进行操作。

enum class eErrorType
{
    eJsonFileNotFound = 0,
    eJsonInvalidFormat,
    eJsonKeyNotFound,
    eJsonEmptyArray,
    eNoError,
    eCustom
};


class Error
{
public:
    // Constructors                                                                     
    Error() { errorType = eErrorType::eNoError; message = ""; }
    Error(eErrorType type) { errorType = type; SetMessage(type); }
    Error(std::string msg) { errorType = eErrorType::eCustom; message = msg; }

    // Public Methods                                                                   
    std::string getErrMessage() { return message; }


private:

    eErrorType errorType;
    std::string message;
    void SetMessage(eErrorType type)
    {
        switch (type)
        {
        case eErrorType::eJsonFileNotFound: message = "Json file not found"; break;
        case eErrorType::eJsonInvalidFormat: message = "Invalid json file"; break;
        case eErrorType::eJsonKeyNotFound: message = "Specified key is not found in json"; break;
        case eErrorType::eJsonEmptyArray: message = "No elements in json array"; break;
        case eErrorType::eNoError: message = "Entry contained an attempt to divide by zero!"; break;
        default: message = ""; break;
        }
    }
};

int main()
{
    try
    {
        //open json file. If file open failed, throw error
        throw eErrorType::eJsonFileNotFound;

        //parse json file. If parsing failed, throw error
        throw eErrorType::eJsonInvalidFormat;

        //check for particular key in the json. If not found, throw error
        throw eErrorType::eJsonKeyNotFound;
    }
    catch (eErrorType errCode)
    {
        Error errObj(errCode);
        std::cout << errObj.getErrMessage() << std::endl;
    }

    return 0;
}

我想要一些改进建议。有没有更好的方法或者任何基于语言的功能可以实现这一点。

标签: c++error-handlingc++17

解决方案


对于自定义错误,您可以从 std::exception 继承,覆盖异常方法并实现您自己的东西,例如:

#include <exception>    // std::exception

//
// custom exception class
//
    class error final :
        public std::exception
    {
    public:
        error(const char* description, short code = -1) throw() :
            description(description), code(code) { }

        const char* what() const throw() override
        {
            return description;
        }

        short Code() const throw()
        {
            return code;
        }

        error(error&& ref)
            : description(ref.description), code(ref.code) { }

        error& operator=(error&&)
        {
            return *this;
        }

        error(const error& ref)
            : description(ref.description), code(ref.code) { }

    private:
        const char* description;
        const short code;
        error& operator=(const error&) = delete;
    };

定义一个宏来显示发生错误的文件名:

#include <cstring>      // std::strrchr
// Show only file name instead of full path
#define __FILENAME__ (std::strrchr(__FILE__, '\\') ? std::strrchr(__FILE__, '\\') + 1 : __FILE__)

定义通用函数以显示错误(以下实现显示消息框,但您可以为控制台程序重新定义它)

#include <string>
#include <windows.h>
#include <codecvt>      // string conversion std::wstring_convert and std::codecvt_utf8

//
// converts string or const char* to wstring
//
std::wstring stringToWstring(const std::string& t_str)
{
    //setup converter
    typedef std::codecvt_utf8<wchar_t> convert_type;
    std::wstring_convert<convert_type, wchar_t> converter;

    //use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
    return converter.from_bytes(t_str);
}

//
// Set error message to your liking using error class
// and show message box, this function is also used to pass in
// std::exception objects
//
template <typename ExceptionClass>
void ShowError(
    HWND hWnd,
    ExceptionClass exception,
    const char* file,
    int line,
    long info = MB_ICONERROR)
{
    std::string error_type = TEXT("Rutime Error");
    std::string error_message = TEXT("File:\t");

#ifdef UNICODE
    error_message.append(stringToWstring(file));
#else
    error_message.append(file);
#endif // UNICODE

    error_message.append(TEXT("\r\nLine:\t"));
    error_message.append(std::to_string(line));
    error_message.append(TEXT("\r\nError:\t"));

#ifdef UNICODE
    error_message.append(stringToWstring(exception.what()));
#else
    error_message.append(exception.what());
#endif // UNICODE

    // Show error message
    MessageBox(hWnd,
        error_message.c_str(),
        error_type.c_str(), static_cast<UINT>(MB_OK | info));
}

然后,您会显示如下错误:

ShowError(nullptr, error("You error message"), __FILENAME__, __LINE__);

对于 Win32/COM 错误类型,函数可以像这样重载:

#include <comdef.h>     // _com_error
#include <windows.h>
#include <string>
//
// Format error code into a string and show message box
// COM and System errors
//
void ShowError(HWND hWnd, const char* file, int line, HRESULT hr = S_OK)
{
    string error_type = TEXT("Rutime Error");
    string error_message = TEXT("File:\t");

#ifdef UNICODE
    error_message.append(stringToWstring(file));
#else
    error_message.append(file);
#endif // UNICODE

    error_message.append(TEXT("\r\nLine:\t"));
    error_message.append(std::to_string(line));
    error_message.append(TEXT("\r\nError:\t"));

    // If HRESULT is omited or S_OK
    // format last error code message
    if (hr == S_OK)
    {
        LPVOID lpBuff = nullptr;

        DWORD dwChars = FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM,
            nullptr,
            GetLastError(),
            0,
            reinterpret_cast<LPTSTR>(&lpBuff),
            0,
            nullptr);

        // If the function succeeds, the return value is
        // the number of TCHARs stored in the output buffer
        if (dwChars)
        {
            error_message.append(reinterpret_cast<LPCTSTR>(lpBuff));
        }
        else // If the function fails, the return value is zero
        {
            error_message.append(TEXT("Unknown Error\t"));
            error_message.append(to_string(GetLastError()));
        }

        // Free the buffer allocated by FormatMessage
        LocalFree(lpBuff);
    }
    else // Format com error code into a message
    {
        _com_error err(hr);
        error_message.append(err.ErrorMessage());
    }

    // Play the sound and show error message
    MessageBox(hWnd,
        error_message.c_str(),
        error_type.c_str(), MB_OK | MB_ICONERROR);
}

对于系统错误,该函数的调用方式略有不同:

ShowError(nullptr, __FILENAME__, __LINE__); // type hresult if needed

编辑: 我从我的项目中复制了代码,目前std::to_string提到的仅适用于 ANSI 版本,您需要修改ShowError函数以有条件地std::to_wstring用于 unicode。ShowErrorstring函数内部还有 ANSI 字符串,wstring如果您不喜欢这样,您可以有条件地使用或定义字符串的宏:

#ifdef UNICODE
    typedef std::wstring string;
#else
    typedef std::string string;
#endif // UNICODE

to_string如果您愿意,也可以:

// conditionaly use string or wide string
#ifdef UNICODE
#define to_string std::to_wstring
#else
#define to_string std::to_string
#endif // UNICODE

如果您希望避免为每个单独的函数调用键入错误消息,您还可以实现enum class代码类型并将它们作为第二个或第三个附加参数传递给异常类,并实现显示自定义错误代码。

另请注意,该ShowError函数可用于 catch 语句中的 std 错误,您只需像这样传递 std 错误对象,例如:

try
{
       // example, or do some heavy memory allocation here to throw
       throw std::bad_alloc;
}
catch(std::bad_alloc& err);
{
       ShowError(nullptr, err, __FILENAME__, __LINE__);
}

可以扩展此方法以修改函数以格式化NTSTATUS消息

有关 Win32 中可能出现的错误消息的完整列表,请参阅

有关上述代码中使用的函数的更多信息,请参见以下链接:

FormatMessage函数

GetLastError函数

一些代码已从该站点复制,例如:

转换为 wstring

只显示文件名

格式化 COM 错误代码


推荐阅读