0

I want to be able to store a pointer of a function in a struct or class, then I can call upon that function when a button is pressed? here is what I have:

struct INFO
{
    std::function<int(int, int)> call_back;
};

class YO
{
public:
    YO() {};
    ~YO() {};

    int SUM(int A, int B) { return A + B; }
};

int main()
{
    //std::function<int(int, int)> datFunc = SUM;
    INFO info;
    YO* yo = new YO();
    info.call_back = yo->SUM;

    //std::function<int(int, int)> func =;
    std::cout << info.call_back(10, 10) << std::endl;

    system("pause");
    return 0;
}

I get errors:

Error   C3867   'YO::SUM': non-standard syntax; use '&' to create a pointer to member   FunctionPointertest 
Error   C2679   binary '=': no operator found which takes a right-hand operand of type 'overloaded-function' (or there is no acceptable conversion) FunctionPointertest 
  • 2
    Possible duplicate of [Function pointer to member function](https://stackoverflow.com/questions/2402579/function-pointer-to-member-function) – Swordfish Oct 22 '18 at 00:03
  • 1
    C++ is not C#. You have to write explicit code! Here you could write: `info.callback = [yo](int A, int B) { return yo->SUM(A, B); };`. – Phil1970 Oct 22 '18 at 00:21

3 Answers3

1

You need to also supply an instance of the host object to the function call, in this case of class YO. std::function supports this, amongst many conversions it can do, but then the prototype becomes

std::function<int(YO*, int, int)> call_back;

And when you call it:

info.call_back(&yo, 10, 10)
Gem Taylor
  • 5,381
  • 1
  • 9
  • 27
0

Some options to do what you want. Using the lambda is the best solution.

void func() {
    INFO info;
    YO yo;
    YO* yo2 = new YO;
    info.call_back = [&yo](int a, int b) {
        return yo.SUM(a, b);
    };
    using namespace std::placeholders;
    info.call_back = std::bind(&YO::SUM, std::ref(yo), _1, _2);
    info.call_back = std::bind(&YO::SUM, yo2, _1, _2);
}

P.S. You don't usually want to use new in c++.

balki
  • 26,394
  • 30
  • 105
  • 151
0

Here are a set of Variadic Template Classes that will enable you to do this with ease if you have C++17 available to you. The first class simply stores a generic version of a std::function of any type! The second class stores the first class through a member function and will register either a function pointer, a function object or a lambda. It also has another member function that will invoke it. I am currently using lambdas in this example. I have two lambdas, the 1st takes two int types adds them and returns an int as in your problem above. The 2nd takes two std::strings concatenates them then prints them to the screen but does not return any value. You can even expand this by having a std::vector<Functor<>> stored in the driver class. The register_callback would change slightly as to pushing them into the vector, and the call_back you have options. You could either call them all in one go, or search via an index value to call a specific one, but I'll leave that as an exercise to you.

#include <string>
#include <iostream>
#include <functional>

template<typename RES_TYPE, typename... ARG_TYPES>
struct Functor {
    std::function<RES_TYPE( ARG_TYPES... )> func_;
};

template<typename RES_TYPE, typename... ARG_TYPES>
class Driver {
private:
    Functor<RES_TYPE, ARG_TYPES...> functor;

public:
    Driver() = default;
    ~Driver() = default;

    void register_callback( const Functor<RES_TYPE, ARG_TYPES...> &func ) {
        functor = func;
    }

    RES_TYPE call_back( ARG_TYPES... args ) {
        return functor.func_( std::forward<ARG_TYPES>(args)... );
    }    
};    

int main() {
    // Function Type: int ( int, int );
    Functor<int, int, int> func;
    auto lambda = []( int a, int b ) { return a + b; };
    func.func_ = lambda;

    Driver<int, int, int> driver;
    driver.register_callback( func );
    int a = 3;
    int b = 5;
    std::cout << driver.call_back( a, b ) << '\n';
    std::cout << driver.call_back( 7, 5 ) << '\n';

    // Function Type: void ( string, string );
    Functor<void, std::string, std::string> func2;
    auto lambda2 = []( std::string str1, std::string str2 ) {
        str1 = str1 + " " + str2;
        std::cout << str1 << '\n';
    };

    Driver <void, std::string, std::string> driver2;
    func2.func_ = lambda2;
    driver2.register_callback( func2 );
    std::string str1 = "Hello";
    std::string str2 = "World";
    driver2.call_back( str1, str2 );
    driver2.call_back( "Generic", "Programming" );

    return 0;
}

The output is:

8
12
Hello World
Generic Programming

If you notice with this code here; there is no need to mess with pointers or dynamic memory. This is all taken care of by the use of std::function for us and the default dtors of the classes.

I tried my best to design this with simplicity, readability while making it portable and generic as possible. I am sure there can be some improvements made, but I think this is in line of what you are asking for.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59