14

这个问题严格来说是关于std::function而不是boost::function。有关更多详细信息,请参阅此问题底部的更新部分,尤其是关于无法std::function根据 C++11 标准比较非空对象的部分。


C++11std::function类模板非常适合维护回调集合。例如,可以将它们存储在 中vector,并在需要时调用它们。然而,维护这些对象并允许注销似乎是不可能的。

让我具体一点,想象一下这个类:

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()
};

示例使用场景:

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);
}

很清楚如何Register()实现该功能。但是,如何实施Unregister(). 假设保存函数对象的容器是vector<std::function<void()>>. 您将如何找到传递给Unregister()调用的特定函数对象?std::function确实提供了一个重载的operator==,但它只测试一个空函数对象(即,它不能用于比较两个非空函数对象以查看它们是否都引用相同的实际调用)。

我会很感激任何想法。

更新:

到目前为止的想法主要包括添加一个与每个std::function可用于注销它的对象相关联的 cookie。std::function我希望有一些对对象本身来说不是外生的东西。std::function此外,和之间似乎有很多混淆boost::function。这个问题严格来说是关于std::function对象的,而不是 boost::function关于对象的。

此外,您不能比较两个非空std::function对象是否相等。根据标准,他们总是会比较不相等。因此,在此问题的上下文中,评论中指向解决方案(并使用boost::function对象启动)的链接显然是错误的。

4

2 回答 2

13

由于您无法测试容器中的元素身份,因此最好使用一个容器(例如std::list),其迭代器在修改容器时不会失效,并将迭代器返回给可用于注销的注册调用者。

如果你真的想使用vector(or deque),你可以在添加回调时将整数索引返回到向量/双端队列中。这种策略自然会要求您确保以这种方式使用索引来识别函数在序列中的位置。如果回调和/或注销很少见,这可能仅仅意味着不重复使用点。或者,您可以实现一个空闲列表来重用空槽。或者,仅从序列的末尾回收空槽,并保持一个基本索引偏移量,当槽从开头回收时该偏移量会增加。

如果您的回调访问模式不需要随机访问遍历,那么将回调存储在 a 中std::list并使用原始迭代器取消注册对我来说似乎最简单。

于 2013-04-10T19:47:13.537 回答
0

我对此有一个想法。

将回调存储为std::weak_ptr<std::function<void(argtype1, argtype1)>>. 然后调用者负责保持对应的std::shared_ptr存活,调用者取消注册回调所要做的就是将所有活动std::shared_ptr的s销毁到回调函数中。

调用回调时,代码必须小心检查std::weak_ptr<>它正在使用的 s 上的锁定失败。当它遇到这些时,它可以将它们从注册的回调容器中删除。

请注意,这并不能提供完整的线程安全性,因为回调调用者可以锁定std::weak_ptr并临时新激活std::shared_ptr回调函数,该回调函数可以在调用者std::shared_ptr超出范围后保持活动状态。

于 2021-05-21T23:02:01.483 回答