Ruben Medrano
Ruben Medrano

Reputation: 159

Store a reference of an object's method received from a function

I was able to pass an object's non static method Dummy to another object's function. The problem comes when I try to store the reference of that method into the object of the function, I don't know how to do it.

#include <iostream>
#include <functional>

class Dummy{
public:
    void runDummyArgs(int z){std::cout<<"TEST: "<<z<<std::endl;};
};

class Test{
public:
   
    template<typename X , typename T>
    void runTest(X* obj, T(X::* func), int z) { (obj->*func)(z); }; 
    /* It works, but I would like to store (obj->*func) in this class as a reference to reuse it when it is needed. */
};

int main()
{
    Dummy *a = new Dummy();
    Test *b = new Test();
    b->runTest(a, &Dummy::runDummyArgs, 99);
    return 0;
}

Upvotes: 1

Views: 499

Answers (1)

user4581301
user4581301

Reputation: 33931

I would write something that looks like:

#include <iostream>
#include <functional>

class Dummy{
public:
    void runDummyArgs(int z){std::cout<<"TEST: "<<z<<std::endl;};
};

class Test{
    std::function<void(int)> mFunc; // added member to hold function
public:
    // added constructor to accept function
    Test(std::function<void(int)> func): mFunc(func)
    {

    }
    // no longer needs any templating. It's all handled by std::function
    // most of the parameters bound by std::function 
    void runTest(int z)
    {
        mFunc(z);
    };
};

int main()
{
    Dummy a; // no need for dynamic allocation. Only use new when forced
             // and that's almost never thanks to containers and smart pointers 

    // This line gets a bit weird. I'll explain it below
    Test b([&a](int z){a.runDummyArgs(z);});

    b.runTest(99); // just needs the end argument of the function
    return 0;
}

OK. That's not so bad. Except for

Test b([&a](int z){a.runDummyArgs(z);});

Test requires a std::function that takes an int and returns nothing. [&a](int z){a.runDummyArgs(z);} is a lambda expression (More documentation that gets into the niggly bits and details) that defines a callable object that takes an int and returns nothing. The resulting instance will be called b. Breaking the lambda down further:

[&a]

Captures variable a by reference. This allows modifying a inside the lambda. Sometimes you want the lambda to carry around its own copy of a, for example if a is a temporary variable that won't be around when the function is called later. To stave off the common future questions about dangling references and const Dummy compiler errors, I'll add a simple example of capture by mutable value to the end of this answer.

(int z)

lambda's parameters

{a.runDummyArgs(z);}

function body that invokes runDummyArgs on a.

More complicated capture by mutable value example:

#include <iostream>
#include <functional>

class Dummy{
    int val = 0; // provide some visible state for instances
public:
    void runDummyArgs(int z)
    {
        std::cout<<"TEST: "<<z + val << std::endl;
        //                     ^ added to show changing state
        val++; // change state
    }
};

class Test{
    std::function<void(int)> mFunc;
public:
    Test(std::function<void(int)> func): mFunc(func)
    {

    }
    void runTest(int z)
    {
        mFunc(z);
    };
};

std::function<void(int)> builder()
{
    Dummy a;
    return [a](int z)mutable{a.runDummyArgs(z);};
    //               ^ the copy of a can be modified.
} // a goes out of scope here, but its copy in the lambda lives on

int main()
{
    Test b(builder());
    b.runTest(99); // print 99
    b.runTest(99); // print 100
    Test c(builder());
    c.runTest(99); // print 99 because we have a copy of a different dummy instance
    c.runTest(99); // print 100
    return 0;
}

Upvotes: 1

Related Questions