Search code examples
c++asynchronouslambdaconcurrencycallback

How could I check if "this" pointer of a class is still valid in a callback function?


class myClass
{
public:
    void fun()
    {
         async_do( [this](int j)
                        {
                            i+=j;
                        } );
    }

private:
    int i=9;
}

int main()
{
    std::shared_ptr<myClass> obj = std::make_shared<myClass>();
    obj->fun();
    obj->reset();
    return 0;
}

The code above, when we call fun(), it will call async_do(), async_do()will return immediately. When the async operation is completed, the lambda function will be called. But, while this async operation is still running, the object could be destroyed. When the lambda is called, the object doesn't exist anymore. What should I do in this callback function in this situation?
Thank you for your help.


Solution

  • weak_from_this is useful in this case.

    Use case: when you need to create a callback but caller of the callback doesn't know the lifetime of this and the design allows this destructed before callback gets invoked.

    The difference between shared_from_this() is that weak_from_this() won't keep the instance alive but only try to access it when needed. It is important to note that capturing std::shared_ptr might prolong the lifetime of a instance until callback is released. It might not fit all the use cases.

    Eg: Requires only invoking the most recent instance instead of holding all the instances and do not want to keep other instance.

    Live Demo

    #include <fmt/core.h>
    #include <fmt/format.h>
    #include <functional>
    #include <memory>
    
    using Callback = std::function<void(int)>;
    
    class myClass : public std::enable_shared_from_this<myClass>
    {
    public:
        Callback createWeakCallback()
        {
            return [weak_ref = weak_from_this()](int j) {
                    auto strong_ref = weak_ref.lock();
                    if (nullptr == strong_ref){
                        fmt::print("This is dead.\n");
                        return;
                    }
                    // use `strong_ref` as `this`
                    strong_ref->add(j);
                };
        }
    
        Callback createStrongCallback()
        {
            return [shared = shared_from_this()](int j) {
                    shared->add(j);
                };
        }
    
        myClass(int val): i(val){}
        ~myClass(){
            fmt::print("dtor, i:{}\n", i);
        }
    
        void add(int j){ 
            i+=j;
            fmt::print("i: {}\n", i);
        }
    
    private:
        int i=9;
    };
    
    int main()
    {
        std::shared_ptr<myClass> obj = std::make_shared<myClass>(0);
        Callback cb = obj->createWeakCallback();
        cb(6);
        obj.reset();
        cb(5);
    
        { // Scope of strongCb
        Callback strongCb;
        {
            std::shared_ptr<myClass> obj = std::make_shared<myClass>(100);
            strongCb = obj->createStrongCallback();
            strongCb(10); // <-- still alive
            fmt::print("End of obj first std::shraed_ptr scope\n");
        }
            fmt::print("End of strongCb\n");
        }   // <-- obj get destructed here
        return 0;
    }
    

    Output:

    i: 6
    dtor, i:6
    This is dead.
    i: 110
    End of obj first std::shraed_ptr scope
    End of strongCb
    dtor, i:110