3

私の問題は、将来のために読み取り可能であるはずのプログラムを書いていることであり、そのプログラムには多くの例外ケースがあります。そのため、例外をスローする必要があるときはいつでも、例外クラスを初期化し、プログラムからの情報を例外クラスに追加するために、10 行以上を作成する必要があります。たとえば、次のようにします。

MyExceptionClass ex;
ex.setErrorMessage("PIN_CANNOT_GO_IN");
ex.setErrorDetails("The pin is asked to go to the state IN while the depth of the r-coordinate does not support it");
ex.setResolutionMessage("Configure your coordinates-file to move first to the correct position before changing the state of the pin");
ex.VariableList().resize(5);
ex.VariableList()[0].push_back("Pin state: ");
ex.VariableList()[0].push_back(ToString(pin.getPinState()));
ex.VariableList()[1].push_back("Pin target state: ");
ex.VariableList()[1].push_back(ToString(coordinatesData[coordinatesIndex].targetPinState));
ex.VariableList()[2].push_back("Current r Value: ");
ex.VariableList()[2].push_back(ToString(EncoderPosition.r));
ex.VariableList()[3].push_back("Current phi Value: ");
ex.VariableList()[3].push_back(ToString(EncoderPosition.phi));
ex.VariableList()[4].push_back("Current z Value: ");
ex.VariableList()[4].push_back(ToString(EncoderPosition.z));

ex.printLog();
ex.writeLog(exceptionLogFilePath.getValue());

throw ex;

そのため、たった 5 つの変数について、すべてを記述する必要がありました...プログラムからのすべての情報 (少なくとも変数) を含め、例外をスローするたびにすべてを書き直さないようにする効率的な方法はありますか?

前もって感謝します。

4

6 に答える 6

4

一般的な例外の VariableList オブジェクトに入力する共通関数 (fill_out_exception_parameters) を使用し、作成する新しい例外クラスでそれを再利用できます。

于 2013-02-10T09:57:37.020 に答える
2

例外クラスに追加されたデータがエラー メッセージを表示するためだけに使用される場合は、文字列連結を使用して、push_back()使用される数を減らすことができます。

たとえば、次のように使用できます。

