[英]Storing boost::function objects in a container
I have a vector of KeyCallback
s: 我有一个KeyCallback
的向量:
typedef boost::function<void (const KeyEvent&)> KeyCallback
which I use to store all listeners for when a keyboard button is pressed. 我用它来存储按下键盘按钮时的所有监听器。 I can add them and dispatch the events to all callbacks with for_each
, but I do not know how to actually erase a specific KeyCallback
signature from my vector. 我可以添加它们并使用for_each
将事件分派给所有回调,但我不知道如何从我的向量中实际删除特定的KeyCallback
签名。
For example I want something like this: 例如,我想要这样的东西:
void InputManager::UnregisterCallback(KeyCallback callback) {
mKeyCallbacks.erase(std::find(mKeyCallbacks.begin(), mKeyCallbacks.end(), callback));
}
According to boost::function
documentation (see here ), there is no such thing as comparing function objects, which would explain my problems with the above. 根据boost::function
文档(见这里 ),没有比较函数对象的东西,这可以解释我上面的问题。 So am I stuck? 我被困了吗? Is there any nice way around this? 这有什么好办法吗?
(I read about boost::signals
for callback mechanisms, but it's apparently quite slow, and I expect callbacks to be fired possibly several times a frame.) (我读到了关于回调机制的boost::signals
,但它显然非常慢,而且我希望回调可能会在几帧内被触发几次。)
http://www.boost.org/doc/libs/1_51_0/doc/html/function/tutorial.html#id1546064 http://www.boost.org/doc/libs/1_51_0/doc/html/function/tutorial.html#id1546064
Function object wrappers can be compared via == or != against any function object that can be stored within the wrapper. 函数对象包装器可以通过==或!=与可以存储在包装器中的任何函数对象进行比较。
So, one of solutions, is to define special type for UnregisterCallback's parameter (which also supports type erasure). 因此,其中一个解决方案是为UnregisterCallback的参数定义特殊类型(它也支持类型擦除)。 That is based on fact, that you can compare boost::function with functor/function - as the result you still will have vector of boost::function, new type is required only for places where you need to perform comparison, eg UnregisterCallback: 这是基于事实,你可以比较boost :: function和functor / function - 结果你仍然会有boost :: function的向量,只有你需要执行比较的地方才需要新类型,例如UnregisterCallback:
#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;
}
Output is: 输出是:
func1
func2, data=1
func2, data=2
func3
________________
func1
func2, data=1
func3
________________
func2, data=1
func3
________________
Pros : 优点 :
Cons : 缺点 :
Another approach is to implement custom boost::function-like solution, which accepts comparable-only callbacks. 另一种方法是实现自定义的boost :: function-like解决方案,它接受可比较的回调。
#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;
}
Output is: 输出是:
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
Pros : 优点 :
Cons : 缺点 :
Here we are creating new class which is inherited from std/boost ::function 这里我们创建一个继承自std / boost :: function的新类
#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();
}
Output is: 输出是:
func1
func2, data=1
func2, data=2
func3
________________
func1
func2, data=1
func3
________________
func2, data=1
func3
________________
Pros : 优点 :
Cons : 缺点 :
EDIT: 编辑:
Awesome, I'm trying it out #1 right now, but I do not quite understand why it works when we apply our own == operators? 太棒了,我现在正在尝试#1,但我不太明白为什么它应用我们自己的==运算符时有效?
boost::function can be compared against functions or functors, but not against another boost::function: boost :: function 可以与函数或函子进行比较 ,但不能与另一个boost :: function进行比较:
#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;
}
In our #1 approach, we do comparison similar to "bf1 == f1;". 在我们的#1方法中,我们进行类似于“bf1 == f1;”的比较。 KeyCallbackChecker captures functor/function and performs such kind of comparison inside ConcreteCallback::equals. KeyCallbackChecker捕获函子/函数并在ConcreteCallback :: equals中执行此类比较。
I am not sure if I understand your problem correctly. 我不确定我是否正确理解你的问题。 My understanding is that you have a vector of KeyCallback
function objects. 我的理解是你有一个KeyCallback
函数对象的向量。 Each of them has the same signature void (const KeyEvent &)
. 它们中的每一个都具有相同的签名void (const KeyEvent &)
。 Normally you call each of them for a certain KeyEvent
object. 通常,您为每个KeyEvent
对象调用它们。 And you want to delete one or more of these objects form the vector. 并且您希望从向量中删除这些对象中的一个或多个。
If I am right, then the actual problem is that how do you identify a certain KeyCallback
object? 如果我是对的,那么实际的问题是你如何识别某个KeyCallback
对象? Here is one solution: use pointer. 这是一个解决方案:使用指针。
If a KeyCallback
object is a function pointer, then no two function can have the same address. 如果KeyCallback
对象是函数指针,那么没有两个函数可以具有相同的地址。 If it is a class object with operator()
, then each concrete object has a unique address. 如果它是一个带有operator()
的类对象,那么每个具体对象都有一个唯一的地址。 Therefore the following code works. 因此,以下代码有效。 But it no longer use a vector but a map
. 但它不再使用矢量而是使用map
。 And you need to use a for
loop instead of for_each
. 而且你需要使用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;
}
And here is output 这是输出
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
The downside is 缺点是
f40
,..., f42
has to be created outside the insertion. 必须在插入之外创建像f40
,..., f42
这样的对象。 You cannot use insert
to insert a function object into the map
on the fly, like, callback.insert(std::make_pair(address, func4(5))); 您不能使用insert
将函数对象动态插入到map
,例如,callback.insert(std :: make_pair(address,func4(5))); unless you assign and manage a unique address
yourself, since you cannot take address of an temporary object. 除非您自己分配和管理唯一address
,否则您无法获取临时对象的地址。 f40
to f42
have unique address. 这个示例工作是所有f40
到f42
都有唯一的地址。 They all exist when we add them, so they cannot have duplicate address. 当我们添加它们时它们都存在,因此它们不能具有重复的地址。 But if say I dynamically created f42
, add it, delete
it, create another func4
object, then add it. 但是如果说我动态创建f42
,添加它, delete
它,创建另一个func4
对象,然后添加它。 It is possible that the new func4
object used the old f42
's memory location, and therefore the address is no longer unique and the new insertion will replace the new one. 新的func4
对象可能使用旧的f42
的内存位置,因此地址不再是唯一的,新插入将替换新的插入。 However I think you can come up with a better unique key for each call back than their addresses. 但是我认为你可以为每个回调提供一个比他们的地址更好的唯一密钥。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.