Reputation: 917
is it possible to produce method definition (with an exact number of parameters and return value of known type), having:
Details:
I have a simple reflection structure (partial specialisation stuff omited for readability), which deduces member functions return type and argument types:
template<typename RetType, typename ...ArgTypes>
struct reflect_method<RetType(HostClassType::*)(ArgTypes...)> {
using method_type = RetType;
using method_args = type_placeholder<ArgTypes...>;
using call_type = RetType(HostClassType::*)(ArgTypes...);
};
where method_type
is a method return type, method_args
are a method argument types "frozen" in a helper template struct type_placeholder
.
What I'm trying to do is to create a method in a generated class which will reflect arguments and return type of another method of some other class. The created method will provide a decoration for the reflected method.
Pseudocode implementation:
#define RPCCLASS(class_name) class RPC##class_name : public class_name \
{ \
using SelfType = RPC##class_name; \
using ParentType = class_name;
#define RPCCLASS_END() };
#define RPCBIND(method_name) \
using method_name_##tag = reflect_method<decltype(ParentType::method_name)>; \
method_name_##tag::method_type
method_name(method_name_##tag::method_args::at<0>::type arg0, \
method_name_##tag::method_args::at<1>::type arg1, \
/* ... */ \
/*don't know how to put correct number of arguments here)*/) \
{ \
/* do some stuff */ \
/* ... */ \
/* invoke the reflected method */ \
return Invoke<method_name_##tag>::apply(this, method_name, \
arg0, \
arg1 \
/*again don't know how to put correct number of arguments here)*/) \
}
// USAGE:
class MyOwnClass {
public:
virtual long long doFun(int a, char b, const std::string& c);
};
RPCCLASS(MyOwnClass)
RPCBIND(doFun)
RPCCLASS_END()
Upvotes: 4
Views: 747
Reputation: 917
I found a solution. Instead of trying to generate a member function to reflect a decorated method I figured out that I can generate a member functor instead. The functor is realised as a template structure with a operator()(...). This allows me to have specialisations with correct number of arguments while retaining member function call semantics.
Sample code :
template <int Count>
struct apply_placeholders
{
};
template <>
struct apply_placeholders<1>
{
template<typename CallType, typename Self, template<typename>class CallPtrType>
static CallType apply(Self* self, CallPtrType<Self> callPtr)
{
return std::bind(std::mem_fn(callPtr), self, std::placeholders::_1);
}
};
template <>
struct apply_placeholders<2>
{
template<typename CallType, typename Self, template<typename>class CallPtrType>
static CallType apply(Self* self, CallPtrType<Self> callPtr)
{
return std::bind(std::mem_fn(callPtr), self, std::placeholders::_1, std::placeholders::_2);
}
};
template <>
struct apply_placeholders<3>
{
template<typename CallType, typename Self, template<typename>class CallPtrType>
static CallType apply(Self* self, CallPtrType<Self> callPtr)
{
return std::bind(std::mem_fn(callPtr), self, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
}
};
template <>
struct apply_placeholders<4>
{
template<typename CallType, typename Self, template<typename>class CallPtrType>
static CallType apply(Self* self, CallPtrType<Self> callPtr)
{
return std::bind(std::mem_fn(callPtr), self, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
}
};
template<typename RetType, template<typename...>class FrozenArgTypes, typename... ArgTypes>
struct mimic_functor_impl
{
};
template<typename RetType, typename... ArgTypes>
struct mimic_functor_impl<RetType, type_placeholder, type_placeholder<ArgTypes...>>
{
public:
using CallType = std::function<RetType(ArgTypes...)>;
template<typename Self>
using CallPtrType = RetType(Self::*)(ArgTypes...);
private:
CallType mCall;
public:
mimic_functor_impl(CallType call) : mCall{call}
{
}
RetType operator () (ArgTypes... args)
{
return mCall(args...);
}
template<typename Self>
static CallType make_function(Self* self, CallPtrType<Self> callPtr)
{
// manually specialise the template method because the compiler get's lost on matching "Self::*" in CallPtrType
return apply_placeholders<sizeof...(ArgTypes)>::template apply<CallType, Self, CallPtrType>(self, callPtr);
}
};
template<typename... ArgTypes>
struct mimic_functor_impl<void, type_placeholder, type_placeholder<ArgTypes...>>
{
public:
using CallType = std::function<void(ArgTypes...)>;
template<typename Self>
using CallPtrType = void(Self::*)(ArgTypes...);
private:
CallType mCall;
public:
mimic_functor_impl(CallType call) : mCall{call}
{
}
void operator () (ArgTypes... args)
{
mCall(args...);
}
template<typename Self>
static CallType make_function(Self* self, CallPtrType<Self> callPtr)
{
// manually specialise the template method because the compiler get's lost on matching "Self::*" in CallPtrType
return apply_placeholders<sizeof...(ArgTypes)>::template apply<CallType, Self, CallPtrType>(self, callPtr);
}
};
template<typename Reflect>
struct mimic_functor : mimic_functor_impl<typename Reflect::method_type, type_placeholder, typename Reflect::method_args>
{
private:
using BaseType = mimic_functor_impl<typename Reflect::method_type, type_placeholder, typename Reflect::method_args>;
public:
mimic_functor(typename BaseType::CallType call) : BaseType(call)
{
}
};
#define __TAG(x) x ## _tag
#define RPCBIND(method_name) \
public: \
using __TAG(method_name) = reflect_method<decltype(&ParentType::method_name)>; \
mimic_functor<__TAG(method_name)> method_name{mimic_functor<__TAG(method_name)>::make_function(dynamic_cast<ParentType*>( const_cast<SelfType*>( this ) ), &ParentType::method_name)};
The rest of the code is like in the question listing.
The apply_placeholders
template expands a required number of placeholders to match the count of parameters in a variadic parameter pack.
The mimic_functor_impl
and mimic_functor
templates create the functor whose operator ()(...) will match the reflected methods signature. The functor, when invoked, also calls the reflected method.
The make_function
member template function creates a bound std::function containing the reflected method with "this" pointer.
Upvotes: 2