C++ function call wrapper with function as template argument
Asked Answered
D

7

17

I'm trying to create a generic wrapper function that takes a function as a template argument and takes the same arguments as that function as its arguments. For example:

template <typename F, F func>
/* return type of F */ wrapper(Ts... Args /* not sure how to get Ts*/)
{
    // do stuff
    auto ret = F(std::forward<Ts>(args)...);
    // do some other stuff
    return ret;
}

The solution needs to be castable to a function pointer with the same type as func so that I can pass it to a C api. In other words, the solution needs to be a function and not a function object. Most importantly, I need to be able to do work in the wrapper function.

If the inline comments aren't clear, I'd like to be able to do something like the following:

struct c_api_interface {
    int (*func_a)(int, int);
    int (*func_b)(char, char, char);
};

int foo(int a, int b)
{
    return a + b;
}

int bar(char a, char b, char c)
{
    return a + b * c;
}

c_api_interface my_interface;
my_interface.func_a = wrapper<foo>;
my_interface.func_b = wrapper<bar>;

I looked for related posts and found these, but none of them are quite what I'm trying to do. Most of these posts concern function objects. Is what I'm trying to do even possible?

Function passed as template argument

Function wrapper via (function object) class (variadic) template

How does wrapping a function pointer and function object work in generic code?

How do I get the argument types of a function pointer in a variadic template class?

Generic functor for functions with any argument list

C++ Functors - and their uses

In response to the first 2 responses, I edited the question to make it clear that I need to be able to do work in the wrapper function (i.e. modify some global state before and after the call to the wrapped function)

Dockery answered 8/4, 2016 at 5:14 Comment(0)
D
12
#include <utility>
#include <iostream>

struct c_api_interface { int (*func_a)(int, int); int (*func_b)(char, char, char); };
int foo(int a, int b) { return a + b; }
int bar(char a, char b, char c) { return a + b * c; }


template<typename Fn, Fn fn, typename... Args>
typename std::result_of<Fn(Args...)>::type
wrapper(Args... args) {
   std::cout << "and ....it's a wrap ";
   return fn(std::forward<Args>(args)...);
}
#define WRAPIT(FUNC) wrapper<decltype(&FUNC), &FUNC>

int main() {
  c_api_interface my_interface;
  my_interface.func_a = WRAPIT(foo);
  my_interface.func_b = WRAPIT(bar);

  std:: cout << my_interface.func_a(1,1) << std::endl;
  std:: cout << my_interface.func_b('a','b', 1) << std::endl;

  return 0;
}

see http://rextester.com/ZZD18334

Doggy answered 8/4, 2016 at 6:17 Comment(5)
Thank you! I'm changing this to the accepted answer because it's shorter that of @T.C., uses just a function and not a struct, uses std::forward, and (as a matter of opinion) is easier to understand because you did not use template argument specialization.Dockery
Though I have to admit the std::forward is only in there because I tried something that would compile under MSVC and then thought "ah screw it, not gonna happen ...but hey, keep the forward" ;-)Doggy
What is the proper way to declare an alias for the wrapper which will look like a normal function to call? Something like using a_wrapped=wrapper<decltype(&foo),&foo>; (but this one does not work).Vevay
This approach will not work through the WRAPIT macro if a function is overloaded.Forzando
It also doesn't seem to work for classes that you call via operator(). By the way in C++17 you should use std::invoke_type_t<> insead of std::result_of<>::type.Stand
S
12
template<class F, F f> struct wrapper_impl;
template<class R, class... Args, R(*f)(Args...)>
struct wrapper_impl<R(*)(Args...), f> {
    static R wrap(Args... args) {
        // stuff
        return f(args...);
    }
};

template<class F, F f>
constexpr auto wrapper = wrapper_impl<F, f>::wrap;

Use as wrapper<decltype(&foo), foo>.

Spermary answered 8/4, 2016 at 6:12 Comment(4)
Thank you so much, this worked perfectly! :) For others: the variable template is a c++14 feature I believe, so that may not work on all compilers. I defined a macro #define wrapper(func) wrapper_impl<decltype(&func), func>::wrap that accomplishes roughly the same thing.Dockery
Could this be adapted to accomodate optional arguments? Meaning that args optional in the wrapped function are optional in the wrapper as well. In this variant, all arguments have to be specified.Vevay
do you think it is possible to extend to wrap a member function? I have tried to add a typename T in template and a T *t as template parameter too. Then calling the function by t->f(args...) but it simply doesn't workPiggott
This errors out in C++17 with: " error: implicit instantiation of undefined template"Warison
D
12
#include <utility>
#include <iostream>

struct c_api_interface { int (*func_a)(int, int); int (*func_b)(char, char, char); };
int foo(int a, int b) { return a + b; }
int bar(char a, char b, char c) { return a + b * c; }


template<typename Fn, Fn fn, typename... Args>
typename std::result_of<Fn(Args...)>::type
wrapper(Args... args) {
   std::cout << "and ....it's a wrap ";
   return fn(std::forward<Args>(args)...);
}
#define WRAPIT(FUNC) wrapper<decltype(&FUNC), &FUNC>

int main() {
  c_api_interface my_interface;
  my_interface.func_a = WRAPIT(foo);
  my_interface.func_b = WRAPIT(bar);

  std:: cout << my_interface.func_a(1,1) << std::endl;
  std:: cout << my_interface.func_b('a','b', 1) << std::endl;

  return 0;
}

