Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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*.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top