これは、例外に情報を追加したいときによく行うことの例です。
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
はnotのように構築できることに注意してください。具体的な派生クラスのいずれかを使用する必要があります。
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 Chapter 9にあります。
また、カスタムメッセージも提供できますが、注意してください-std::string
またはstd::stringstream
またはその他の方法でメッセージを作成することは安全ではありません例外が発生します。
一般に、例外のコンストラクターでメモリを割り当てる(C++の方法で文字列を処理する)か、スローする直前に違いはありません-本当に必要なものの前にstd::bad_alloc
例外をスローできます。
したがって、スタックに割り当てられたバッファ(マキシムの答えのように)はより安全な方法です。
http://www.boost.org/community/error_handling.html で非常によく説明されています
したがって、より良い方法は、特定の種類の例外であり、書式設定された文字列の作成を回避することです(少なくともスローする場合)。