see http://rextester.com/ZZD18334

Doggy answered 8/4, 2016 at 6:17 Comment(5)
Thank you! I'm changing this to the accepted answer because it's shorter that of @T.C., uses just a function and not a struct, uses std::forward, and (as a matter of opinion) is easier to understand because you did not use template argument specialization.Dockery
Though I have to admit the std::forward is only in there because I tried something that would compile under MSVC and then thought "ah screw it, not gonna happen ...but hey, keep the forward" ;-)Doggy
What is the proper way to declare an alias for the wrapper which will look like a normal function to call? Something like using a_wrapped=wrapper<decltype(&foo),&foo>; (but this one does not work).Vevay
This approach will not work through the WRAPIT macro if a function is overloaded.Forzando
It also doesn't seem to work for classes that you call via operator(). By the way in C++17 you should use std::invoke_type_t<> insead of std::result_of<>::type.Stand
J
1

you may try something like that (Ugly, but works)

#include <iostream>
#include <functional>

struct wrapper_ctx
{
  wrapper_ctx ()
  {
    std::cout << "Before" << std::endl;
  }
  ~wrapper_ctx ()
  {
    std::cout << "after" << std::endl;
  }
};

template <typename F, typename... Args>
auto executor (F&& f, Args&&... args) -> typename std::result_of<F(Args...)>::type
{
  wrapper_ctx ctx;
  return std::forward<F>(f)( std::forward<Args>(args)...);

}

template <typename F>
class wrapper_helper;


template<typename Ret, typename... Args>
class wrapper_helper <std::function<Ret(Args...)>>
{
  std::function<Ret(Args...)> m_f;
public:
  wrapper_helper( std::function<Ret(Args...)> f ) 
      : m_f(f) {}
  Ret operator()(Args... args) const 
  { 
    return executor (m_f, args...); 
  }
};

template <typename T>
wrapper_helper<T> wrapper (T f)
{
  return wrapper_helper <T>(f);
}


int sum(int x, int y)
{
  return x + y;
}


int main (int argc, char* argv [])
{

  std::function<int(int, int)> f = sum;
  auto w = wrapper (f);

  std::cout << "Executing the wrapper" << std::endl;
  int z = w(3, 4);

  std::cout << "z = " << z << std::endl;
}
Judicature answered 8/4, 2016 at 6:51 Comment(0)
S
0

you probably need something like

template <typename F>
class Wrapper {
public:
    Wrapper(F *func) : function(func) {}
    operator F* () { return function; }
    F *function;
};

Which you can use like void (*funcPtr)(int) = Wrapper<void(int)>(&someFunction);

Slumber answered 8/4, 2016 at 5:31 Comment(3)
I appreciate the response, but this doesn't do what I asked for in the original post. In your solution, if I call funcPtr() it calls someFunction. I would like it to call a function that does some work before and after calling someFunction.Dockery
@Eric, now I understand what you want and don't understand why are you looking for such a hard way. Lambda functions without capturing are castable to c function pointers: void (*funcPtr)(int) = [](int a){ /*smth*/}; - call wrapped function and before/after routines right inside lambdaSlumber
I tried that at first. I believe lambdas are only castable to function pointers if they don't capture, and to make this generic I needed to write a lambda that captures the function it's going to call, but maybe there's a way to make that work that I'm missing.Dockery
S
0

I think that will be the concise way to do what you want:

template <typename F>
F* wrapper(F* pFunc)
{
    return pFunc;
}

and use it like this:

my_interface.func_a = wrapper(foo);
my_interface.func_a(1, 3);
Spurgeon answered 8/4, 2016 at 5:37 Comment(2)
Thanks for the response, but this doesn't do what I asked for. I need to be able to do some work in the wrapper function before and after I call the wrapped function (in this case foo). Thus the wrapper function needs to have the same function signature as the wrapped function.Dockery
I'm not sure you can retrieve signature from template arguments (namely Ts... Args) but you can save them for deferred call as showed here and then in Wrapper::operator() do stuff before and after call to underlying func with saved argumentsSpurgeon
H
0

You may try this

template <class R, class... Args>
struct wrap
{
    using funct_type = R(*)(Args...);
    funct_type func;
    wrap(funct_type f): func(f) {};
    R operator()(Args&&... args)
    {
          //before code block
          std::cout << "before calling\n";
          R ret=func(std::forward<Args>(args)...);
          //after code block
          std::cout << "After calling\n";
    }
};

use like this for example:

int somefunc(double &f, int x);
auto wrapped_somefunc=wrap{somefunc};
double f=1.0;
int x = 2;
auto result=wrapped_somefunc(f,x);
Housewifery answered 27/11, 2022 at 13:59 Comment(4)
No perfect forwarding?Monetta
You pass function pointers as parameter there's no need i think for perfect forwardingHousewifery
I mean here: R operator()(Args... args). Could have been R operator()(Args&&... args) and later R ret=func(std::forward<Args>(args)...);Monetta
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Wera
H
0

This one is for c++17 and newer uses auto template parameters:

template <auto func, class... Args>
auto wrap_func(Args... args)
{
    std::cout << "before calling wrapped func\n";
    auto ret = func(args...);
    std::cout << "after calling wrapped func\n";
    return ret;
}

use for example:

int some_func(int a, int b);
auto ret = wrap_func<some_func>(2, 3);
Housewifery answered 28/11, 2022 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.