使用std :: function改变回调 [英] Alterantive for callbacks using std::function
问题描述
目前我正在尝试一个基本上如下的代码:
Currently I am trying out a code that does essentially the following:
void f(int x) { cout << "f("<<x<<")" << endl; }
class C
{
public:
void m(int x) { cout << "C::m("<<x<<")" << endl; }
};
class C2
{
public:
void registerCallback(function<void(int)> f)
{
v.push_back(f);
}
private:
vector<function<void(int)>> v;
void callThem()
{
for (int i=0; i<v.size(); i++)
{
v[i](i);
}
}
};
int main()
{
C2 registrar;
C c;
registrar.registerCallback(&f); // Static function
registrar.registerCallback(bind(&C::m, &c, placeholders::_1)); // Method
return 0;
}
但是我陷入了这种模式。我想检查一个回调是否已经注册,我想通过从向量中删除它可以取消注册一个回调。我刚刚了解到 std :: function
对象不能被比较,这意味着不可能在容器中搜索它们的存在。
This works pretty well. However I got stuck with this pattern. I would like to check if a callback already has been registered and I would like to be able to unregister a callback by removing it from the vector. I just learned that std::function
objects can not be compared which means it is impossible to search for their existence in the container.
所以我需要一个替代。当然,我想保持编译时类型检查和注册任意类的方法的能力。
So I need an alternative. Of course I would like to keep compile-time type checks and the ability to register methods of arbitrary classes.
如何实现一个类似的解决方案,允许注销回调和检查双重注册?
How can I achieve a similar solution that allows unregistering the callback and checking for double registration? Are there any trade-offs I need to accept?
推荐答案
基本的问题是大多数函数对象是不可比的。虽然可以比较具有等式运算符的纯函数指针和用户定义函数对象,但是lambdas, std :: bind()
等的结果不能。使用函数对象的地址来标识它们通常不是合适的方法,因为对象倾向于被复制。 可以使用 std :: reference_wrapper< Fun>
以避免复制它们,但是存储在 std :: function< F>
将仍然具有不同的地址。
The underlying problem is that most function objects are not comparable. While plain function pointers and user-defined function objects with an equality operator can be compared, lambdas, the result of std::bind()
, etc. cannot. Using the address of function objects to identify them is generally not a suitable approach because the objects tend to be copied. It may be possible to use std::reference_wrapper<Fun>
to avoid having them copied but the objects stored in a std::function<F>
will still have a different address.
使用C ++ 11可变参数模板很容易创建 std :: function< ...>
的自定义版本,它提供比较功能。它甚至可以有两个版本:
With C++11 variadic templates it is reasonably easy to create a custom version of std::function<...>
which provides comparison facilities. There may even be two versions thereof:
- 一个版本,它接受任意函数对象,但显然只能比较类似的函数对象:是否使用构造函数参数提供相等运算符或不使用合适的基类。
- 一个版本,总是提供一个工作比较,显然,不能用于非等同可比对象。
后者稍微容易定义,看起来像这样:
The latter is slightly easier to define and would look something like this:
template <typename...> class comparable_function;
template <typename RC, typename... Args>
class comparable_function<RC(Args...)> {
struct base {
virtual ~base() {}
virtual RC call(Args... args) = 0;
virtual base* clone() const = 0;
virtual bool compare(base const*) const = 0;
};
template <typename Fun>
struct concrete: base {
Fun fun;
concrete(Fun const& fun): fun(fun) {}
RC call(Args... args) { return this->fun(args...); }
base* clone() const { return new concrete<Fun>(this->fun); }
bool compare(base const* other) const {
concrete const* o = dynamic_cast<concrete<Fun>>(other);
return o && this->fun == o->fun;
}
};
std::unique_ptr<base> fun;
public:
template <typename Fun>
comparable_function(Fun fun): fun(new concrete<Fun>(fun)) {}
comparable_function(comparable_function const& other): fun(other.fun->clone()) {}
RC operator()(Args... args) { return this->fun->call(args); }
bool operator== (comparable_function const& other) const {
return this->fun->compare(other.fun.get());
}
bool operator!= (comparable_function const& other) { return !(this == other); }
};
我想,我忘了(和/或错误的)一些东西,但这是需要的。对于可选的可比较版本,您将有两个版本的 concrete
:一个如上实现,另一个总是返回 false
。根据在构造函数中是否有 Fun
的运算符 ==
,您可以创建一个或另一个。
I guess, I have forgotten (and/or mistyped) something but rather that's what's needed. For the optionally comparable version you'd have two versions of concrete
: one which is implemented as above and another one which always returns false
. Depending on whether there is an operator ==
for the Fun
in the constructor you'd create one or the other.
这篇关于使用std :: function改变回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!