Garmekain
Garmekain

Reputation: 319

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):

Upvotes: 3

Views: 132

Answers (1)

user2296177
user2296177

Reputation: 2837

I've provided a vastly superior implementation with an explanation here:

https://codereview.stackexchange.com/q/140510/88422


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();
}

Upvotes: 2

Related Questions