Search code examples
c++classc++11callbackpointer-to-member

Storing virual functions in Array/Vector container


I have a base class with common behavior and derived classes with override virtual functions. But if tried to save the function pointer as std::vector I will get the error:

 ISO C++ forbids taking the address of an unqualified or
  parenthesized non-static member function to form a pointer to member function.

I need something like this:

class Base
{
public:
    virtual int fun1(int inp) = 0;
    virtual int fun2(int inp) = 0;
    int init(int inp, std::vector<std::function<int(int)>> Callbacks, int index)
    {
        if (index == 0)
            return Callbacks[0](inp);
        else
            return Callbacks[1](inp);
    }
    int run()
    {
        return init(5, { &fun1, &fun2 }, 0);
    }
};

class A : public Base
{
    int fun1(int inp)
    {
        return inp * 10;
    }
    int fun1(int inp)
    {
        return inp * 100;
    }
};

class B : public Base
{
    int fun1(int inp)
    {
        return inp * 20;
    }
    int fun2(int inp)
    {
        return inp * 200;
    }
};

int main(int argc, char const* argv[])
{
    auto f = new B;
    int e = f->run();
    return 0;
}

Is there any way to do what I want? Maybe I can bind a virtual function to a container somehow? Or maybe can I set the lambda function as virtual? When I tried to declare lambda function in the Base class when I change function in derived class I get errors:


Solution

  • You get a "pointer" to a member with the syntax &Class::Member (Class::Member is the "qualified" name of the member).

    These are pointers only in an abstract sense, and need to be dereferenced relative to an object.
    You do this with the ->* or .* operator, depending on whether the left-hand side is a pointer or not.
    Note that you must use this explicitly, if that is the relevant object.

    The type of &Base::fun1 and &Base::fun2 is int (Base::*)(int), which can't be converted to std::function<int(int)>.

    Putting it together:

    int init(int inp, std::vector<int (Base::*)(int)> Callbacks, int index)
    {
        if (index == 0)
            return (this->*Callbacks[0])(inp); // The leftmost parentheses are necessary.
        else
            return (this->*Callbacks[1])(inp);
    }
    
    int run()
    {
        return init(5, {&Base::fun1, &Base::fun2}, 0);
    }
    

    If you don't want to limit the callbacks to members, you can use lambda functions, capturing this:

    int init(int inp, std::vector<std::function<int(int))> Callbacks, int index)
    {
        if (index == 0)
            return Callbacks[0](inp);
        else
            return Callbacks[1](inp);
    }
    
    int run()
    {
        return init(5, 
                    {[this](int i) { return fun1(i); },
                     [](int) { return 567; } }, 
                    0);
    }