ex.VariableList()[0].push_back(string("Pin state: ") + ToString(pin.getPinState());

それぞれに個別のインデックス (1、2、3、4 など) を使用する代わりに、他のすべてのメッセージを連結することもできます。

さらに、各フィールドに対して、専用のセッター メソッドを使用して適切な値をフィードできます。例えば:

ex.VariableList()[0].setPinState(ToString(pin.getPinState()));

次に、パーツをエラー メッセージが表示され"Pin state: "ている場所に移動する必要があります。


さらに進んで、例外クラスは、エラー メッセージに寄与するすべてのオブジェクトを受け入れる専用のメソッドを持ち、代わりにそのメッセージを呼び出すことができます。例えば:

void MyExceptionClass::setMessage(Pin& pin, CoordinatesData& cd, EncoderPosition& ep) {
    setPinState(ToString(pin.getPinState()));
    // set whatever else you want here
}

さらに、ToString()メッセージが出力される場所にパーツを移動し、例外クラスに値を保存するだけです。たとえば、上記の行を次のように変更します (署名を適宜変更する必要があります)。

setPinState(pin.getPinState());

そして、印刷ロジックにそれを文字列に変換する方法を決定させます。さらなる利点は、同じメッセージを異なる形式で印刷できることです。

于 2013-02-10T09:53:55.983 に答える
1

Boost Exception を使用して、例外オブジェクトに任意のデータを追加することを合理化し、コール スタックをバブルアップするときに、より関連性の高いデータでそれらを拡張することができます。必要に応じて任意の例外に文字通り任意のデータを保存できるため、例外に保存する必要がある可能性のあるすべてのものを事前に定義することについて心配する必要はありません。

于 2013-02-17T07:27:27.513 に答える
0

これらのテキストによる説明は、ランタイムデータとして各インスタンスに書き込まれるのではなく、本質的に例外クラスにリンクされている必要があります。

同様に、そのすべての情報データは例外クラスのメンバーである必要があり、後でテキストとして出力するためにフォーマットできます(おそらく例外クラス自体のメンバー関数で)。

于 2013-02-10T14:11:37.840 に答える
0

同様の問題がありました:コンテキスト情報で例外を強化するにはどうすればよいですか?

Boost が提案する 1 つの解決策は、再スローする前にブロックtry/catch内の例外を強化することです。例外を強化しますが、自動的catchには強化しません。

私が最終的に思いついた解決策は、C++ デストラクタの強度に基づいた非常に単純なものです。しかし、最初に、それを使用する方法:

void foo(int i) {
    LOG_EX_VAR(i);
    // do something that might throw
}

はい、これですべてです。単一のマクロ呼び出しiが、マクロが展開された関数名、ファイル名、および行番号と共に例外コンテキストに追加されます。

背後にあるものは何ですか?最も重要な constとちょっとした魔法。

class LogVar {
public:
    LogVar(LogVar const&) = delete;
    LogVar& operator=(LogVar const&) = delete;

    virtual ~LogVar() {}

protected:
    LogVar();
}; // class LogVar

template <typename T>
class LogVarT: public LogVar {
public:
    LogVarT(char const* fc, char const* fl, int l, char const* n, T const& t):
        _function(fc), _filename(fl), _line(l), _name(n), _t(t) {}

    ~LogVar() {
        ContextInterface::AddVariable(_function, _filename, _line, _name, _t);
    }

private:
    char const* _function;
    char const* _filename;
    int _line;

    char const* _name;
    T const& _t;
}; // class LogVarT

template <typename T>
LogVarT make_log_var(char const* fc,
                     char const* fl,
                     int l,
                     char const* n,
                     T const& t)
{
    return LogVarT(fc, fl, l, n, t);
}

#define LOG_EX_VAR(Var_)                                                      \
    LogVar const& BOOST_PP_CAT(_5416454614, Var_) =                           \
        make_log_var(__func__, __FILE__, __LINE__, #Var_, Var_);

ContextInterface::AddVariable()難しい部分 (関数) を機能させることができれば、これはかなりうまく機能します。

あなたがそれを気にしたくない場合は、thread_local std::vector<LogVar*>あなたがしたように行ってください. 無駄に多くの仕事をしていることに注意してください。

興味のある方はフォローしてみてください。

  1. ここで最も重要な部分は、スレッドセーフなものを取得することです。したがって、コンテキストはグローバルになります...スレッドごと(別名thread_local)。しかし、その場合でも、誤って参照を外部に漏らしてしまう可能性があります。
  2. いくつかの例外が共存する可能性があることを理解することが重要です。ただし、特定の時点でキャッチされない例外は 1 つだけです。つまり、catch句内で例外がスローされる可能性があります。
  3. 自分自身でスローした例外のみをインストルメント化できるため、他の例外にはある種のデフォルト ポリシーが必要です。たとえば、ロギング。

それでは、インターフェイスをまっすぐにしましょう。

class ContextInterface {
public:
    typedef std::unique_ptr<ContextInterface> UPtr;
    typedef std::shared_ptr<ContextInterface> SPtr;
    typedef std::weak_ptr<ContextInterface> WPtr;

    static UPtr SetDefault(UPtr d) {
        std::swap(d, DefaultContext);
        return d;
    }

    template <typename T, typename... Args>
    static SPtr SetActive(Args&&... args) {
        SPtr ci = ExceptionContext.lock();
        if (ci.get()) { return ci; }

        ci.reset(new T(std::forward<Args>(args)...));
        ExceptionContext = ci;
        return ci;
    }

    template <typename T>
    static void AddVariable(char const* fc,
                            char const* fl,
                            int l,
                            char const* n,
                            T const& t)
    {
        SPtr sp = ExceptionContext.lock();
        ContextInterface* ci = sp.get();

        if (not ci) { ci = DefaultContext.get(); }

        if (not ci) { return; }

        ci->report(fc, fl, l, n) << t;
    }

    virtual ~ContextInterface() {}

private:
    static thread_local UPtr DefaultContext;
    static thread_local WPtr ExceptionContext;

    virtual std::ostream& report(char const* fc,
                                 char const* fl,
                                 int l,
                                 char const* n) = 0;
}; // class ContextInterface

そして最後に、最後の欠落部分 (まあ、私が推測したい実際のコンテキストは別として): 基本例外クラスの例です。

class ContextualException: public virtual std::exception {
public:
    ContextualException(): _c(ContextInterface::SetActive<ExceptionContext>()) {}

    ContextInterface const& context() const { return *_c; }

private:
    ContextInterface::SPtr _c;
}; // class ContextualException
于 2013-02-10T14:01:52.133 に答える
0

私はそれを行うための最もクリーンな方法を得たと思います。あなたの考えを聞かせてください。

したがって、次のように、関連するすべての変数をテンプレート化されたクラスにカプセル化します (簡単な例です)。

class VarBase
{
VarBase();
static std::vector<VarBase*> __allParams;
string getStringValue() = 0;
};

template <typename T>
class Var : public VarBase
{
    T value;
    string name;
    string description;
    toString();
    operator T();
    string getStringValue();
};

VarBase::VarBase()
{
    __allParams.push_back(this);
}
VarBase::~VarBase()
{
    //handle removing from __allParams vector or whatever container
}
template <typename T>
std::string Var<T>::getStringValue()
{
    std::stringstream s;
    s << paramValue;
    return s.str();
}

私の例外クラスがVarBaseクラスと友達である場合、__allParamsにアクセスしてループし、 getStringValue() を呼び出すことができます.

追加のアイデアは大歓迎です。

于 2013-02-10T10:30:11.703 に答える