2014-02-09 85 views
1

有時我需要將一些成員函數綁定到它的調用對象,以相同的方式處理成員函數和非成員函數。例如(該tipical回調爲例):綁定通用成員函數

#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()調用中。

現在考慮一個通用版本。不應該有問題,是不是?:

#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()重載只是將對象綁定到成員函數,並且因爲回調有三個參數,所以我們使用三個佔位符。

但考慮一下:如果回調函數有多少個參數會怎樣?
換句話說,我有什麼,如果callback_list類模板與可變參數模板?:

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

我如何可以綁定一個可變參數函數與在std::bind()調用點已知的任何功能參數定義做,也就是說,沒有指定數量的佔位符?

+1

相關? http://stackoverflow.com/q/21192659/420683 – dyp

+0

@dyp大聲笑我檢查了[這個](http://stackoverflow.com/questions/5608606/callback-with-variadic-template-to-ambiguous-overloads) ,[this](http://stackoverflow.com/questions/14803112/short-way-to-stdbind-member-function-to-object-instance-without-binding-param),[this](http:// stackoverflow.com/questions/11372855/stdbind-to-stdfunction)和[this](http://stackoverflow.com/questions/18380820/how-to-combine-stdbind-variadic-templates-and-perfect-forwarding)但我沒有找到該線程...非常感謝。 – Manu343726

+0

那麼[lambda版本](http://stackoverflow.com/a/14803335/420683)也會起作用,不是嗎? – dyp

回答

2

要使用std::bind,我們需要以某種方式提供一定數量的佔位符,具體取決於回調函數參數的數量。我所描述的方式here如何可以做到這一點,通過創建佔位符發電機:

template<int> struct placeholder_template {}; 

通過對上面的模板部分專業std::is_placeholderstd::bind看到實例placeholder_template<N>作爲佔位符類型。用通常的索引技巧,我們然後展開placeholder_template<0>{}, placeholder<1>{}, ...., placeholder<N-1>{},其中N是函數參數的數量。

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

的佔位符發生器和整數序列碼,從對方的回答:

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-預選賽成員函數,您可以使用非常一般的「模式」,如

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

並通過SFINAE進行限制。 Here's a trait,它允許您從這樣的函數指針中推斷出參數的數量(通過tuple_size)。

+0

謝謝,最終它的工作。只有一個問題:我不能將綁定的結果(即'std :: bind()'返回的綁定代理)分配給'std :: function',這當然可以正常工作。可以綁定播放更改結果代理的簽名,然後打破轉換? – Manu343726

+0

@ Manu343726這不是我在[現場示例](http://coliru.stacked-crooked.com/a/1d8a5563ab772d52)中做的事情嗎?我不得不再次看看Std,看看它是否真的需要工作,但對我來說,這聽起來像是一個圖書館的缺陷。你使用過哪個庫實現? – dyp