Reputation: 56
I would like to support calls like this
class BaseClass {
public:
virtual void print2(float arg1, float arg2) = 0;
};
int main() {
invoke(subclass, &BaseClass::print2, 1, 2);
}
where
subclass
is automatically upcasted to BaseClass
int
to float
I managed to solve the part with the base class. But the argument part gives me a headache. My approach is to extract all the needed type information from the handed function pointer. The other arguments are casted implicitly then (if possible).
What i have so far is this:
template<class Func> struct FunctionPointer2 {};
template<class Class, typename Arg1, typename Arg2> struct FunctionPointer2<void (Class::*)(Arg1, Arg2)> {
typedef Class Class;
typedef Arg1 Argument1;
typedef Arg2 Argument2;
};
template <typename Func>
void invoke2(typename FunctionPointer2<Func>::Class* obj, Func func, typename FunctionPointer2<Func>::Argument1 arg1, typename FunctionPointer2<Func>::Argument2 arg2) {
(obj->*func)(arg1, arg2);
}
It works but for each count of parameters another function, function pointer struct, ... is needed.
So here's the question: Is following even possible? How can i make it possible?
template<class Func> struct FunctionPointer {};
template<class Class, typename ... Args> struct FunctionPointer<void (Class::*)(Args ...)> {
typedef Class Class;
typedef Args Arguments; // pass type parameter pack here
};
template <typename Func>
void invoke(typename FunctionPointer<Func>::Class* obj, Func func, typename FunctionPointer<Func>::Arguments ... params) {
(obj->*func)(params ...);
}
Thanks for you help :)
Upvotes: 0
Views: 576
Reputation: 66200
Not sure to understand... but if you want that the first argument is up-casted... I suppose you're looking something as
template <typename D, typename R, typename B, typename ... As1,
typename ... As2>
void invoke (D && d, R(B::*pnt)(As1...), As2 && ... args)
{ (dynamic_cast<B&>(std::forward<D>(d)).*pnt)
(std::forward<As2>(args)...); }
Observe there is a couple of variadic template packs: As1...
, for the argument required from the method, As2...
, for the args...
you pass to invoke
.
You can unify this packs as follows
template <typename D, typename R, typename B, typename ... As>
R invoke (D && d, R(B::*pnt)(As...), As && ... args)
void { (dynamic_cast<B&>(std::forward<D>(d)).*pnt)
(std::forward<As>(args)...); }
but is dangerous because there is the risk that the compiler can't deduce As...
types when there isn't a perfect match (as in your example, where you pass two int
to a method that wants two float
).
The following is a full compiling example
#include <iostream>
struct BaseClass
{ virtual void print2 (float, float) = 0; };
struct Derived : public BaseClass
{
void print2 (float f1, float f2)
{ std::cout << f1 << ", " << f2 << std::endl; }
};
template <typename D, typename R, typename B, typename ... As1,
typename ... As2>
void invoke (D && d, R(B::*pnt)(As1...), As2 && ... args)
{ (dynamic_cast<B&>(std::forward<D>(d)).*pnt)
(std::forward<As2>(args)...); }
int main()
{
Derived der;
invoke(der, &BaseClass::print2, 1, 2);
}
Upvotes: 1
Reputation: 96286
Both the manual upcast and extracting the parameter types should be unnecessary.
You can just do this:
template <typename T, typename M, typename ...P>
void invoke(T &&object, M member, P &&... params)
{
(std::forward<T>(object).*member)(std::forward<P>(params)...);
}
Upvotes: 4