1

I have a list of boost::function objects and am trying to find a specific one so I can remove it from the list. Effectively a function is registered (pushed onto a vector) and I wish to be able to unregister it (search the vector and remove the matching function pointer). Here is the code:

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

The compiler (VS2008 SP1) doesn't like the line:

if (*HandlerIter == Handler)

and I can't figure out how to achieve this.

pugdogfan
  • 311
  • 3
  • 10
  • 3
    Why can't I compare boost::function objects with operator== or operator!=? http://www.boost.org/doc/libs/1_50_0/doc/html/function/faq.html#id1565973 – PiotrNycz May 23 '13 at 15:34
  • See related thread http://stackoverflow.com/questions/3629835/why-is-stdfunction-not-equality-comparable – Steven Maitlall May 23 '13 at 15:38
  • 1
    You might consider to use signals: http://www.boost.org/doc/libs/1_50_0/doc/html/signals/tutorial.html#id3161780 – PiotrNycz May 23 '13 at 15:39

2 Answers2

2

In addition to Yakk's answer, another usual way to implement this is to keep an iterator to the item in the container (this iterator acts at the "token" Yakk talks about).

Since you will possibly be deleting and adding other items before you get to delete a particular item, you must choose a container that doesn't invalidate its iterators on insertion/removal. std::vector is obviously not fit for this, but std::list is.

Your registerHandler function will just have to return the iterator returned by std::list::insert, and unregisterHandler will just be a matter of calling HandlerFunctionList.erase(iteratorToken);.

The only downside of this implementation is that, unlike Yakk's, it doesn't use a dictionary to store the tokens so it can't check the validity of the token beforehand and things will go wrong if the user passes an invalid iterator to your unregisterHandler.

However, the upside is increased performance since it avoids the intermediary dictionary altogether.

Choose your poison.

syam
  • 14,701
  • 3
  • 41
  • 65
  • The downside is that you used `std::list`. If the number of calls done to the functions in the list exceeds the number of functions in the list, you fail the performance test for `std::list`, namely that you iterate over it fewer times than you delete from the middle. I'd bet that a `std::vector< std::unique_ptr, function >` (where the `std::unique_ptr::get()` acts as our token) that does a linear-search-and-remove would perform faster with less overhead than using a `std::list`, but it would be more complex. – Yakk - Adam Nevraumont May 24 '13 at 13:20
  • @Yakk: is iterating over a `list` really *that* slow? I can understand cache-trashing would be an issue with thousands or millions of items but in this use-case I doubt it matters. – syam May 24 '13 at 15:18
  • @Yakk: I just did a quick benchmark: for 1000 items, iterating over a `list` is about 40% slower than a `vector`. For 100 items it's down to about 30%. 50 items: 25%. So indeed you are right, my bad. – syam May 24 '13 at 15:28
  • When walking over a list, you are basically randomly jumping through memory. I do commend you on finding something that is even a marginal edge use case for `std::list` however! They are rather hard to find. :) – Yakk - Adam Nevraumont May 24 '13 at 16:01
1

When registering callbacks, build tokens (I usually use guids or int) and return them to the caller.

The caller who wants to remove a callback must use that token to make the request.

This makes it possible to register the same function pointer twice, with distinct identities, among other things.

If you use 64 bit integers and just blinding increment each token, and you register 1 million functions every frame, and you are running at 1000 frames per second, and you leave your code running for 100 thousand years, wrap around will not occur. At 1 million years, it will. Decide if using a full-on guid, or an int whereby you search for gaps and recycle them, is worthwhile.

Another option is to use a std::unique_ptr<char>( new char() ) to abuse the heap, and make your token a void*.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524