Binding a generic member function

前端 未结 1 1944
一向
一向 2021-01-07 02:51

Sometimes I need to bind some member functions to its calling object, to treat member functions and non-member functions in the same homogeneous way. For example (The tipica

相关标签:
1条回答
  • 2021-01-07 03:39

    To work with std::bind, we need to somehow supply a certain amount of placeholders, depending on the amount of function parameters of the callback. I've described a way here how you can do that, by creating a generator for placeholders:

    template<int> struct placeholder_template {};
    

    By partially specializing std::is_placeholder for the above template, std::bind sees the instantiations placeholder_template<N> as placeholder types. With the usual indices trick, we then expand placeholder_template<0>{}, placeholder<1>{}, ...., placeholder<N-1>{}, where N is the number of function parameters.

    template<typename... Params>
    class callback_list
    {
    public:
        using callback_t = std::function<void(Params...)>;
    
        //Overload for non-member handlers:
        void add( const callback_t& handler )
        {
            _handlers.push_back( handler );
        }
    
    private:
        //Overload for member handlers:
        template<typename CLASS, int... Is>
        void add( CLASS& object_ref , 
                  void(CLASS::*member_function)( Params... ) ,
                  int_sequence<Is...> )
        {
            using namespace std::placeholders;
    
            _handlers.push_back( std::bind( member_function , 
                                            std::ref( object_ref ) , 
                                            placeholder_template<Is>{}...
                                          ) 
                               );
        }
    public:
        template<typename CLASS>
        void add( CLASS& object_ref , 
                  void(CLASS::*member_function)( Params... ) )
        {
            add( object_ref, member_function,
                 make_int_sequence<sizeof...(Params)>{} );
        }
        
    
        template<typename... ARGS>
        void operator()( ARGS&&... args )
        {
            for( auto& handler : _handlers )
                handler( std::forward<ARGS>( args )... );
        } 
    
    private:
        std::vector<callback_t> _handlers;
    };
    

    The code for the placeholder generator and the integer sequence, from the other answer:

    template<int...> struct int_sequence {};
    
    template<int N, int... Is> struct make_int_sequence
        : make_int_sequence<N-1, N-1, Is...> {};
    template<int... Is> struct make_int_sequence<0, Is...>
        : int_sequence<Is...> {};
    
    template<int> // begin with 0 here!
    struct placeholder_template
    {};
    
    #include <functional>
    #include <type_traits>
    
    namespace std
    {
        template<int N>
        struct is_placeholder< placeholder_template<N> >
            : integral_constant<int, N+1> // the one is important
        {};
    }
    

    Using C++14, one can use std::index_sequence_for<Params...> instead of custom make_int_sequence<sizeof...(Params)>.


    Side remark: If you want to accept member functions with cv- and ref-qualifiers, you could use a very general "pattern" like

    template<class C, class T>
    void add(C& ref, T fun);
    

    and restrict via SFINAE. Here's a trait that lets you deduce the number of parameters from such a function pointer (via tuple_size).

    0 讨论(0)
提交回复
热议问题