Как избежать помещения std :: string в класс исключений? - PullRequest
8 голосов
/ 23 февраля 2012

Вот мой класс исключений:

class Win32Failure : public std::exception
{
public:
    Win32Failure( char const* win32_function_name, LONG error_code );

    char const* win32_function_name() const { return win32_function_name_; }
    LONG error_code() const { return error_code_; }

    virtual char const* what() const;

private:

    std::string GetFormattedMessage() const;

    char const* win32_function_name_;
    LONG error_code_;
    std::string error_text_;
};

Win32Failure::Win32Failure( char const* win32_function_name, LONG error_code )
    : error_code_(error_code)
    , win32_function_name_(win32_function_name)
{
    std::stringstream error_msg;
    error_msg   << win32_function_name << " failed with code: "
                << error_code << " (" << GetFormattedMessage() << ")"
                ;

    error_text_ = error_msg.str();
}

std::string Win32Failure::GetFormattedMessage() const
{
    TCHAR message_buffer[1000];

    FormatMessage(
        //FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        error_code_,
        0, // Default language
        reinterpret_cast<LPTSTR>(&message_buffer),
        sizeof(message_buffer) / sizeof(TCHAR),
        NULL
        );

    return std::string(message_buffer);
}

char const* Win32Failure::what() const
{
    return error_text_.c_str();
}

рекомендации по повышению исключений рекомендуют не размещать какие-либо объекты, которые выделяют память, как члены моего класса исключений.В этом случае использование std::string нарушает это.Я уважаю правило для этого, однако я не могу придумать способ реализовать переопределение what() без использования std :: string для управления памятью (вместо того, чтобы требовать, чтобы вызывающий управлял для меня).

Я мог бы использовать буфер фиксированного размера в качестве члена и использовать функции библиотеки C (например, snprintf()) для выполнения этой работы, но это не очень идиоматично для C ++ и, следовательно, не является идеальным решением.

Это подходящая реализация класса исключения?Если нет, какие улучшения могут быть сделаны?

Ответы [ 3 ]

6 голосов
/ 23 февраля 2012

Что бы это ни стоило, все типы исключений, определенные в <stdexcept>, принимают std::string в качестве аргументов. Разработчики библиотеки могут интерпретировать это как «хорошо». Я думаю, что главный аргумент против этого заключается в том, что если вы находитесь в среде с ограниченным объемом памяти, вы не сможете выделить память для выброса исключения.

2 голосов
/ 23 февраля 2012

Исключения должны защищать от возможного истощения ресурсов во время их строительства.Использование динамических массивов в исключениях бесполезно (если только это не делается с помощью указателей и существует план возврата в случае сбоя выделения).Использование std::string для передачи пользователю некоторой информации вдвойне: во-первых, это динамический массив, во-вторых, он в основном бесполезен - функция, которая выдает исключение, не знает обстоятельств, чтобы попытаться разумно объяснить причину сбоя.

Исключения должны выполнять полезные функции, такие как отслеживание стека + восстановление параметров функции (если возможно) + идентификатор внешнего сообщения (например, динамического библиотечного ресурса) в случае, если для конечного пользователя требуется форматированное сообщение низкого уровня.Если исключение делает для конечного пользователя, оно должно сделать это в форме связного отформатированного сообщения об ошибке из шаблона внешней строки (возможно, добавив некоторые параметры времени выполнения).Если исключение используется для помощи при отладке и делает его разработчиком, тогда трассировка стека, параметры функции, состояние машины полезны, некоторые жестко заданные общие строки - нет.

РЕДАКТИРОВАТЬ: похоже, вы пытаетесьчтобы сделать обертки исключений C ++ вокруг вызовов Windows API, я прав?Если это так, вы должны рассмотреть несколько вещей:

  • с использованием _set_se_translator () для обработки SE как исключения C ++;
  • с использованием dbghelp.dll 's StackWalk64 () , SymFromAddr () и аналогичные функции для генерации удобочитаемой трассировки стека (просто адрес полезен также для отладки вне сайта) в конструкторе исключений;
  • с использованиемвстроенная универсальная оболочка для проверки на наличие ошибки, возвращаемой Windows API, которая выдает исключение при указанном условии или пересылает возвращаемое значение в качестве возвращаемого типа.Просто убедитесь, что нет никаких накладных расходов (ссылки на шаблоны + inline + rvalue полностью исключают накладные расходы; также убедитесь, что вы НЕ ВЫБИРАЕТЕ исключение из функции проверки напрямую, делегируйте его не встроенной функции, чтобы избежать накладных расходов компилятора для функций, которые явно генерируют исключения).
0 голосов
/ 23 февраля 2012

в качестве части закрытых данных класса имеет статический буфер символов (рассмотрите возможность использования TLS при многопоточности)

what () просто возвращает константный указатель на этот буфер.

в GetFormattedMessage() вы заполняете буфер (так же, как вы это делаете в настоящее время) (и потенциально конвертируете из unicode / wchars, если это необходимо, поскольку вы имеете дело с TCHAR)

как член статического класса, буфер предварительно выделяется настек до любого исключения.

вам не нужно управлять памятью, так как она распределена в стеке.вы, тем не менее, затрачиваете 1000 байт стекового пространства (обычно это не имеет большого значения)

однако имеется только 1 буфер для хранения текста ошибки, но в любой момент времени должно существовать только одно ваше пользовательское исключение (ядумаю?)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...