Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

remove any element of vector<std::function<...>> that bound to member function

how to remove function that bound to member function of this object :

std::vector<std::function<void(int)>> callbacks;

class MyClass {
public:
    MyClass() {
        callbacks.push_back(
            std::bind(&MyClass::myFunc,this,std::placeholders::_1)
        );
    }
    ~MyClass() {
        auto it = std::remove_if( std::begin(callbacks),
                                  std::end(callbacks),
                                  [&](std::function<void(int)>& f) {
                return // <-- this is my question
                       //     true (remove) if f is bound to member function 
                       //     of this
        });
        callbacks.erase(it,std::end(callbacks));
    }
    void myFunc(int param){...}
};
like image 244
uray Avatar asked Feb 21 '23 01:02

uray


1 Answers

    typedef decltype(std::bind(&MyClass::myFunc,this,std::placeholders::_1)) bound_type;

    auto it = std::remove_if( std::begin(callbacks),
                              std::end(callbacks),
                              [](const std::function<void(int)>& f) {
          return f.target<bound_type>() != nullptr;
    });

The member function template std::function::target<T> returns a pointer to the target object if it is of type T, otherwise it returns null. So you just need to be able to name the type of the target object, which you can get from decltype. Pretty simple really :-)

N.B. that will remove any callbacks of that type, not only ones that have bound the this pointer for the specific object being destroyed. If you are trying to prevent invoking callbacks on an object after it has been destroyed and have no possible way to identify which elements of the vector refer to which objects, you could consider putting a shared_ptr in your class, then storing a weak_ptr to it in the callback, which can be used to detect if the object has been destroyed:

class MyClass
{
    struct NullDeleter { void operator()(void*) const { } };
    std::shared_ptr<MyClass> sp;

    static void safe_invoke(void (MyClass::*f)(int), const std::weak_ptr<MyClass>& wp, int i)
    {
        if (std::shared_ptr<MyClass> safe_this = wp.lock())
            (safe_this.get()->*f)(i);
    }

public:
    MyClass() : sp(this, NullDeleter()) {
        callbacks.push_back(
            std::bind(safe_invoke, &MyClass::myFunc ,std::weak_ptr<MyClass>(sp),
                      std::placeholders::_1)
        );
    };

This wraps the call to the member function with the invoke function that converts the weak_ptr to a shared_ptr before calling the member function. If the object has been destroyed the shared_ptr will be empty, so the function does nothing. This doesn't actually remove the callback when it becomes invalid, but does make it safe to call.

like image 192
Jonathan Wakely Avatar answered Apr 28 '23 15:04

Jonathan Wakely