Возвращает динамическую строку из std:: exception `what`

Я убежден в этом, что я должен создавать подклассы std::exception для всех моих запросов на выброс исключений. Теперь я смотрю, как переопределить метод what.

В ситуации, с которой я столкнулся, было бы очень удобно, если строка what будет динамической. Некоторые части кода анализируют XML файл, например, и добавление номера или номера строки в сообщение об ошибке полезно для меня.

Я пытаюсь выполнить Рекомендации по обработке исключений Boost.

Что я хотел бы знать:

  • what возвращает a const char *, что означает, что любой зрелище, скорее всего, не освободит строку. Поэтому мне нужно другое место для хранения результата, но где это будет? (Мне нужна безопасность потоков.)

  • what также включает throw() в своей сигнатуре. Хотя я могу помешать what отбрасывать что-либо, мне кажется, что этот метод действительно не предназначен для чего-то слишком динамичного. Если what - неправильное место, то где я должен это делать?


Из ответов, которые я получил до сих пор, похоже, что единственный способ добиться этого - сохранить строку в исключении. Рекомендации Boost рекомендуют против этого, что меня сбивает с толку, потому что std::runtime_error делает именно это.

Даже если бы я использовал C-строку, мне пришлось бы использовать статический размер буфера или управлять памятью, что тоже может потерпеть неудачу. (Мне интересно, действительно ли это единственное, что может пойти не так, как в std::string copy-constructor. Это означает, что я ничего не получу, используя динамически выделенные C-строки.)

Осталась ли еще одна опция?

Ответ 1

В моих классах исключений обычно нет ничего, кроме конструктора, и смотрите в этих строках:

class MyEx: public std::runtime_error 
{
public: 
    MyEx(const std::string& msg, int line): 
        std::runtime_error(msg + " on line " + boost::lexical_cast<string>(line)) 
    {} 
}; 

Произвольный пример, но это базовый класс, который обрабатывает сообщение what().

Но если вы захотите, вы можете также назначить базовую часть объекта исключения после того, как вы собрали сообщение в теле конструктора.

#include <stdexcept>
#include <string>
#include <sstream>

class MyEx: public std::runtime_error
{
public:
    MyEx(const std::string& msg, int line):
        std::runtime_error("")
    {
        std::stringstream ss;
        ss << msg << " on line " << line;
        static_cast<std::runtime_error&>(*this) = std::runtime_error(ss.str());
    }
};

#include <iostream>
int main()
{
    try {
        throw MyEx("Evil code", __LINE__);
    }
    catch (const std::exception& e) {
        std::cout << e.what() << '\n';
    }
}

Однако, что касается руководящих принципов повышения эффективности, возможно, вам стоит обратить внимание на то, что числовые данные (позиции и строки) могут быть лучше всего доступны в виде чисел с помощью других методов. В рекомендациях говорится, что меньше беспокоиться о сообщении what().

Ответ 2

Руководства по повышению эффективности основаны на двух предположениях: копирование объекта исключения может вызвать другое исключение, а строка what() не является надежным или надежным инструментом. Это актуальная проблема, если вы пишете библиотеку, которая будет широко использоваться в различных средах. Если у вас есть более полное представление о том, как будет использоваться исключение, вы можете судить о том, оправданы ли эти опасения или нет ничего о чем-либо. Программирование - это целый ряд компромиссов, и компромиссы, которые имеют смысл для разработчиков Boost, могут не относиться к вам.

Ответ 3

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

Ответ 5

Я принимаю ответ UncleBens, потому что я считаю это технически самым правильным и полным ответом на мой оригинальный вопрос.

Для справки я фактически выбрал другое решение и вообще не использовал what. Я переработал код, который у меня есть, чтобы использовать что-то вроде этого как базовый класс исключения:

struct Exception : public virtual std::exception
{
  virtual const char* what() const throw()
  {
    try {
      return typeid(this).name();
    }
    catch (const std::exception& e) {
      return "<unknown exception>";
    }
  }

  // Extended description; may throw.
  virtual void describe(std::ostream& out) const = 0;
};

В основном просто заполняя what самой значимой вещью, которую я мог найти, не беспокоясь об этом в другом месте. Я собираюсь посмотреть, как это тарифы, я думаю.