C++0x の nested_exception について 其の弐(実装)
C++0x の nested_exception について 其の壱(仕様) の続きです。前回翻訳した仕様を元により理解を深める為に 2003 ベースの仕様に準拠しているコンパイラで利用可能な nested_exception を実装しました。サンプルコードは http://d.hatena.ne.jp/melpon/20081203/1228289137 をベースに作成しました。
nestexcp.h
// ■制限及び注意事項 // ・このヘッダーファイルの影響を受けないコードから throw された例外は // 正しく処理されません。 // ・送出(throw)する例外の型は複写構築可能なものでなければなりません。 // → throw に元からある制限ですが。(§15.1¶5) // ・例外仕様( 関数が throw する例外型を明示しておくヤツ )は使えません。 // ・例外オブジェクトを指定しない再送出( throw; とかってするヤツ )もで // きません。 // → std::ex::rethrow(); がその代用として利用できます。 // ・再送出の際に送出される型が const 化されます。 #include <assert.h> #include <exception> namespace std { namespace private_namespace { inline void set_current_exception_ptr(const class exception_core_ptr *); template<class T> inline void set_current_exception(T &); class exception_core_ptr { public: typedef exception_core_ptr this_type; protected: mutable int ref_count; public: exception_core_ptr() :ref_count(1) { } void inc_ref_count() const { ++ref_count; } void dec_ref_count() const { if (0 == --ref_count) { delete this; } } virtual void throw_target() const { } protected: // 外部からのデストラクト禁止(このクラスは自殺型です。) virtual ~exception_core_ptr() { assert(0 == ref_count); } private: // コピー禁止 exception_core_ptr(const this_type &); // 代入禁止 void operator = (const this_type &); }; template<class T> class actual_exception_core_ptr :public exception_core_ptr { public: typedef actual_exception_core_ptr<T> this_type; T target; actual_exception_core_ptr(const T & a) :exception_core_ptr(), target(a) { } virtual void throw_target() const // [[noreturn]] { set_current_exception_ptr(this); throw target; } private: // コピー禁止 exception_ptr_value(const this_type &); // 代入禁止 void operator = (const this_type &); }; } class exception_ptr { public: typedef exception_ptr this_type; typedef private_namespace::exception_core_ptr core_type; typedef const core_type * const_ptr_type; typedef core_type * ptr_type; protected: const_ptr_type value; public: exception_ptr() :value(NULL) { } exception_ptr(const_ptr_type a_value) :value(a_value) { inc_ref_count(); } exception_ptr(const this_type & a) :value(a.value) { inc_ref_count(); } template<class T> exception_ptr(T * a_value) :value(exception_cast<T>(a_value)) { inc_ref_count(); } ~exception_ptr() { dec_ref_count(); } this_type & operator = (const_ptr_type a_value) { if (value != a_value) { set_value(a_value); } return *this; } this_type & operator = (const this_type & a) { return *this = a.value; } template<class T> this_type & operator = (T a_value) { set_value(exception_cast<T>(a_value)); return *this; } void throw_target() const { if (value) { value->throw_target(); } } friend bool operator == (const this_type & a, const this_type & b) { return a.value == b.value; } friend bool operator != (const this_type & a, const this_type & b) { return a.value != b.value; } protected: void set_value(const_ptr_type a_value) { dec_ref_count(); value = a_value; inc_ref_count(); } template<class T> ptr_type exception_cast(T & a) { using namespace private_namespace; try { return new actual_exception_core_ptr<T>(a); } catch(...) { try { return new actual_exception_core_ptr<std::bad_exception>(std::bad_exception()); } catch(...) { return NULL; } } } void inc_ref_count() { if (value) { value->inc_ref_count(); } } void dec_ref_count() { if (value) { value->dec_ref_count(); } } }; namespace private_namespace { template<class dummy> class current_exception_manager_body { public: static exception_ptr current_exception; // スレッドローカルな値だと思ってください。 template<class T> T operator = (T a) { set_current_exception(a); return a; } }; template<class dummy> exception_ptr current_exception_manager_body<dummy>::current_exception; typedef current_exception_manager_body<int> current_exception_manager; inline void set_current_exception_ptr(const exception_core_ptr * a) { std::private_namespace::current_exception_manager::current_exception = a; } template<class T> inline void set_current_exception(T & a) { std::private_namespace::current_exception_manager::current_exception = a; } } exception_ptr current_exception() { return std::private_namespace::current_exception_manager::current_exception; } void rethrow_exception(exception_ptr a) { // a が null でないことを要求する。(§18.7.5¶9) // a が null の場合の挙動は未定義...とすら記述されていない。 a.throw_target(); } template<class T> exception_ptr copy_exception(T a) { #if 0 try { throw a; } catch(...) { return current_exception(); } #else std::private_namespace::set_current_exception<T>(a); return current_exception(); #endif } } // http://tricklib.com/cxx/dagger/inherit.h よりハンドインクルード namespace tricklib { // 単継承 template<class T1> class monomorph :public T1 { public: typedef T1 parent_type; typedef monomorph<parent_type> this_type; typedef T1 primary_type; monomorph(const T1 &X1) :primary_type(X1) { } monomorph(const this_type &X) :primary_type(X) { } this_type & get_monomorph() { return *this; } const this_type & get_monomorph() const { return *this; } primary_type & get_primary() { return *this; } const primary_type & get_primary() const { return *this; } }; template<class T1> inline monomorph<T1> get_monomorph(const T1 &X1) { return monomorph<T1>(X1); } template<class T1> inline monomorph<T1> * new_monomorph(const T1 &X1) { return new monomorph<T1>(X1); } // 二重継承 template<class T1, class T2> class dimorph :public monomorph<T1>, public T2 { public: typedef monomorph<T1> parent_type; typedef dimorph<parent_type, T2> this_type; typedef T1 primary_type; // bcc が馬鹿なので定義し直す。 typedef T2 secondary_type; dimorph(const T1 &X1, const T2 &X2) :parent_type(X1), secondary_type(X2) { } dimorph(const this_type &X) :parent_type(X), secondary_type(X) { } this_type & get_dimorph() { return *this; } const this_type & get_dimorph() const { return *this; } secondary_type & get_secondary() { return *this; } const secondary_type & get_secondary() const { return *this; } }; template<class T1, class T2> inline dimorph<T1, T2> get_dimorph(const T1 &X1, const T2 &X2) { return dimorph<T1, T2>(X1, X2); } template<class T1, class T2> inline dimorph<T1, T2> * new_dimorph(const T1 &X1, const T2 &X2) { return new dimorph<T1, T2>(X1, X2); } } namespace std { class nested_exception { exception_ptr value; public: nested_exception() throw() :value(current_exception()) { // nop; } nested_exception(const nested_exception & a) throw() // = default; :value(a.value) { // nop; } nested_exception& operator=(const nested_exception & a) throw() // = default; { value = a.value; return *this; } virtual ~nested_exception() // = default; { // nop; } // access functions void rethrow_nested() const // [[noreturn]] { value.throw_target(); } exception_ptr nested_ptr() const { return value; } }; template <class E> void rethrow_if_nested(const E & a_e) { const nested_exception * e = dynamic_cast<const nested_exception *>(&a_e); if (e) { e->rethrow_nested(); } } //template<class T> void throw_with_nested(T && t) // [[noreturn]] template<class T> void throw_with_nested(T t) // [[noreturn]] { std::private_namespace::current_exception_manager exception_manager; nested_exception * e = dynamic_cast<nested_exception *>(&t); if (e) { throw exception_manager = t; } else { throw exception_manager = tricklib::dimorph<nested_exception, T>(nested_exception(), t); } } namespace ex { inline void rethrow() { if (exception_ptr() == current_exception()) { throw; } else { current_exception().throw_target(); } } } } #define throw throw std::private_namespace::current_exception_manager() =
サンプルコード
#include <stdio.h> #include <stdlib.h> #include <exception> #include <iostream> #include "nestexcp.h" class my_nested : public std::exception { private: const char* what_; public: my_nested(const char* what) : what_(what) { } virtual const char * what() const { return what_; } }; void output_if_exception(const std::exception * e) { std::cout << e->what() << std::endl; } void output_if_exception(...) { std::cout << "unknown exception" << std::endl; } class hoge_exception { public: hoge_exception(const char * what) { } }; int main(int argc, char * args []) { argc, args; try { try { try { throw hoge_exception("Inner Inner Error!"); } catch (...) { throw_with_nested(my_nested("Inner Error!")); } } catch (...) { throw_with_nested(my_nested("Error!")); } } catch (my_nested & e) { output_if_exception(&e); try { rethrow_if_nested(e); } catch (const my_nested & e2) { output_if_exception(&e2); try { rethrow_if_nested(e2); } catch (const hoge_exception & e3) { output_if_exception(&e3); } } } return EXIT_SUCCESS; }