Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++11 : Compare lambda expression

Imagine have the following class :

#include <functional>
#include <vector>

template<typename T1> class Signaler
{
public:
  typedef std::function<void (T1)> Func;

public:
  Signaler()
  {
  }

  void Call(T1 arg)
  {
    for(Int32 i = (Int32)_handlers.size() - 1; i > -1; i--)
    {
      Func handler = _handlers[i];
      handler(arg);
    }
  }

  Signaler& operator+=(Func f)
  {
    _handlers.push_back( f );
    return *this;
  }

  Signaler& operator-=(Func f)
  {
    for(auto i = _handlers.begin(); i != _handlers.end(); i++)
    {
      if ( (*i).template target<void (T1)>() == f.template target<void (T1)>() )
      {
        _handlers.erase( i );
        break;
      }
    }

    return *this;
  }

private:
  std::vector<Func> _handlers;
};

And I use it the following way :

Signaler Global::Signal_SelectionChanged;

class C1
{
public:
    void Register()
    {
        Global::Signal_SelectionChanged   += [&](SelectionChangedEventArgs* e)  { this->selectionChangedEvent_cb(e); };
    }

    void Unregister()
    {
        Global::Signal_SelectionChanged   -= [&](SelectionChangedEventArgs* e)  { this->selectionChangedEvent_cb(e); };
    }

    void selectionChangedEvent_cb(SelectionChangedEventArgs* e) {}
};

class C2
{
public:
    void Register()
    {
        Global::Signal_SelectionChanged   += [&](SelectionChangedEventArgs* e)  { this->selectionChangedEvent_cb(e); };
    }

    void Unregister()
    {
        Global::Signal_SelectionChanged   -= [&](SelectionChangedEventArgs* e)  { this->selectionChangedEvent_cb(e); };
    }

    void selectionChangedEvent_cb(SelectionChangedEventArgs* e) {}
};

Now, the problem that I have is when I call 'Unregister' from the class C2, it removes the wrong version of the 'lambda" expression, because the 'lambda' looks similar.

How can I solve this problem ?

Any idea ?

Thanks

like image 218
Spectral Avatar asked Aug 02 '26 18:08

Spectral


1 Answers

The problem is that you are using std::function::target with a type that is not the type of the object stored in the std::function, so it is returning a null pointer. That is, you need to know the actual type of the object stored in the std::function to be able to call target.

Even if you were to call target with the lambda closure type used to add the callback, this wouldn't work for two reasons: first, lambda closure types are unique (5.1.2p3) so the += and -= lambdas have different types even if they are syntactically identical; second, the closure type for a lambda-expression is not defined to have an operator== (5.1.2p3-6, 19-20), so your code would not even compile.

Switching from lambdas to std::bind wouldn't help, as bind types are also not defined to have operator==.

Instead, consider using an id to register/unregister callbacks. You could also use your own functor which defines operator==, but that would be a lot of work.

like image 185
ecatmur Avatar answered Aug 05 '26 08:08

ecatmur



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!