17

Say want to store the following:

typedef std::function<void(int)> MyFunctionDecl;

..in a collection:

typedef std::vector<MyFunctionDecl> FunctionVector;
FunctionVector v;

This is possible, but if I want to find something using std::find:

FunctionVector::const_iterator cit = std::find(v.begin(), v.end(), myFunctionDecl);

.. we get an error due to the == operator.

As has been suggested to me in a previous question concerning this, this can be gotten around by encapsulating the function declaration within another class, which provides a == operator:

class Wrapper
{
private:
    MyFunctionDecl m_Func;

public:
    // ctor omitted for brevity

    bool operator == (const Wrapper& _rhs)
    {
         // are they equal?
    }; // eo ==
};  // eo class Wrapper

So what I want to do is somehow generate a hash for "MyFunctionDecl" so that I can properly implement the == operator. I could have some kind of unique identifier, and ask the caller to provide a unique identifier for the delegate, but this seems a bit of a pain and is error prone.

Is there a way that I can do this? So that the same functions will return the same ID for comparative purposes? So far, the only way around it is to dump the notion of using std::function and go back to using fast delegates which support comparisons. But then I lose the ability to use lambdas.

Any help appreciated!

EDIT

Given the answer below, this is what I have come up with... any caveats I might have missed? I'm in the process of putting it through it's paces now:

class MORSE_API Event : boost::noncopyable
{
public:
    typedef std::function<void(const EventArgs&)> DelegateType;
    typedef boost::shared_ptr<DelegateType> DelegateDecl;

private:
    typedef std::set<DelegateDecl> DelegateSet;
    typedef DelegateSet::const_iterator DelegateSet_cit;
    DelegateSet m_Delegates;

public:
    Event()
    {
    };  // eo ctor


    Event(Event&& _rhs) : m_Delegates(std::move(_rhs.m_Delegates))
    {
    };  // eo mtor

    ~Event()
    {
    };  // eo dtor

    // methods
    void invoke(const EventArgs& _args)
    {
        std::for_each(m_Delegates.begin(),
                      m_Delegates.end(),
                      [&_args](const DelegateDecl& _decl) { (*_decl)(_args); });
    };  // eo invoke

    DelegateDecl addListener(DelegateType f)
    {
        DelegateDecl ret(new DelegateType(f));
        m_Delegates.insert(ret);
        return ret;
    };  // eo addListener

    void removeListener(const DelegateDecl _decl)
    {
        DelegateSet_cit cit(m_Delegates.find(_decl));
        if(cit != m_Delegates.end())
            m_Delegates.erase(cit);
    };  // eo removeListener

};  // eo class Event
Moo-Juice
  • 38,257
  • 10
  • 78
  • 128
  • 1
    Why do you need to find? If you need to find a std::function like this, you will need to find it based on some identifier or similar, say an automatically generated int for each new Wrapper you create. Get the int for future comparison and make sure that you only create each wrapper once. – villintehaspam Jan 03 '11 at 13:25
  • You cannot compare functions, this is the *halting problem* (http://en.wikipedia.org/wiki/Halting_problem ) – Alexandre C. Jan 03 '11 at 13:33
  • 2
    @Alexandre I think the halting problem is related but definitely not the same. I would think that the idea of the hash is that you don't need to run the function to compare it. – San Jacinto Jan 03 '11 at 13:40
  • 1
    @villintehaspam, Adding and iterating is not a problem. However, want to create an event-type class that allows multiple delegates to be stored and, if they so desire, to remove themselves also. Think of `addListener` and `removeListener` as the interface to this kind of thing. – Moo-Juice Jan 03 '11 at 13:46
  • 2
    @Moo-Juice, in that case you are never going to generate new wrappers for the same function again and so using ints like in my comment above will work great. Just make addListener return the unique identifier and let removeListener take that as an argument. – villintehaspam Jan 03 '11 at 13:50
  • Related: [Comparing std::tr1::function<> objects](http://stackoverflow.com/q/89488/20984) – Luc Touraille Jan 03 '11 at 13:51
  • Why do you need to find function that you already have? o_O – Pawel Zubrycki Jan 03 '11 at 14:04
  • So you want to tag each function in a collection? Why not to use a std::map for that instead of std::vector then? – Gene Bushuyev Jan 03 '11 at 16:10
  • I think you should look at the boost signals library... which I believe alrdy implements what you are trying to do. – ronag Jan 03 '11 at 17:29

2 Answers2

7

Have you looked at Boost Signals? It may already be doing what you want to do.

Anyway, a simple way of wrapping the function would be to use a shared_ptr. If you do

typedef std::shared_ptr<std::function<void(int)> > MyFunctionDecl;

and make sure that the function is wrapped immediately inside the shared_ptr when you create it (so that the pointer is unique), pointers can be tested for equality so std::find would work.

For example you can do so with a factory function like

template <class Functor>
MyFunctionDecl createDelegate(Functor f) {
    return MyFunctionDecl(new std::function<void(int)>(f));
}

This way you give an unique identity to the function (its pointer) when you create the delegate.

BTW, I'd use an std::set instead of an std::vector, as both find and erase are logarithmic rather than linear.

Giuseppe Ottaviano
  • 4,533
  • 2
  • 18
  • 18
  • thanks for this. I have created an implementation given your idea and am in the process of checking it out now. Have amended my original post to show current code. Thanks! – Moo-Juice Jan 03 '11 at 15:08
-1
#include <boost/type_traits.hpp>
#include <iostream>

template<typename T>
class Wrapper
{
    private:
       T m_Func;

    public:

       template<typename U>
       bool operator==(const Wrapper<U>& _rhs)
       {
           return boost::is_same<T, U>::value;
       }
};

int main()
{
    Wrapper<int> Wint;
    Wrapper<bool> Wbool;
    std::cout << (Wint == Wbool) << std::endl;
    std::cout << (Wint == Wint) << std::endl;
}