web-dev-qa-db-ja.com

可変メッセージでstd :: exceptionsをスローする方法は?

これは、例外に情報を追加したいときによく行うことの例です。

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

より良い方法がありますか?

98
Ben

私の解決策は次のとおりです。

#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
39
Torsten

標準の例外は、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::exceptionnotのように構築できることに注意してください。具体的な派生クラスのいずれかを使用する必要があります。

152
Kerrek SB

runtime_errorrange_erroroverflow_errorlogic_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);
24
Neel Basu

次のクラスは非常に便利です。

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());
15

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);

きれいに見えます。

11
Shreevardhan

本当に良い方法は、例外用のクラスを作成することです

何かのようなもの:

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 で非常によく説明されています

したがって、より良い方法は、特定の種類の例外であり、書式設定された文字列の作成を回避することです(少なくともスローする場合)。

0