2010-03-16

簡易版std::functionを実装してみた

誰かが、Boost::functionを読めばレベルが3ぐらい上がるとつぶやいていた。読もうとしたが、五分で諦めた。プリプロセッサを使いすぎなのだ。だいたい、std::functionぐらいなら、自分で実装した方が、レベルが上がるのではないかと思う。というわけで、4時間ぐらいかけて、書いてみた。

アロケーターも、似たようなType Erasureのテクニックを使って実装すればいい。

ところで、target_type()の文面がよく分からない。Tとあるが、target_type()はテンプレートではないし、Tが定義されていないのではないか。

追記、target_type()を理解したが、実装方法が面倒だ。さて、どうするか。

追記、target_type()/target()を実装した。さらに、perfect forwardingになった。

namespace hito {

class bad_function_call : public std::exception
{
public:
    bad_function_call() {}
    virtual char const * what() const throw()
    {
        return "bad function call exception" ;
    }
} ;

template < typename > class function ;// primary template.

template < typename R, typename ... ArgTypes >
class function< R ( ArgTypes... ) >
{
public :
    typedef R result_type ;

private :

    struct holder_base
    {
        virtual ~holder_base() {}
        virtual holder_base * clone() const = 0 ;
        virtual std::type_info const & target_type() const = 0 ;
        virtual void * target() = 0 ;

        virtual result_type invoke( ArgTypes... ) = 0 ;
    } ;

    template < typename F >
    struct holder : public holder_base
    {
    public :
        holder(F f) : f( f )
        { }
        virtual ~holder() { }

        virtual holder_base * clone() const
        {
            return allocate_holder( f ) ;
        }

        virtual std::type_info const & target_type() const
        {
            return typeid( F ) ;
        }

        virtual void * target()
        {
            return &f ;
        }

        virtual result_type invoke( ArgTypes... args )
        {
            return f( std::forward<ArgTypes>(args)... ) ;
        }
    private :
        F f ;
    } ;

    template < typename holder_R, typename T, typename ... Types >
    struct member_holder : public holder_base
    {
    public :
        member_holder( holder_R (T::* const f)( Types... ) )
            : f(f)
        { }
        virtual ~member_holder() {}

        virtual holder_base * clone() const
        {
            return allocate_holder( f ) ;
        }

        virtual std::type_info const & target_type() const
        {
            return typeid( f ) ;
        }


        virtual void * target()
        {
            return &f ;
        }

        result_type invoke_impl( T t1, Types... args )
        {
            return (t1.*f)( std::forward<Types>(args)... ) ;
        }

        result_type invoke_impl( T * t1, Types... args )
        {
            return ((*t1).*f)( std::forward<Types>(args)... ) ;
        }

        virtual result_type invoke( ArgTypes ... args )
        {
            return invoke_impl( std::forward<ArgTypes>(args)... ) ;
        }

    private :
        holder_R (T::* f)( Types...) ;
    } ;

    template < typename T, typename DATA >
    struct data_member_holder : holder_base
    {
    public :
        data_member_holder( DATA T::* const f )
            : f( f )
        { }

        virtual ~data_member_holder() { }

        virtual holder_base * clone() const
        {
            return allocate_holder( f ) ;
        }

        virtual std::type_info const & target_type() const
        {
            return typeid( f ) ;
        }

        virtual void * target()
        {
            return &f ;
        }

        result_type invoke_impl( T & t1 )
        {
            return t1.*f ;
        }

        result_type invoke_impl( T * const t1 )
        {
            return (*t1).*f ;
        }

        virtual result_type invoke( ArgTypes ... args )
        {
            return invoke_impl( args... ) ;
        }

    private :
        DATA T::* f ;
    } ;

    holder_base * ptr ;

private :

    template < typename F >
    static holder_base * allocate_holder(F f)
    {
        return new holder< F >( f ) ;
    }

    template < typename holder_R, typename T, typename ... Types >
    static holder_base * allocate_holder( holder_R (T::* f)( Types... ) )
    {
        return new member_holder<holder_R, T, Types...>( f ) ;
    }

    template < typename T, typename DATA >
    static holder_base * allocate_holder( DATA T::* f )
    {
        return new data_member_holder<T, DATA>( f ) ;
    }

    void deallocate_holder()
    {
        delete ptr ;
        ptr = nullptr ;
    }

    holder_base * clone() const
    {
        if ( ptr != nullptr )
            return ptr->clone() ;
        else
            return nullptr ;
    }
    
public :

    explicit function() : ptr( nullptr )  { }
    function( std::nullptr_t ) : ptr( nullptr ) { }

    function( function const & init_expr ) 
        : ptr( init_expr.clone() )
    { }

    function( function && init_expr )
        : ptr( init_expr.ptr )
    {
        init_expr.ptr = nullptr ;
    }

    template < typename F >
    function( F f )
        : ptr( allocate_holder( f ) )
    { }


    function & operator = ( function const & assign_expr )
    {
        if ( this == &assign_expr )
            return *this ;

        deallocate_holder() ;
        this->ptr = assign_expr.clone() ;

        return *this ;
    }

    function & operator = ( function && assign_expr )
    {
        if ( this == &assign_expr )
            return *this ;

        deallocate_holder() ;
        this->ptr = assign_expr.ptr ;
        assign_expr.ptr = nullptr ;

        return *this ;
    }

    function & operator = ( std::nullptr_t )
    {
        deallocate_holder() ;
        return *this ;
    }

    template < typename F >
    function & operator = ( F f )
    {
        delete ptr ;
        ptr = allocate_holder( f ) ;
    }

    ~function()
    {
        deallocate_holder() ;
    }

    void swap( function & arg )
    {
        std::swap( this->ptr, arg.ptr ) ;
    }
    
    explicit operator bool() const
    {
        return bool( ptr != nullptr ) ;
    }

    result_type operator () ( ArgTypes... args ) const
    {
        if ( ptr != nullptr)
            return ptr->invoke( std::forward<ArgTypes>(args)... ) ;
        else
            throw bad_function_call() ;
    }


    std::type_info const & target_type() const
    {
        if ( ptr != nullptr )
            return ptr->target_type() ;
        else
            return typeid( void ) ;
    }

    template < typename T >
    T * target()
    {
        if ( target_type() == typeid(T) )
            return static_cast<T *>( ptr->target() ) ;
        else
            return nullptr ;
    }

    template < typename T >
    T const * target() const
    {
        if ( target_type() == typeid(T) )
            return static_cast<T const *>( ptr->target() ) ;
        else
            return nullptr ;
    }

} ;

}

No comments:

Post a Comment

You can use some HTML elements, such as <b>, <i>, <a>, also, some characters need to be entity referenced such as <, > and & Your comment may need to be confirmed by blog author. Your comment will be published under GFDL 1.3 or later license with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.