【问题标题】:Comparing function pointers stored as boost::function比较存储为 boost::function 的函数指针
【发布时间】:2013-05-23 15:24:29
【问题描述】:

我有一个 boost::function 对象的列表,我正在尝试找到一个特定的对象,以便将其从列表中删除。实际上注册了一个函数(推送到一个向量上),我希望能够取消注册它(搜索向量并删除匹配的函数指针)。代码如下:

#include <string>
#include <vector>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>

class DummyClass
{
public:
    std::string Data;
};
typedef boost::shared_ptr<DummyClass> DummyClassPtrType;

class UpdaterClass
{
public:
    void handle(DummyClassPtrType Dummy);
};

class ManagerClass
{
public:
    typedef boost::function<void (DummyClassPtrType Dummy)> HandlerFunctionType;
    typedef std::vector<HandlerFunctionType> HandlerFunctionListType;
    //
    HandlerFunctionListType HandlerFunctionList;
    void registerHandler(HandlerFunctionType Handler)
    {
        HandlerFunctionList.push_back(Handler);
    }
    void unRegister(HandlerFunctionType Handler)
    {
        // find the function pointer in the list and delete it from the list if found
        HandlerFunctionListType::iterator HandlerIter = HandlerFunctionList.begin();
        while (HandlerIter != HandlerFunctionList.end())
        {
            if (*HandlerIter == Handler) // error C2666: 'boost::operator ==' : 4 overloads have similar conversions
            {
                HandlerIter = HandlerFunctionList.erase(HandlerIter);
                break;
            }
            else
            {
                ++HandlerIter;
            }
        }
    }
};

int main()
{
    ManagerClass Manager;
    UpdaterClass Updater;
    Manager.registerHandler(boost::bind(&UpdaterClass::handle, &Updater, _1));
    Manager.unRegister(boost::bind(&UpdaterClass::handle, &Updater, _1));
    return 0;
}

编译器(VS2008 SP1)不喜欢这行:

if (*HandlerIter == Handler)

我不知道如何实现这一点。

【问题讨论】:

标签: c++ boost boost-function


【解决方案1】:

除了 Yakk 的回答之外,实现这一点的另一种常用方法是为容器中的项目保留一个迭代器(这个迭代器作用于 Yakk 所说的“令牌”)。

由于您可能会在删除特定项目之前删除和添加其他项目,因此您必须选择一个不会在插入/删除时使其迭代器无效的容器。 std::vector 显然不适合这个,但std::list 是。

您的registerHandler 函数只需返回由std::list::insert 返回的迭代器,而unregisterHandler 只需调用HandlerFunctionList.erase(iteratorToken);

这个实现的唯一缺点是,与 Yakk 不同,它不使用字典来存储令牌,因此它无法事先检查令牌的有效性,如果用户传递了无效的迭代器,事情就会出错给你的unregisterHandler

不过,好处是提高了性能,因为它完全避免了中间字典。

选择你的毒药。

【讨论】:

  • 缺点是你使用了std::list。如果对列表中函数的调用次数超过列表中函数的数量,则您无法通过std::list 的性能测试,即您迭代它的次数少于从中间删除的次数。我敢打赌,执行线性搜索和删除的std::vector&lt; std::unique_ptr&lt;char&gt;, function &gt;std::unique_ptr&lt;char&gt;::get() 充当我们的令牌)会比使用std::list 执行得更快,开销更少,但它会更复杂。
  • @Yakk:迭代list 真的那么慢吗?我可以理解缓存垃圾对成千上万个项目来说是一个问题,但在这个用例中我怀疑它是否重要。
  • @Yakk:我刚刚做了一个快速基准测试:对于 1000 个项目,迭代 listvector 慢大约 40%。对于 100 个项目,它下降到大约 30%。 50 件:25%。所以你确实是对的,我的错。
  • 遍历一个列表时,你基本上是在随机跳转内存。但是,我确实赞扬您找到了 std::list 的边缘用例!他们很难找到。 :)
【解决方案2】:

注册回调时,构建令牌(我通常使用 guid 或 int)并将它们返回给调用者。

想要删除回调的调用者必须使用该令牌来发出请求。

这使得注册同一个函数指针两次成为可能,具有不同的身份,除其他外。

如果您使用 64 位整数并且只是盲目地递增每个令牌,并且您每帧注册 100 万个函数,并且您以每秒 1000 帧的速度运行,并且您的代码运行了 10 万年,那么回绕不会发生。 100万年后,它会。决定是否值得使用完整的 guid 或 int 来搜索差距并回收它们。

另一种选择是使用std::unique_ptr&lt;char&gt;( new char() ) 来滥用堆,并使您的令牌成为void*

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-13
    • 2012-10-05
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2010-12-27
    相关资源
    最近更新 更多