Reputation: 319
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.Upvotes: 3
Views: 132
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