同様の問題がありました:コンテキスト情報で例外を強化するにはどうすればよいですか?
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*>
あなたがしたように行ってください. 無駄に多くの仕事をしていることに注意してください。
興味のある方はフォローしてみてください。
- ここで最も重要な部分は、スレッドセーフなものを取得することです。したがって、コンテキストはグローバルになります...スレッドごと(別名
thread_local
)。しかし、その場合でも、誤って参照を外部に漏らしてしまう可能性があります。
- いくつかの例外が共存する可能性があることを理解することが重要です。ただし、特定の時点でキャッチされない例外は 1 つだけです。つまり、
catch
句内で例外がスローされる可能性があります。
- 自分自身でスローした例外のみをインストルメント化できるため、他の例外にはある種のデフォルト ポリシーが必要です。たとえば、ロギング。
それでは、インターフェイスをまっすぐにしましょう。
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