Is there a way to break a template pointer to a function?

I currently have a template like this:

template<typename func, typename ret, typename... args> class Entry{ public: PVOID Address; ret operator()(args...){ return ((func) this->Address)(args...); } }; 

And I use it like this:

 Entry<int(*)(int), int, int> func; // ^func ^ret ^args func.Address = (PVOID) 0xDEADC0DE; func(123); // calls 0xDEADC0DE with '123' as argument 

However, I was wondering if this is only possible:

 Entry<int(*)(int)> func; // ^only specifying the function prototype once instead of breaking it down func(123); 

If I have this, I would not be able to overload operator() , since I cannot separate the type of the function pointer into arguments and the type of the return value (so that I can write return_type operator()(args...) ).

Is there any way to achieve this?

I am using VS2013 November 2013 CTP

+6
source share
3 answers

You can do this with specialization as follows:

 // Entry has one template argument template<typename func> class Entry; // and if it a function type, this specialization is used as best fit. template<typename ret, typename... args> class Entry<ret(args...)>{ public: PVOID Address; ret operator()(args... a){ return ((ret(*)(args...)) this->Address)(a...); } }; int main() { Entry<int(int)> foo; foo.Address = (PVOID) 0xdeadc0de; func(123); } 

To use it with a function pointer type, as in your example (although I like the function type syntax more), write

 // here ------v template<typename ret, typename... args> class Entry<ret(*)(args...)>{ 

Addition. Another thing came to me when I had lunch: there is a problem (with a little) with operator() that may or may not bother you: you do not encounter forwarding problems with parameters that are passed by value or by lvalue link, because they are simply passed as they are passed (because the argument list is exactly the same for the function pointer and operator() ), but if you plan to use rvalue-reference parameters, this does not work for them implicitly. For this reason

 Entry<int(int&&)> foo; foo(123); 

not compiled. If you plan to use this with functions that accept rvalue references, operator() can be fixed as follows:

 ret operator()(args... a){ // explicit forwarding ----v return ((ret(*)(args...)) this->Address)(std::forward<args>(a)...); } 
+5
source

Partial specialization is possible, as shown in the @Wintermutes post.
However, what you are trying should be possible without it:

 template <typename func> class Entry{ public: PVOID Address; template <typename... Args> auto operator()(Args&&... args) -> decltype( ((func*) Address)(std::forward<Args>(args)...) ) { return ((func*) Address)(std::forward<Args>(args)...); } }; 

The template argument must be a function type. However, you can make it work with both a function and a pointer to function types with a slight modification in the returned expression: instead of using func* as the target cast type, use typename std::remove_pointer<func>::type* , i.e. .

 template <typename... Args> auto operator()(Args&&... args) -> decltype( ((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...) ) { return ((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...); } 

Demo

+1
source

Metaprogramming Approach. First, some pointers that try to store call information:

 template<class...>types {using type=types;}; enum class calling_convention { cdecl, clrcall, stdcall, fastcall, thiscall, vectorcall, }; template<class Sig> struct signature_properties; template<class R, class...Args> struct signature_properties { using return_type = R; using argument_types = types<Args...>; }; template<class FuncPtr> struct function_properties; #define CAT_(A,B) A##B #define CAT(A,B) CAT_(A,B) #define CALLING_CONVENTION_SUPPORT( CONVENTION ) \ template<class R, class... Args> \ struct function_properties< R(CAT(__, CONVENTION) *)(Args...) >: \ signature_properties<R(Args...)> \ { \ using type = R(CAT(__, CONVENTION) *)(Args...) \ static const calling_convention convention = calling_convention::CONVENTION; \ static type from_pvoid(void const* pvoid) { \ return static_cast<type>(pvoid); \ } \ } CALLING_CONVENTION_SUPPORT(cdecl); CALLING_CONVENTION_SUPPORT(clrcall); CALLING_CONVENTION_SUPPORT(stdcall); CALLING_CONVENTION_SUPPORT(fastcall); CALLING_CONVENTION_SUPPORT(thiscall); CALLING_CONVENTION_SUPPORT(vectorcall); #undef CAT #undef CAT_ #undef CALLING_CONVENTION_SUPPORT 

Icky Macros. And a serious bust. And unverified. But you have an idea.

Next, an assistant to do the work:

 template<class FuncPtrType, class R, class Args> struct helper; template<class FuncPtrType, class R, class... Args> struct helper<FuncPtrType, R, types<Args...>> { FuncPtrType ptr; R operator()(Args...args)const { return ptr(std::forward<Args>(args)...); } helper(FuncPtrType p):ptr(p) {}; helper( helper const& )=default; helper& operator=( helper const& )=default; }; 

Perfect forwarding in an assistant would also be seductive.

Finally, we use the feature class above to bounce work from Entry to helper :

 template<class FuncPtrType> struct Entry:helper< FuncPtrType, typename signature_properties<FuncPtrType>::return_type, typename signature_properties<FuncPtrTpye>::arguments > { using parent = helper< FuncPtrType, typename signature_properties<FuncPtrType>::return_type, typename signature_properties<FuncPtrTpye>::arguments >; Entry(void const* pvoid):parent( static_cast<FuncPtrType>(pvoid) ) {} }; 

except that we include the constructor in Entry to take void const* and forward the typed pointer to helper .

One change is that we drop from void* to our type of function at the earliest point we know is the type of function, and not at the point that we call.

0
source

Source: https://habr.com/ru/post/981621/


All Articles