绑定通用成员函数 [英] Binding a generic member function

查看:91
本文介绍了绑定通用成员函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有时我需要绑定一些成员函数到它的调用对象,以同样的方式处理成员函数和非成员函数。例如(特征回调示例):

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 tipical callback example):

#include <vector>
#include <functional>

void f(int){}

struct foo
{
    void f(int){}
};

int main()
{
    using namespace std::placeholders;

    foo my_foo;
    std::vector<std::function<void()>> functions;

    functions.push_back( f );
    functions.push_back([](int){});
    functions.push_back( std::bind( &foo::f , my_foo , _1 ) );


    for( auto& function : functions )
    {
        function(0);
    }
}

成员函数具有更多的参数,需要放在 std :: bind()调用的内部。

As more parameters the member function has, more placeholders we need to put inside the std::bind() call.

现在考虑一个通用版本。不应该有问题,不是?:

Now consider a generic version of that. There shouldn't be problem, isn't?:

#include <vector>
#include <functional>

void f(int){}

struct foo
{
    void f(int){}
};

template<typename FIRST , typename SECOND , typename THIRD>
class callback_list
{
    using callback_t = std::function<void(FIRST,SECOND,THIRD>)>;



    //Overload for non-member handlers:
    void add( const callback_t& handler )
    {
        _handlers.push_back( handler );
    }

    //Overload for member handlers:
    template<typename CLASS>
    void add( CLASS& object_ref , 
                      void(CLASS::*member_function)( FIRST,SECOND,THIRD ) )
    {
        using namespace std::placeholders;

        _handlers.push_back( std::bind( member_function , 
                                        std::ref( object_ref ) , 
                                        _1 , _2 , _3
                                      ) 
                           );
    }

    template<typename... ARGS>
    void operator()( ARGS&&... args )
    {
        for( auto& handler : _handlers )
            handler( std::forward<ARGS>( args )... );
    } 

private:
    std::vector<callback_t> functions;
};


void f(int,int,int){}

struct foo
{
    void f(int,int,int){}
};

int main()
{
    using namespace std::placeholders;

    foo my_foo;
    callback_list<int,int,int> callbacks;

    callbacks.add( f );
    callbacks.add([](int,int,int){});
    callbacks.add( my_foo , &foo::f );

    callbacks(0,0,0);
}

好的。成员回调的 add()重载只是将对象绑定到成员函数,并且因为回调有三个参数,我们使用三个占位符。

Ok. The add() overload for member callbacks just binds the object to the member function, and because the callbacks are of three parameters, we use three placeholders.

但是请考虑这样:如果回调有任意数量的参数会怎样?

换句话说,如果 callback_list 类模板是用可变参数模板定义的:

But consider this: What if the callbacks have any number of parameters?.
In other words, what I have to do if the callback_list class template is defined with a variadic template?:

template<typename... ARGS>
class callback_list{ ... };

如何将变量函数与任何已知的函数参数绑定 std :: bind()调用,即,具有未指定数量的占位符?

How can I bind a variadic function with any function parameter known at the point of the std::bind() call, i.e., with an unspecified number of placeholders?

推荐答案

要使用 std :: bind 提供一定数量的占位符,具体取决于回调函数的参数数量。我在这里介绍了如何通过为占位符创建生成器的方法:

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

通过部分专门化 std :: is_placeholder 上述模板 std :: bind 将实例化 placeholder_template< N> 视为占位符类型。使用通常的索引技巧,然后将 placeholder_template< 0> {},占位符< 1> {},...,占位符< N-1> {} N 是函数参数的数量。

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






想要接受具有cv-和ref-qualifiers的成员函数,可以使用非常一般的模式,如


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

并通过SFINAE进行限制。 这里有一个特征,可让您从这样的函数指针中推导出参数的数量(通过 tuple_size )。

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

这篇关于绑定通用成员函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