c++pointerslambdaunreal-engine5

Member functions called in lambdas


This question arose when programming for Unreal Engine with TFunction<> but it is fundamentally about C++ and I believe the example is equivalent.

The questions are:

  1. How to ensure that the callback is not called when the object doesn't exist
  2. Is there a significant overhead from this type of member-function-from-lambda call?
  3. Are there some other hidden dangers in this approach?

The code:

#include <iostream>
#include <functional>

// Basic class with a callback function expressed as a lambda calling a member function 
class Foo {
    private:
        int i = 0;
    public:
        Foo(int j){
            i = j;
        }

        // A member function that should be called by a manager
        void f(){
            std::cout << i << std::endl;
        }

        // Wrap the member function in a lambda
        std::function<void()> wrapper = [&](){f();}; // <---- What happens when object is destroyed before call? 
};

// Manager class which calls a specified function after some work 
class Manager {
    public:
        // A function that returns immediately and after some time calls the passed function
        void asyncWork(std::function<void()> callback){
            // Some work here...

            callback();
        } 
};

int main() {

    // Create object
    Foo foo(42);

    // Create manager
    Manager manager;

    // Pass the callback function to the manager
    manager.asyncWork(foo.wrapper);

    return 0; 
}

The program does the following:


Solution

  • To answer specific to Unreal Engine, the solution is to use a TWeakObjectPtr. This requires the class be derived from UObject.

    Assuming that you change Foo to UFoo : public UObject then:

    TFunction<void()> UFoo::Wrapper()
    {
        TWeakObjectPtr WeakThis (this);
        return [WeakThis]()
        {
            if (ThisClass* This = WeakThis.Get())
            {
                // This is a valid object
                This->CallPrivateMethod();  // or call This->f() if you want
            }
            // else the object WeakThis points to has been destroyed
        };
    }
    

    To generalize to C++, presumably the same method would work with a std::weak_ptr.