これは、例外に情報を追加したいときによく行うことの例です。
std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());
それを行うためのより良い方法はありますか?
これは、例外に情報を追加したいときによく行うことの例です。
std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());
それを行うためのより良い方法はありますか?
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
できないことに注意してください。具体的な派生クラスの1つを使用する必要があります。
これが私の解決策です:
#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
、、、、などのさまざまな例外があり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 std::exception("Could not load config file '"_s + configfile + "'"_s);
きれいに見えます。
多分これ?
throw std::runtime_error(
(std::ostringstream()
<< "Could not load config file '"
<< configfile
<< "'"
).str()
);
一時的なostringstreamを作成し、必要に応じて<<演算子を呼び出します。次に、それを丸かっこで囲み、評価結果(ostringstream)に対して.str()関数を呼び出して、一時的なstd::stringをコンストラクターに渡します。 runtime_errorの。
注:ostringstreamと文字列は一時的なr値であるため、この行が終了するとスコープ外になります。例外オブジェクトのコンストラクターは、コピーまたは(より適切な)移動セマンティクスのいずれかを使用して入力文字列を取得する必要があります。
追加:このアプローチを必ずしも「ベストプラクティス」とは見なしませんが、機能し、ピンチで使用できます。最大の問題の1つは、このメソッドにはヒープ割り当てが必要であるため、演算子<<がスローできることです。あなたはおそらくそれが起こることを望まないでしょう。ただし、その状態になった場合は、おそらくもっと多くの問題を心配する必要があります。
あなたが望むものに関して答えるべき2つのポイントがあります:
1.1。
最初のポイントは、カスタム例外用の特別な型(クラス)を作成し、クラスのフィールドとしてパラメーターを渡すことです。
次のようなもの:
class BaseFor_Exceptions : public std::exception {
protected:
BaseFor_Exceptions();
};
class Exception1 : public BaseFor_Exceptions {
public:
Exception1(uint32_t value1);
private:
uint32_t value1;
};
throw Exception1(0);
2番目のポイントは、可変サイズ(ファイル名)の値を渡そうとしているため、例外オブジェクトを準備するときにメモリ割り当てを実行していることです。
(std::stringとstd::stringstreamの両方のオブジェクトを変更する場合)std :: bad_alloc例外がその過程でスローされる可能性があるため、例外の準備またはスロー(*)に失敗します–情報と状態が失われます。
適切に設計されたプログラムでは、例外を準備または処理するときにメモリ割り当てを回避するのは簡単です。必要なのは次のとおりです。
} catch (const ConfigurationLoadError & ex) {
std::cerr
<< “Some message 1 ”
<< serviceLocator1.SomeGetMethod1().Get_ConfigurationFileName();
} catch (const SomeException & ex) {
std::cerr
<< “Some message 2 ”
<< serviceLocator1.SomeGetMethod2().GetEventDetailsString(ex.Get_Value1());
}
もちろん、バッファサイズの制限を受け入れ、事前に割り当てられたバッファを使用するオプションは常にあります。
また、例外に使用されるタイプ(クラス)は、コピーコンストラクターから例外をスローすることは許可されていないことに注意してください。これは、最初の例外が値によってキャッチされようとすると、コピーコンストラクターの呼び出しが可能になるためです(コンパイラによって削除されます)、この追加の例外は、最初の例外がキャッチされる前に最初の例外処理を中断します。これにより、std::terminateが呼び出されます。C ++ 11コンパイラは、キャッチ時にコピーを排除することが許可されている場合がありますが、両方の省略が常に賢明であるとは限りません。また、賢明な場合は、許可のみであり、義務ではありません(https://en.cppreference.com/を参照)。詳細については、 w / cpp / language / copy_elisionを参照してください。C++11より前は、言語の標準で問題が規制されていませんでした)。
'*'また、コンストラクターからスローされる例外(追加と呼ばれる)を回避し、例外に使用されるタイプ(クラス)のコンストラクターを移動する(初期と呼ぶ)必要があります。これは、コンストラクターと移動コンストラクターが次の場合に呼び出される可能性があるためです。タイプのオブジェクトを初期例外としてスローし、次に追加の例外をスローすると、初期例外オブジェクトの作成が妨げられ、初期例外が失われるだけです。コピーコンストラクタからの追加の例外と同様に、最初の例外をスローすると、同じことが発生します。
例外でカスタムメッセージをスローする必要があるときはいつでも、を使用してCスタイルの文字列を作成しsnprintf()
、それを例外コンストラクターに渡します。
if (problem_occurred) {
char buffer[200];
snprintf(buffer, 200, "Could not load config file %s", configfile);
string error_mesg(buffer);
throw std::runtime_error(error_mesg);
}
余分な文字列string error_mesg(buffer)
が必要かどうかはわかりません。がスタックメモリ上にあると考えbuffer
ます。例外キャッチャーが実行を続ける場合、キャッチャーがスタックに割り当てられたC文字列への参照を保持できるようにするのは問題があります。string
代わりに、例外にaを渡すと、値によるコピーが呼び出され、buffer
配列がディープコピーされます。
カスタム例外のカスタムエラーメッセージを作成すると醜いコードになるという点で、同様の問題が発生しました。これが私の解決策でした:
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にはすでに内部バッファがあります。