2012-12-01 151 views
1

存儲函數指針在void指針向量中具有不同參數。將void *轉換爲std :: function <void()>

unordered_map<string, vector<void*> > List; 

template <typename T> 
void Listen(string Name, function<void(T)> Function) 
{ 
    List[Name].push_back(&Function); 
} 

然後我想打電話給他們,假設T是同類型Fire作爲用於Listen

template <typename T> 
void Fire(string Name, T Data) 
{ 
    auto Functions = List[Name]; 

    for (auto i = Functions.begin(); i != Functions.end(); ++i) 
    { 
     (function<void(T)>)i)(Data); 
    } 
} 

但我得到一個編譯器錯誤,其內容爲error C2064: term does not evaluate to a function taking 1 arguments in file ...\vc\include\xrefwrap 431 1

我在做什麼錯?

+0

您正在推送參數的地址。 – chill

+1

爲什麼不把'std :: function'保存在'List'中? – Lol4t0

+0

@ Lol4t0。可悲的是我無法在同一個列表中保存不同的類型。例如'function ','function '和'function '都是不同的類型。 – danijar

回答

2

首先,你正在做的地址參數,在這裏:

List[Name].push_back(&Function); 

然後你要一個iterator對象轉換爲std::function對象的位置:

(function<void(T)>)i) 

什麼你試圖做的事情可以這樣做,雖然它不是很漂亮,但可以輕描淡寫:

unordered_map<string, vector<void*> > List; 

template <typename T> 
void Listen(string Name, function<void(T)> &Function) 
{ 
    List[Name].push_back(&Function); 
} 

template <typename T> 
void Fire(string Name, T Data) 
{ 
    auto Functions = List[Name]; 

    for (auto i = Functions.begin(); i != Functions.end(); ++i) 
    { 
     function<void(T)> *ptr = *i; 

     (*ptr) (Data); 
    } 
} 

它可以在很多方面突破,例如,你無法控制的功能,在Listen在某些名義登記被稱爲與Fire正確的說法 - 考慮調用Listen<int> ("foo", f);,然後做Fire<double> ("foo", 3.14);

另一種方法 - 只是通過回調封:

unordered_map<string, std::vector<function<void()> > > List; 

void Listen(string Name, function<void()> Function) 
{ 
    List[Name].push_back(Function); 
} 

void Fire(string Name) 
{ 
    auto Functions = List[Name]; 

    for (auto i = Functions.begin(); i != Functions.end(); ++i) 
     (*i)(); 
} 
+0

爲什麼在'Listen'函數的參數上使用'&'?我能以某種方式避免嗎? – danijar

+0

這是確保函數被左值調用的函數,即具有地址的東西。仍然必須確保地址保持有效,只要它在矢量/地圖中。 – chill

1
#include <functional> 
#include <unordered_map> 
#include <memory> 
#include <string> 
#include <vector> 

template<typename T> struct BlockDeduction{typedef T type;}; 
struct BaseCallback { 
    virtual ~BaseCallback(); 
    template<typename T> 
    void DoCall(typename BlockDeduction<T>::type&& t) const; 
}; 
template<typename T> 
struct Callback: BaseCallback 
{ 
    std::function<void(T)> func; 
    Callback(std::function<void(T)> const& f):func(f) {} 
}; 


template<typename T> 
void BaseCallback::DoCall(typename BlockDeduction<T>::type&& t) const { 
    Assert(dynamic_cast<Callback<T>const*>(this)); 
    static_cast<Callback<T>const*>(this).func(std::forward(t)); 
} 

typedef std::unique_ptr<BaseCallback> upCallback; 
template<typename T> 
upCallback make_callback(std::function<void(T)> const& f) { 
    return upCallback(new Callback<T>(f)); 
} 


struct Listener { 
    std::unordered_map< std::string, std::vector<upCallback>> List; 
    template<typename T> 
    void Listen(std::string Name, std::function<void(T)> f) { 
    List[Name].push_back(make_callback(f)); 
    } 
    template<typename T> 
    void Fire(std::string Name, typename BlockDeduction<T>::type&& t) { 
    auto callbacks = List.find(Name); 
    if (callbacks == List.end()) return; 
    for(auto it = callbacks->second.begin(); it != callbacks->second.end(); ++it) { 
     if (it +1 = callbacks->second.end()) 
     { 
     (**it).DoCall<T>(std::forward(t)); 
     } else { 
     (**it).DoCall<T>(t); 
     } 
    } 
    } 
}; 

...或者類似的東西。

這將std::function的副本存儲在地圖中,一般包裹起來。內存通過unique_ptr處理。我仔細地阻止了在類型必須與您在安裝Listener時使用的點類型(當時的自動類型演繹相當脆弱)的類型演繹。

在調試中,如果違反名稱< - >類型映射,則會出現斷言失敗。

一些額外的工作需要完成無限的回調。只要寫一個DoCall是蒙上BaseCallbackCallback<void>,專門Callback<void>是一個無參function包裝上無參function專門make_callback,譜寫Fire(string)方法Listener調用裸DoCall

或者創建一個struct Empty並使用lambdas在function<void(Empty)>中包含空函數,這會涉及少量的代碼,但在運行時會較慢。

+0

我喜歡那樣,但我需要一些時間才能真正理解你所做的一切。 – danijar

+0

請注意,'std :: forward'廢話可能是錯誤的 - 我仍然試圖去讚美它。我可能不應該包含它,並且只做了一些'T'類型的冗餘副本。 'BlockDeduction'僅用於阻止自動函數類型推演(因爲'Fire'採取的'T'必須與'T'' Listen'完全相同的類型,或未定義的行爲結果)。 'BaseCallback'確實存在以便擁有一個虛擬dtor(所以它的子類dtor被調用),'DoCall'方法只是將危險的'static_cast'放入類中。 'unique_ptr'管理生命週期。 – Yakk

相關問題