создание собственного механизма обработки ошибок в c ++ - PullRequest
0 голосов
/ 09 апреля 2019

Я хотел бы создать класс и перечисление для обработки ошибок в моем проекте.На данный момент я делаю это следующим образом.

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;
}

Я хотел бы получить несколько предложений по улучшению.Есть ли лучший способ сделать это или какие-либо языковые функции доступны для достижения этой цели.

1 Ответ

3 голосов
/ 09 апреля 2019

Для пользовательских ошибок вы можете наследовать от 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. Также string внутри функции ShowError является строкой 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 можно использовать для ошибок std внутри оператора catch, где вы просто передаете объект ошибки 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 см. this .

Для получения дополнительной информации о функциях, используемых в приведенном выше коде, см. Следующую ссылку:

FormatMessage функция

GetLastError функция

Часть кода была скопирована с этого сайта, например:

Преобразовать в строку

Показывать только имя файла

Формат COM Код ошибки

...