How do I create an array of function pointers of different prototypes?
but given that I only want to use the functions without supplying an argument
It simply doesn't work like that. Did you ever wonder, then when you're putting function declarations in a header, why you have to write default parameters into the header and cannot place it in the definition in the implementation source file?
That's because the default parameters are in fact not "embedded" into the function, but used by the compiler to augment a function call with those parameters at a calling location, where those parameters are omitted. (EDIT: Also, as @Aconcagua so keenly observed in a comment, since default parameters are usually defined as part of a header function declaration, any change of the default values requires a full recompilation of any compilation unit that included those headers, ergo function declarations, for the change to actually take effect!)
While it's perfectly possible to do some really weird type casting madness to construct an array of function pointers like that, eventually you'll have to cast back to the original function call signature in order to not invoke undefined behavior.
If anything you'll have to bind the function pointer, together with a set of default parameters in some type that abstracts away the calling, does supply the parameters, and to the outside offers a polymorphic interface. So you'd have a std::vector<function_binder>
or function_binder[]
where function binder has an operator()
that calls the function.
But when you're doing binding in the first place, you can bind it in an anonymous function, i.e. lambdas. At the time of lambda instantiation the default parameters are bound.
std::vector<void(*)()> fvec = {
[]{ func0(); },
[]{ func1(); },
[]{ func2(); }
};
You can use std::bind
std::function<ParentClass *(void)> arr[5] = {
std::bind(&fun1),
std::bind(&fun2),
std::bind(&fun3, false),
std::bind(&fun4),
std::bind(&fun5, 1, 3)
};
now you can do
for(int i=0; i<5; i++)
arr[i]();
You have to make sure every function parameter of all functions are bound.
This also works well with member functions. You just have to bind the object reference (e.g. this
) as first parameter.
C-style or not, what you have is straight undefined behaviour. Use lambdas:
void (*arr[5])() = {
[] { fun1(); },
[] { fun2(); },
[] { fun3(); },
[] { fun4(); },
[] { fun5(); }
};
These are okay because they perform the call through the function's correct type, and are themselves convertible to void (*)()
.
Forwarding the returned value stays simple enough, since the lambda provides a context for the conversion. In your case, since ChildClass
supposedly inherits from ParentClass
, an implicit conversion is enough:
ParentClass *(*arr[5])() = {
[]() -> ParentClass * { return fun1(); },
[]() -> ParentClass * { return fun2(); },
[]() -> ParentClass * { return fun3(); },
[]() -> ParentClass * { return fun4(); },
[]() -> ParentClass * { return fun5(); }
};
A c++20 solution:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
template<auto f, class R, class...Args>
struct explicit_function_caster {
using Sig=R(Args...);
using pSig=Sig*;
constexpr operator pSig()const {
return [](Args...args)->R {
return static_cast<R>(f(std::forward<Args>(args)...));
};
}
};
template<auto f>
struct overload_storer_t {
template<class R, class...Args>
constexpr (*operator R() const)(Args...) const {
return explicit_function_caster<f, R, Args...>{};
}
template<class...Args>
auto operator()(Args&&...args)
RETURNS( f( std::forward<Args>(args)... ) )
};
template<auto f>
overload_storer_t<f> generate_overloads={};
#define OVERLOADS_OF(...) \
generate_overloads< \
[](auto&&...args) \
RETURNS( __VA_ARGS__( decltype(args)(args)... ) ) \
>
which is a lot of boilerplate, but gets us:
ParentClass* (*arr[5])() = {
OVERLOADS_OF(fun1),
OVERLOADS_OF(fun2),
OVERLOADS_OF(fun3),
OVERLOADS_OF(fun4),
OVERLOADS_OF(fun5)
};
void (*arr2[5])() = {
OVERLOADS_OF(fun1),
OVERLOADS_OF(fun2),
OVERLOADS_OF(fun3),
OVERLOADS_OF(fun4),
OVERLOADS_OF(fun5)
};
basically generate_overloads<x>
takes a constexpr
callable object x
and lets you cast it at compile time to a pointer to a function of any compatible signature and call it with (almost) any signature.
Meanwhile, OVERLOADS_OF
converts a function name into a constexpr
object that does overload resolution on that function name. I use it here because fun3
as a function pointer does not know about its default arguments, but at overload resolution time it does.
In this particular case, it is far easier to just write toy lambdas to do this work; this is just an attempt to automate writing those toy lambdas for arbitrary compatible signatures.