2012-10-26 108 views
4

我有一個KeyCallbacks向量:typedef boost::function<void (const KeyEvent&)> KeyCallback,我用這個向量來存儲鍵盤按鈕被按下時所有的監聽器。我可以將它們添加並分派事件與for_each的所有回調,但我不知道如何實際刪除從我的矢量特定KeyCallback簽名,比如我想是這樣的:在容器中存儲boost :: function對象

void InputManager::UnregisterCallback(KeyCallback callback)      { mKeyCallbacks.erase(std::find(mKeyCallbacks.begin(), mKeyCallbacks.end(), callback));       } 

據提高: :函數文檔,沒有比較函數對象(http://www.boost.org/doc/libs/1_51_0/doc/html/function/faq.html#id1564716),這將解釋我的問題與以上。那麼我卡住了嗎?有沒有什麼好方法呢?

(我讀到的boost ::信號回撥機制,但它顯然是很慢的,我希望回調被解僱可能多次幀)

回答

8

方法1:

http://www.boost.org/doc/libs/1_51_0/doc/html/function/tutorial.html#id1546064

函數對象包裝可以通過==或!=與任何可以存儲在包裝中的函數對象進行比較。

因此,其中一個解決方案是爲UnregisterCallback的參數(它也支持類型擦除)定義特殊類型。這是基於事實的,您可以將boost :: function與functor/function進行比較 - 因爲結果仍然會有boost :: function的向量,只有需要執行比較的地方纔需要新類型,例如, UnregisterCallback:

LIVE DEMO

#include <boost/scoped_ptr.hpp> 
#include <boost/function.hpp> 
#include <algorithm> 
#include <iostream> 
#include <typeinfo> 
#include <ostream> 
#include <vector> 
#include <string> 

using namespace std; 
using namespace boost; 

typedef int KeyEvent; 
typedef function<void (const KeyEvent &)> KeyCallback; 

struct AbstractCallback 
{ 
    virtual bool equals(const KeyCallback &f) const=0; 
    virtual ~AbstractCallback(){} 
}; 

template<typename Callback> 
struct ConcreteCallback : AbstractCallback 
{ 
    const Callback &callback; 
    explicit ConcreteCallback(const Callback &p_callback) : callback(p_callback) {} 
    virtual bool equals(const KeyCallback &f) const 
    { 
     return callback == f; 
    } 
}; 

struct KeyCallbackChecker 
{ 
    scoped_ptr<AbstractCallback> func; 
public: 
    template<typename Func> 
    KeyCallbackChecker(const Func &f) : func(new ConcreteCallback<Func>(f)) {} 
    friend bool operator==(const KeyCallback &lhs,const KeyCallbackChecker &rhs) 
    { 
     return rhs.func->equals(lhs); 
    } 
    friend bool operator==(const KeyCallbackChecker &lhs,const KeyCallback &rhs) 
    { 
     return rhs==lhs; 
    } 
}; 

void func1(const KeyEvent &) 
{ 
    cout << "func1" << endl; 
} 

void func3(const KeyEvent &) 
{ 
    cout << "func3" << endl; 
} 

class func2 
{ 
    int data; 
public: 
    explicit func2(int n) : data(n) {} 
    friend bool operator==(const func2 &lhs,const func2 &rhs) 
    { 
     return lhs.data==rhs.data; 
    } 
    void operator()(const KeyEvent &) 
    { 
     cout << "func2, data=" << data << endl; 
    } 
}; 

struct Caller 
{ 
    template<typename F> void operator()(F f) 
    { 
     f(1); 
    } 
}; 

class Callbacks 
{ 
    vector<KeyCallback> v; 
public: 
    void register_callback(const KeyCallback &callback) 
    { 
     v.push_back(callback); 
    } 
    void unregister_callback(const KeyCallbackChecker &callback) 
    { 
     vector<KeyCallback>::iterator it=find(v.begin(),v.end(),callback); 
     if(it!=v.end()) 
      v.erase(it); 
    } 
    void call_all() 
    { 
     for_each(v.begin(),v.end(),Caller()); 
     cout << string(16,'_') << endl; 
    } 
}; 

int main(int argc,char *argv[]) 
{ 
    Callbacks cb; 
    cb.register_callback(func1); 
    cb.register_callback(func2(1)); 
    cb.register_callback(func2(2)); 
    cb.register_callback(func3); 
    cb.call_all(); 

    cb.unregister_callback(func2(2)); 
    cb.call_all(); 
    cb.unregister_callback(func1); 
    cb.call_all(); 

return 0; 
} 

輸出是:

func1 
func2, data=1 
func2, data=2 
func3 
________________ 
func1 
func2, data=1 
func3 
________________ 
func2, data=1 
func3 
________________ 

優點

  • 我們仍然使用boost ::功能在註冊和向量保存
  • 函子對象只應在需要將其傳遞給unregister_callback時定義比較
  • 它可以簡單地概括 - 只需添加一個模板參數而不是使用typedefed KeyCallback。所以,可以很容易地在其他地方使用,用於其他類型的回調。

缺點

  • 如果用戶已經有了回調包裹提振::功能 - 它不能與unregister_callback使用,因爲它需要的東西,可以比較的boost ::功能(例如函數指針,或具有定義的比較算符)


方法#2:

另一種方法是實現自定義boost :: function類似的解決方案,它接受可比較的回調。

LIVE DEMO

#include <boost/shared_ptr.hpp> 
#include <algorithm> 
#include <iostream> 
#include <typeinfo> 
#include <ostream> 
#include <vector> 

using namespace std; 
using namespace boost; 

typedef int KeyEvent; 
typedef void (*func_type)(const KeyEvent &); 

struct AbstractCallback 
{ 
    virtual void operator()(const KeyEvent &p)=0; 
    virtual bool compare_to(const std::type_info &rhs_type,const void *rhs) const=0; 
    virtual bool compare_to(const std::type_info &rhs_type,const func_type *rhs) const=0; 
    virtual bool equals(const AbstractCallback &rhs) const=0; 
}; 

template<typename Callback> 
struct ConcreteCallback : AbstractCallback 
{ 
    Callback callback; 
    ConcreteCallback(Callback p_callback) : callback(p_callback) {} 
    void operator()(const KeyEvent &p) 
    { 
     callback(p); 
    } 
    bool compare_to(const std::type_info &rhs_type,const void *rhs) const 
    { 
     return (typeid(Callback)==rhs_type) && 
      (*static_cast<const Callback*>(rhs) == callback); 
    } 
    bool compare_to(const std::type_info &rhs_type,const func_type *rhs) const 
    { 
     return false; 
    } 
    bool equals(const AbstractCallback &rhs) const 
    { 
     return rhs.compare_to(typeid(Callback),&callback); 
    } 
}; 

template<> 
struct ConcreteCallback<func_type> : AbstractCallback 
{ 
    func_type callback; 
    ConcreteCallback(func_type p_callback) : callback(p_callback) {} 
    void operator()(const KeyEvent &p) 
    { 
     callback(p); 
    } 
    bool compare_to(const std::type_info &rhs_type,const void *rhs) const 
    { 
     return false; 
    } 
    bool compare_to(const std::type_info &rhs_type,const func_type *rhs) const 
    { 
     return *rhs == callback; 
    } 
    bool equals(const AbstractCallback &rhs) const 
    { 
     return rhs.compare_to(typeid(func_type),&callback); 
    } 
}; 


struct KeyCallback 
{ 
    shared_ptr<AbstractCallback> func; 
public: 
    template<typename Func> 
    KeyCallback(Func f) : func(new ConcreteCallback<Func>(f)) {} 
    friend bool operator==(const KeyCallback &lhs,const KeyCallback &rhs) 
    { 
     return lhs.func->equals(*rhs.func); 
    } 
    void operator()(const KeyEvent &p) 
    { 
     (*func)(p); 
    } 
}; 

void func1(const KeyEvent &) 
{ 
    cout << "func1" << endl; 
} 

void func3(const KeyEvent &) 
{ 
    cout << "func3" << endl; 
} 

class func2 
{ 
    int data; 
public: 
    func2(int n) : data(n) {} 
    friend bool operator==(const func2 &lhs,const func2 &rhs) 
    { 
     return lhs.data==rhs.data; 
    } 
    void operator()(const KeyEvent &) 
    { 
     cout << "func2, data=" << data << endl; 
    } 
}; 

struct Caller 
{ 
    template<typename F> 
    void operator()(F f) 
    { 
     f(1); 
    } 
}; 

int main(int argc,char *argv[]) 
{ 
    vector<KeyCallback> v; 

    v.push_back(KeyCallback(func1)); 
    v.push_back(KeyCallback(func1)); 
    v.push_back(KeyCallback(func1)); 

    v.push_back(KeyCallback(func2(1))); 
    v.push_back(KeyCallback(func2(1))); 

    v.push_back(KeyCallback(func2(2))); 
    v.push_back(KeyCallback(func2(2))); 
    v.push_back(KeyCallback(func2(2))); 
    v.push_back(KeyCallback(func2(2))); 

    v.push_back(KeyCallback(func3)); 

    for_each(v.begin(),v.end(),Caller()); 

    cout << count(v.begin(),v.end(),KeyCallback(func1)) << endl; 
    cout << count(v.begin(),v.end(),KeyCallback(func2(1))) << endl; 
    cout << count(v.begin(),v.end(),KeyCallback(func2(2))) << endl; 
    cout << count(v.begin(),v.end(),KeyCallback(func3)) << endl; 
    return 0; 
} 

輸出是:

func1 
func1 
func1 
func2, data=1 
func2, data=1 
func2, data=2 
func2, data=2 
func2, data=2 
func2, data=2 
func3 
3 
2 
4 
1 

優點

  • 我們註冊/註銷回調使用相同的類型。用戶可以將他的函數和函子保存到KeyCallback之外 - 並將KeyCallback傳遞給我們的unregister_callback。
  • 有升壓沒有依賴::功能

缺點

  • 函子對象必須有定義的比較,即使它不與unregister_callback
  • 使用。如果用戶已經有回調包裝到boost :: function - 它不能轉換爲我們的KeyCallback,因爲它需要定義的比較。
  • 如果你在其他地方需要類似的功能,使用不同類型的回調 - 那麼我們的boost ::函數類應該被改進(採用不同的參數等等),或者我們可以提取和修改boost ::功能本身。


方法3:

這裏我們創建這是從STD /升壓繼承新類::功能

LIVE DEMO

#include <type_traits> 
#include <functional> 
#include <algorithm> 
#include <stdexcept> 
#include <iostream> 
#include <typeinfo> 
#include <utility> 
#include <ostream> 
#include <vector> 
#include <string> 

using namespace std; 

// _____________________________Implementation__________________________________________ 

#define USE_VARIADIC_TEMPLATES 0 

template<typename Callback,typename Function> 
bool func_compare(const Function &lhs,const Function &rhs) 
{ 
    typedef typename conditional<is_function<Callback>::value,typename add_pointer<Callback>::type,Callback>::type request_type; 
    if (const request_type* lhs_internal = lhs.template target<request_type>()) 
     if (const request_type* rhs_internal = rhs.template target<request_type>()) 
      return *rhs_internal == *lhs_internal; 
    return false; 
} 

#if USE_VARIADIC_TEMPLATES 
    #define FUNC_SIG_TYPES typename ...Args 
    #define FUNC_SIG_TYPES_PASS Args... 
#else 
    #define FUNC_SIG_TYPES typename function_signature 
    #define FUNC_SIG_TYPES_PASS function_signature 
#endif 

template<FUNC_SIG_TYPES> 
struct function_comparable: function<FUNC_SIG_TYPES_PASS> 
{ 
    typedef function<FUNC_SIG_TYPES_PASS> Function; 
    bool (*type_holder)(const Function &,const Function &); 
public: 
    function_comparable(){} 
    template<typename Func> 
    function_comparable(Func f_) 
     : Function(f_), type_holder(func_compare<Func,Function>) 
    { 
    } 
    template<typename Func> 
    function_comparable &operator=(Func f_) 
    { 
     Function::operator=(f_); 
     type_holder=func_compare<Func,Function>; 
     return *this; 
    } 
    friend bool operator==(const Function &lhs,const function_comparable &rhs) 
    { 
     return rhs.type_holder(lhs,rhs); 
    } 
    friend bool operator==(const function_comparable &lhs,const Function &rhs) 
    { 
     return rhs==lhs; 
    } 
    friend void swap(function_comparable &lhs,function_comparable &rhs)// noexcept 
    { 
     lhs.swap(rhs); 
     lhs.type_holder.swap(rhs.type_holder); 
    } 
}; 

// ________________________________Example______________________________________________ 

typedef void (function_signature)(); 

void func1() 
{ 
    cout << "func1" << endl; 
} 

void func3() 
{ 
    cout << "func3" << endl; 
} 

class func2 
{ 
    int data; 
public: 
    explicit func2(int n) : data(n) {} 
    friend bool operator==(const func2 &lhs,const func2 &rhs) 
    { 
     return lhs.data==rhs.data; 
    } 
    void operator()() 
    { 
     cout << "func2, data=" << data << endl; 
    } 
}; 
struct Caller 
{ 
    template<typename Func> 
    void operator()(Func f) 
    { 
     f(); 
    } 
}; 
class Callbacks 
{ 
    vector<function<function_signature>> v; 
public: 
    void register_callback_comparator(function_comparable<function_signature> callback) 
    { 
     v.push_back(callback); 
    } 
    void register_callback(function<function_signature> callback) 
    { 
     v.push_back(callback); 
    } 
    void unregister_callback(function_comparable<function_signature> callback) 
    { 
     auto it=find(v.begin(),v.end(),callback); 
     if(it!=v.end()) 
      v.erase(it); 
     else 
      throw runtime_error("not found"); 
    } 
    void call_all() 
    { 
     for_each(v.begin(),v.end(),Caller()); 
     cout << string(16,'_') << endl; 
    } 
}; 

int main() 
{ 
    Callbacks cb; 
    function_comparable<function_signature> f; 
    f=func1; 
    cb.register_callback_comparator(f); 

    cb.register_callback(func2(1)); 
    cb.register_callback(func2(2)); 
    cb.register_callback(func3); 
    cb.call_all(); 

    cb.unregister_callback(func2(2)); 
    cb.call_all(); 
    cb.unregister_callback(func1); 
    cb.call_all(); 
} 

輸出是:

func1 
func2, data=1 
func2, data=2 
func3 
________________ 
func1 
func2, data=1 
func3 
________________ 
func2, data=1 
func3 
________________ 

優點

  • 我們可以使用註冊/註銷回調同一類型。用戶可以將他的函數和函子保存到KeyCallback之外 - 並將KeyCallback傳遞給我們的unregister_callback。在這個版本中我們可以使用簡單的boost :: function作爲註冊函數的參數。
  • 我們仍然可以使用boost ::功能在註冊和矢量
  • 存儲當我們使用boost :: function的註冊,只有當需要通過它來unregister_callback仿函數對象應該定義的比較。
  • 它是通用的 - 因此,可以很容易地在其他地方使用,用於其他類型的回調。
  • 此版本基於普通函數指針而不是分配+抽象類(vptr)。所以它少了一個錯誤,更容易管理。

缺點

  • 如果用戶已經有了回調包裹提振::功能 - 它不能與unregister_callback使用,因爲它需要的東西,可以比較的boost ::功能(例如函數指針,或具有定義的比較算符)


編輯:

真棒,我想它#1現在,但我不明白爲什麼當我們用自己的==操作符它的工作原理?

的boost ::功能can be compared對函數或仿函數,而不是針對其他的boost ::功能:

#include <boost/function.hpp> 

void f1(){} 
void f2(){} 

int main() 
{ 
    boost::function<void()> bf1(f1),bf2(f2); 
    bf1 == f1; // Works OK 
    //bf1 == bf2; - COMPILE ERROR 
    return 0; 
} 

在我們的#1的方法,我們做類似的比較 「BF1 == F1」; 。 KeyCallbackChecker捕獲仿函數/函數,並在ConcreteCallback :: equals中執行此類比較。

+0

真棒,我現在正在嘗試它#1,但我不太明白爲什麼它適用於我們自己的==運營商? – KaiserJohaan

+0

我已向底部添加了答案。我還補充說明了兩種方法的優缺點。 –

+0

謝謝,我現在明白了!如果可以的話,我會再次高舉! – KaiserJohaan

0

我不確定是否正確理解您的問題。我的理解是,你有一個向量KeyCallback函數對象。他們每個人都有相同的簽名void (const KeyEvent &)。通常情況下,你可以打電話給他們每個人一個KeyEvent對象。並且您想要從矢量中刪除一個或多個這些對象。

如果我說得對,那麼實際問題是,您如何識別某個KeyCallback對象?這裏有一個解決方案:使用指針。

如果一個KeyCallback對象是一個函數指針,那麼沒有兩個函數可以具有相同的地址。如果它是一個帶有operator()的類對象,那麼每個具體對象都有一個唯一的地址。因此,下面的代碼工作。但它不再使用矢量,而是使用map。而且您需要使用for循環而不是for_each

#include <iostream> 
#include <map> 
#include <boost/function.hpp> 

typedef int KeyEvent; 
typedef boost::function<void (const KeyEvent &)> KeyCallback; 

void func1 (const KeyEvent &x) 
{ 
    std::cout << "Call func1 with " << x << std::endl; 
} 

void func2 (const KeyEvent &x) 
{ 
    std::cout << "Call func2 with " << x << std::endl; 
} 

class func3 
{ 
    public : 

    void operator() (const KeyEvent &x) const 
    { 
     std::cout << "Call func3 with " << x << std::endl; 
    } 
}; 

class func4 
{ 
    public : 

    func4() : data_(0) {} 
    func4 (int d) : data_(d) {} 

    void operator() (const KeyEvent &x) const 
    { 
     std::cout << "Call func4(" << data_ << ") with " << x << std::endl; 
    } 

    private : 

    int data_; 
}; 

template <typename T> 
long ptol (T *p) 
{ 
    void *vp = (void *) p; 
    return reinterpret_cast<long>(vp); 
} 

int main() 
{ 
    func3 f30; 
    func4 f40; 
    func4 f41(1); 
    func4 f42(2); 
    std::map<long, KeyCallback> callback; 
    callback[ptol(&func1)] = func1; 
    callback[ptol(&func2)] = func2; 
    callback[ptol(&f30)] = f30; 
    callback[ptol(&f40)] = f40; 
    callback[ptol(&f41)] = f41; 
    callback[ptol(&f42)] = f42; 

    for (std::map<long, KeyCallback>::const_iterator m = callback.begin(); m != callback.end(); ++m) 
     m->second(1); 

    std::cout << "ERASE func1 and f41" << std::endl; 

    callback.erase(ptol(&func1)); 
    callback.erase(ptol(&f41)); 

    for (std::map<long, KeyCallback>::const_iterator m = callback.begin(); m != callback.end(); ++m) 
     m->second(1); 

    return 0; 
} 

這裏是輸出

Call func1 with 1 
Call func2 with 1 
Call func4(2) with 1 
Call func4(1) with 1 
Call func4(0) with 1 
Call func3 with 1 
ERASE func1 and f41 
Call func2 with 1 
Call func4(2) with 1 
Call func4(0) with 1 
Call func3 with 1 

缺點是

  • 您無法通過一個特定的順序給他們打電話。
  • f40,...,f42之類的對象必須在插入之外創建。 callback.insert(std :: make_pair(address,func4(5)));你不能使用insert來動態地將函數對象插入到map中,例如 。 ,除非您自己分配和管理唯一的address,因爲您無法獲取臨時對象的地址。
  • 本例的工作是所有f40f42都有唯一的地址。當我們添加它們時它們都存在,所以它們不能有重複的地址。但是如果說我動態地創建了f42,那麼添加它,delete吧,再創建func4對象,然後添加它。新的func4對象可能使用舊的f42的內存位置,因此該地址不再是唯一的,新的插入將替換新的插入。

不過,我認爲你可以想出一個比他們的地址更好的每個回調的唯一鍵。

+0

boost :: function的主要屬性是類型擦除。他的代碼只是接收boost :: function,並沒有關於funciton/functor本身的任何信息。只要看看UnregisterCallback簽名:void UnregisterCallback(KeyCallback callback)。你打算如何在UnregisterCallback的論點上調用ptol? –

相關問題