3

I have been trying to implement a callback function in c++. Within a class, I have a struct, a number of methods, and a method that creates an instance of the struct with one of the other methods as its argument.

The struct has many other variables, but an illustration is depicted here:

class MYCLASS
{
public:
    MYCLASS();

    struct TEST{
        std::function<int(int)> foo;
    };

    int plus(int x){
        return x + 1;
    }

    int minus(int x){
        return x - 1;
    }

    void sim(){
        TEST T;             // make an instance of TEST
        T.foo = plus(5);    // assign TEST.foo a function (plus or minus)  
        T.foo();            // call the method we assigned
    }
};

Within the sim method, I want to create an instance of test and give it either plus or minus, depending on some criterion. Both lines where I try and give the instance T a plus function and subsequently call it are incorrect.

JeJo
  • 30,635
  • 6
  • 49
  • 88
AngusTheMan
  • 564
  • 1
  • 6
  • 15
  • 2
    `plus(5)` is not a method. It's a result of calling `plus` with arg `5` which in your case is simply `6`. What you want is `T.foo = plus; int result = T.foo(5);`. – freakish Oct 08 '18 at 20:29
  • Thank you, this gives me the following `error: reference to non-static member function must be called T.foo = plus; ` – AngusTheMan Oct 08 '18 at 20:37
  • right, both your functions have to be static. Or you have to wrap the call into "this" catching lambda: `T.foo = [this](int a) { return this->plus(a); }`. Note that this is no longer safe. Your `MYCLASS` has to outlive `TEST` otherwise UB – freakish Oct 08 '18 at 20:43
  • @freakish thank you, that worked! :) – AngusTheMan Oct 08 '18 at 20:49

2 Answers2

2

If you want to delay the call to T.foo, then you could use a lambda like this:

    T.foo = [this](int x) { return plus(x); };
    T.foo(5);
HugoTeixeira
  • 4,674
  • 3
  • 22
  • 32
1

Option - 1

If the member functions plus() and minus() are simple enough like you have shown, you can make them as lambda functions inside the struct TEST. Since the capture-less lambdas can be stored in typed function pointers, the following will do what you want. See live demo

#include <iostream>
class MYCLASS
{
    int m_var = 5; // just for demonstration
public:
    MYCLASS() = default;
    struct TEST
    {
        using fPtrType = int(*)(int);   // function pointer type
        const fPtrType foo1 = [](int x) { return x + 1; };  // plus function
        const fPtrType foo2 = [](int x) { return x - 1; };  // minus function
    };

    void sim()
    {
        TEST T;
        std::cout << "Answer from int PLUS(int): " << T.foo1(m_var) << std::endl;
        std::cout << "Answer from int MINUS(int): " << T.foo2(m_var) << std::endl;
    }
};

Option - 2

If the above alter a lot in your code, use typed function pointer again for member functions and do as follows; which will avoid unnecessary copying(by capturing) the class instance to the lambda and template instantiation and other performance issues comes along with std::function as well.

See live demo

#include <iostream>    
class MYCLASS
{
    using fPtrType = int(MYCLASS::*)(int); // class member function pointer type
public:
    MYCLASS() = default;
    struct TEST { fPtrType foo = nullptr; };

    int plus(int x) { return x + 1; }
    int minus(int x) { return x - 1; }

    void sim()
    {
        TEST T;
        T.foo = &MYCLASS::plus; // now you can
        std::cout << "Answer from int PLUS(int): " << (this->*T.MYCLASS::TEST::foo)(5) << std::endl;
                                                     //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ syntax would be a bit ugly
        // later same ptr variable for minus()
        T.foo = &MYCLASS::minus;
        int answer = (this->*T.MYCLASS::TEST::foo)(5);
        std::cout << "Answer from int MINUS(int): " << answer << std::endl;
    }
};

int main()
{
    MYCLASS obj;
    obj.sim();
    return 0;
}

Output:

Answer from int PLUS(int): 6
Answer from int MINUS(int): 4
JeJo
  • 30,635
  • 6
  • 49
  • 88