다음은 예외에 몇 가지 정보를 추가하고 싶을 때 자주하는 작업의 예입니다.
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::string
std::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에 이미 내부 버퍼가 있습니다.