std :: function作为回调,可以注销吗? [英] std::function as callback, is unregistration possible?

查看:402
本文介绍了std :: function作为回调,可以注销吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题严格地关于 std :: function ,而不是 boost :: function 。有关更多详细信息,请参阅此问题底部的更新部分,特别是有关不能比较非空的部分 std :: function C ++ 11标准中的对象。






C ++ 11 std ::函数类模板非常适合维护回调的集合。例如,可以将它们存储在向量中,并在需要时调用它们。



让我具体来说,想象这个类:

  class Invoker 
{
public:
void Register(std :: function< void()> f);
void unregister(std :: function< void()> f);

void InvokeAll();

private:
//一些包含传递给Register()的函数对象的容器
};

示例使用场景:

  void foo()
{
}

int main()
{
std :: function< void > f1 {foo};
std :: function< void()> f2 {[] {std :: cout< Hello\\\
;}};

Invoker inv;

//容易的部分

//注册回调
inv.Register(f1);
inv.Register(f2);

//调用它们
inv.InvokeAll();

//看似不可能的部分。如何Unregister()实现实际
//找到正确的对象注销(即,从其容器中删除)?
inv.Unregister(f2);
inv.Unregister(f1);
}

很清楚 Register / code>函数可以实现。但是,如何实现 Unregister()。让我们说,保存函数对象的容器是 vector< std :: function< void()>> 。如何找到传递到 Unregister()调用的特定函数对象? std :: function 提供一个重载的运算符== ,但是只测试一个空的函数对象它不能用于比较两个非空函数对象以查看它们是否都指向相同的实际调用)。



任何想法。



更新:



的一个cookie与每个 std :: function 对象关联,可以用来注销它。我希望对于 std :: function 对象本身不是外生的。此外, std :: function boost :: function 之间似乎有很多混乱。该问题严格地关于 std :: function 对象, boost :: function 对象。



此外,你不能比较两个非空的 std :: function 对象。他们总是比较不相等的标准。所以,在这个问题的上下文中,注释中的链接只是那些(并使用 boost :: function 对象引导)显然是错误的。

解决方案

由于你不能测试容器中的元素标识,最好使用容器(如 std :: list ),当容器被修改时,它们的迭代器不会失效,并返回迭代器返回可以用于注销的注册调用者。



<如果你真的想使用向量(或 deque ),你可以将积分索引返回到向量/ deque当回调被添加时。这个策略自然要求您确保索引以这种方式可用,以识别函数在序列中的位置。如果回调和/或注销是罕见的,这可能仅仅意味着不重用斑点。或者,您可以实现一个空闲列表来重用空槽。



如果你的回调访问模式不支持不需要随机访问遍历,将回调存储在 std :: list 中,并使用原始迭代器取消注册看起来最简单。


The question is strictly about std::function and not boost::function. See the Update section at the bottom of this question for more details, especially the part about it not being possible to compare non-empty std::function objects per the C++11 standard.


The C++11 std::function class template is great for maintaining a collection of callbacks. One can store them in a vector, for example and invoke them when need be. However, maintaining these objects and allowing for unregistration seems to be impossible.

Let me be specific, imagine this class:

class Invoker
{
public:
  void Register(std::function<void()> f);
  void Unregister(std::function<void()> f);

  void InvokeAll();

private:
  // Some container that holds the function objects passed to Register()
};

Sample usage scenario:

void foo()
{
}

int main()
{
  std::function<void()> f1{foo};
  std::function<void()> f2{[] {std::cout << "Hello\n";} };

  Invoker inv;

  // The easy part

  // Register callbacks
  inv.Register(f1);
  inv.Register(f2);

  // Invoke them
  inv.InvokeAll();

  // The seemingly impossible part. How can Unregister() be implemented to actually
  // locate the correct object to unregister (i.e., remove from its container)?
  inv.Unregister(f2);
  inv.Unregister(f1);
}

It is fairly clear how the Register() function can be implemented. However, how would one go about implementing Unregister(). Let's say that the container that holds the function objects is vector<std::function<void()>> . How would you find a particular function object that is passed to the Unregister() call? std::function does supply an overloaded operator==, but that only tests for an empty function object (i.e., it cannot be used to compare two non-empty function objects to see if they both refer to the same actual invocation).

I would appreciate any ideas.

Update:

Ideas so far mainly consist of the addition of a cookie to be associated with each std::function object that can be used to unregister it. I was hoping for something that is not exogenous to the std::function object itself. Also, there seems to be much confusion between std::function and boost::function. The question is strictly about std::function objects, and not boost::function objects.

Also, you cannot compare two non-empty std::function objects for equality. They will always compare non-equal per the standard. So, links in the comments to solutions that do just that (and use boost::function objects to boot) are patently wrong in the context of this question.

解决方案

Since you can't test for element identity in the container, it's probably best to use a container (such as std::list) whose iterators do not invalidate when the container is modified, and return iterators back to registering callers that can be used to unregister.

If you really want to use vector (or deque), you could return the integral index into the vector/deque when the callback is added. This strategy would naturally require you to make sure indexes are usable in this fashion to identify the function's position in the sequence. If callbacks and/or unregistration is rare, this could simply mean not reusing spots. Or, you could implement a free list to reuse empty slots. Or, only reclaim empty slots from the ends of the sequence and maintain a base index offset that is increased when slots are reclaimed off the beginning.

If your callback access pattern doesn't require random access traversal, storing the callbacks in a std::list and using raw iterators to unregister seems simplest to me.

这篇关于std :: function作为回调,可以注销吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