TrickDiary このページをアンテナに追加 RSSフィード Twitter

2008-12-27

[] 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;
}

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証