2010-11-29

std::functionの真に正しい実装

だいぶ前の記事では、target()の意味を勘違いしていた。void *へのキャストが出てくるので、おかしいと思ったのだ。しかし、std::functionのtargetって誰が使うんだ、これ。


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 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 result_type invoke( ArgTypes... args )
        {
            return f( std::forward<ArgTypes>(args)... ) ;
        }

        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 ) ;
        }

        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)... ) ;
        }

        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 ) ;
        }

        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... ) ;
        }

        DATA T::* f ;
    } ;

    holder_base * ptr ;

private :

    template < typename F >
    struct make_holder_type
    {
        typedef holder < F > type ;
    } ;
    template < typename holder_R, typename T, typename ... Types >
    struct make_holder_type< holder_R (T::*)( Types... ) >
    {
        typedef member_holder<holder_R, T, Types...> type ;
    } ;

    template < typename T, typename DATA >
    struct make_holder_type< DATA T::* >
    {
        typedef data_member_holder<T, DATA> type ;
    } ;

    template < typename F >
    static holder_base * allocate_holder(F f)
    {
        typedef typename make_holder_type<F>::type type ;
        return new type( 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 nullptr ;

        typedef typename make_holder_type<T>::type type ;
        auto actual_ptr = static_cast< type * >( ptr ) ;
        return &actual_ptr->f ;
        
    }

    template < typename T >
    T const * target() const
    {
        if ( target_type() != typeid(T) )
            return nullptr ;

        typedef typename make_holder_type<T>::type type ;
        auto actual_ptr = static_cast< type * >( ptr ) ;
        return &actual_ptr->f ;
    }

} ;

}

No comments: