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
1) std::vector is a sequence container that encapsulates dynamic size arrays. 2) std::pmr::vector is an alias template that uses a polymorphic allocator. The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets to regular pointers to elements.
A vector is a sequence container class that implements dynamic array, means size automatically changes when appending elements. A vector stores the elements in contiguous memory locations and allocates the memory as needed at run time.
To create a vector in C++, you first have to include the vector library. You do this by adding the line #include <vector> at the top of your file. This line goes after the line #include <iostream> and any other header files you've included in your program. The std::vector is included in the #include <vector> library.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With