Garmekain Garmekain - 3 months ago 16
C++ Question

Create a overriden method in base class when you don't know the arguments passed to the overriding functions?

I have an abstract class A which is base for the other classes, let's say B and C.

Classes B and C should have a

void
type
handle_input
method, but argument number and types may vary from B to C.

I also have a function that takes a pointer to an A as an argument - polymorphically speaking, that could be either A, B or C - and calls the method
handle_input
.




The problem comes when defining
A::handle_input
because the arguments passed to
B::handle_input
may be different to the ones passed to
C::handle_input
.

That leds me to create in class A something like:

/* base class */
class A {
template <class... FUNCTION_ARGS>
virtual void handle_input(FUNCTION_ARGS&&...) = 0;
};


But errors occur because the template is calculated at compile time, while virtual is used at run time.

I also did

class A {
template <class... FUNCTION_ARGS>
using input_handler = function<void(FUNCTION_ARGS&&...)>;

template <class... FUNCTION_ARGS>
input_handler<FUNCTION_ARGS...> handle_input;
};


But the outcome is more or less the same, as expected.




I guess my question would be "How can you create a overriden method in base class when you don't know the arguments passed to the potentially multiple overriding functions?"

Notes(correct me if I'm wrong):


  • handle_input
    is dynamically polymorphic, so it has to be virtual.

  • handle_input
    's argument number in unknown, so parameter packs must be used.


Answer

To circumvent what NathanOliver is saying, you can use closures. They will store their arguments, and you can call them polymorphically.

Disclaimer: This is a very barebones implementation to show the technique.

template<class F, class... Args>
auto make_closure( F&& f, Args&&... args ) noexcept
{
    return [=] { return f( args... ); };
}

struct fn_base
{
    virtual ~fn_base() = default;
    virtual void invoke() = 0;
};

template<class T, class... Args>
struct fn : public fn_base
{
    using closure_t = decltype( make_closure( std::declval<T>(), std::declval<Args>()... ) );
    closure_t closure_;

    fn( closure_t&& closure ) : closure_{ std::move( closure ) } {}

    void invoke() override
    {
        closure_();
    }
};

template<class F, class... Args>
auto make_fn( F&& f, Args&&... args )
{
    return fn<F, Args...>{ make_closure( std::forward<F>( f ), std::forward<Args>( args )... ) };
}

Sample usage:

#include <iostream>

void f( int, char )
{
    std::cout << "f( int, char )\n";
}

void g( double )
{
    std::cout << "g( double )\n";
}

int main( int, char*[] )
{
    auto df0 = make_fn( &f, 1, 'c' );
    auto df1 = make_fn( &g, 0.5 );
    fn_base* f0 = &df0;
    fn_base* f1 = &df1;
    f0->invoke();
    f1->invoke();
}