[c++] 변수 메시지로 std :: exceptions를 던지는 방법은 무엇입니까?

다음은 예외에 몇 가지 정보를 추가하고 싶을 때 자주하는 작업의 예입니다.

std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());

더 좋은 방법이 있습니까?



답변

내 해결책은 다음과 같습니다.

#include <stdexcept>
#include <sstream>

class Formatter
{
public:
    Formatter() {}
    ~Formatter() {}

    template <typename Type>
    Formatter & operator << (const Type & value)
    {
        stream_ << value;
        return *this;
    }

    std::string str() const         { return stream_.str(); }
    operator std::string () const   { return stream_.str(); }

    enum ConvertToString
    {
        to_str
    };
    std::string operator >> (ConvertToString) { return stream_.str(); }

private:
    std::stringstream stream_;

    Formatter(const Formatter &);
    Formatter & operator = (Formatter &);
};

예:

throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData);   // implicitly cast to std::string
throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData >> Formatter::to_str);    // explicitly cast to std::string


답변

표준 예외는 다음에서 구성 할 수 있습니다 std::string.

#include <stdexcept>

char const * configfile = "hardcode.cfg";
std::string const anotherfile = get_file();

throw std::runtime_error(std::string("Failed: ") + configfile);
throw std::runtime_error("Error: " + anotherfile);

기본 클래스 std::exception는 이렇게 구성 할 수 없습니다 . 구체적인 파생 클래스 중 하나를 사용해야합니다.


답변

같은 다른 예외가있다 runtime_error, range_error, overflow_error, logic_error, 등 당신은 생성자에 문자열을 전달해야하고, 당신은 당신이 당신의 메시지에 원하는대로 연결할 수 있습니다. 그것은 단지 문자열 연산입니다.

std::string errorMessage = std::string("Error: on file ")+fileName;
throw std::runtime_error(errorMessage);

다음 boost::format과 같이 사용할 수도 있습니다 .

throw std::runtime_error(boost::format("Error processing file %1") % fileName);


답변

다음 클래스는 매우 유용 할 수 있습니다.

struct Error : std::exception
{
    char text[1000];

    Error(char const* fmt, ...) __attribute__((format(printf,2,3))) {
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(text, sizeof text, fmt, ap);
        va_end(ap);
    }

    char const* what() const throw() { return text; }
};

사용 예 :

throw Error("Could not load config file '%s'", configfile.c_str());


답변

C ++ 14 ( operator ""s) 인 경우 문자열 리터럴 연산자 사용

using namespace std::string_literals;

throw std::exception("Could not load config file '"s + configfile + "'"s);

또는 C ++ 11에서 직접 정의하십시오. 예를 들어

std::string operator ""_s(const char * str, std::size_t len) {
    return std::string(str, str + len);
}

그러면 throw 문은 다음과 같습니다.

throw std::exception("Could not load config file '"_s + configfile + "'"_s);

멋지고 깨끗해 보입니다.


답변

정말 좋은 방법은 예외에 대한 클래스 (또는 클래스)를 만드는 것입니다.

다음과 같은 것 :

class ConfigurationError : public std::exception {
public:
    ConfigurationError();
};

class ConfigurationLoadError : public ConfigurationError {
public:
    ConfigurationLoadError(std::string & filename);
};

그 이유는 예외가 문자열을 전송하는 것보다 훨씬 더 바람직하기 때문입니다. 오류에 대해 서로 다른 클래스를 제공하면 개발자가 특정 오류를 해당 방식으로 처리 할 수있는 기회를 제공합니다 (단지 오류 메시지를 표시하는 것이 아님). 예외를 포착하는 사람들은 계층 구조를 사용하는 경우 필요한만큼 구체적 일 수 있습니다.

a) 구체적인 이유를 알아야 할 수도 있습니다.

} catch (const ConfigurationLoadError & ex) {
// ...
} catch (const ConfigurationError & ex) {

a) 다른 사람은 세부 사항을 알고 싶어하지 않습니다

} catch (const std::exception & ex) {

이 주제에 대한 영감은 https://books.google.ru/books?id=6tjfmnKhT24C 9 장 에서 찾을 수 있습니다.

또한 사용자 지정 메시지를 제공 할 수도 있지만주의 해야합니다std::stringstd::stringstream . 예외가 발생할 수있는 방법 중 하나 또는 다른 방법으로 메시지를 작성하는 것은 안전하지 않습니다 .

일반적으로 예외 생성자에서 메모리를 할당하는지 (C ++ 방식으로 문자열 작업) 또는 throw 직전에 차이가 없습니다. std::bad_alloc 예외가 발생하기 직전에 . 정말 원하는 것보다 먼저 예외가 발생 될 수 있습니다.

따라서 스택에 할당 된 버퍼 (Maxim의 답변과 같이)가 더 안전한 방법입니다.

http://www.boost.org/community/error_handling.html 에서 매우 잘 설명됩니다 .

따라서 더 좋은 방법은 특정 유형의 예외이며 형식화 된 문자열 작성을 피하는 것입니다 (적어도 던질 때).


답변

내 사용자 지정 예외에 대한 사용자 지정 오류 메시지를 만들면 코드가 추악하다는 점에서 유사한 문제가 발생했습니다. 이것이 내 해결책이었습니다.

class MyRunTimeException: public std::runtime_error
{
public:
      MyRunTimeException(const std::string &filename):std::runtime_error(GetMessage(filename)) {}
private:
      static std::string GetMessage(const std::string &filename)
     {
           // Do your message formatting here. 
           // The benefit of returning std::string, is that the compiler will make sure the buffer is good for the length of the constructor call
           // You can use a local std::ostringstream here, and return os.str()
           // Without worrying that the memory is out of scope. It'll get copied
           // You also can create multiple GetMessage functions that take all sorts of objects and add multiple constructors for your exception
     }
}

이것은 메시지를 만드는 논리를 분리합니다. 나는 원래 what ()을 재정의하는 것에 대해 생각했지만, 당신은 당신의 메시지를 어딘가에 포착해야한다. std :: runtime_error에 이미 내부 버퍼가 있습니다.