2012-12-01 121 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

相关问题