Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to break down a templated pointer to a function?

Currently I have a template like so:

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

And I'm using 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 it's possible to only have this:

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

If I have it like that, I wouldn't be able to overload operator() as I cannot split up the function pointer type into arguments and return type (so that I can write return_type operator()(args...)).

Is there any way to achieve this?

I'm using VS2013 Nov 2013 CTP

like image 659
rev Avatar asked Jan 27 '15 18:01

rev


1 Answers

You can do it with a specialization like this:

// Entry has one template argument
template<typename func> class Entry;

// and if it's 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 like in your example (although I like the function type syntax better), write

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

Addendum: One more thing came to me when I was out getting dinner: There is a (slight) problem with the operator() that may or may not concern you: You don't run into forwarding problems with parameters that are passed by value or by lvalue reference because they're just passed on as they were passed in (because the argument list is exactly the same for the function pointer and the operator()), but if you plan to use rvalue-reference parameters, that does not work for them implicitly. For this reason,

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

does not compile. If you plan to use this with functions that take rvalue references, operator() can be fixed like so:

ret operator()(args... a){
  //                   explicit forwarding ----v
  return ((ret(*)(args...)) this->Address)(std::forward<args>(a)...);
}
like image 102
Wintermute Avatar answered Oct 03 '22 06:10

Wintermute