【问题标题】:C++ function call wrapper with function as template argument以函数作为模板参数的 C++ 函数调用包装器
【发布时间】:2016-07-29 06:29:16
【问题描述】:

我正在尝试创建一个通用包装函数,该函数将函数作为模板参数,并将与该函数相同的参数作为其参数。例如:

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;
}

解决方案需要转换为与func 相同类型的函数指针,以便我可以将其传递给C api。换句话说,解决方案需要是一个函数而不是函数对象。最重要的是,我需要能够在包装函数中工作

如果内联 cmets 不清楚,我希望能够执行以下操作:

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>;

我查找了相关帖子并找到了这些帖子,但它们都不是我想要做的。这些帖子中的大多数都涉及函数对象。我正在尝试做的事情是否可能?

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

针对前 2 个回复,我编辑了问题以明确我需要能够在包装函数中工作(即在调用包装函数之前和之后修改一些全局状态)

【问题讨论】:

    标签: c++ templates variadic-templates variadic-functions


    【解决方案1】:
    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;
    

    用作wrapper&lt;decltype(&amp;foo), foo&gt;

    【讨论】:

    • 非常感谢,这非常有效! :) 对于其他人:变量模板是我相信的 c++14 功能,因此它可能不适用于所有编译器。我定义了一个宏 #define wrapper(func) wrapper_impl&lt;decltype(&amp;func), func&gt;::wrap 来完成大致相同的事情。
    • 这可以适应可选参数吗?这意味着包装函数中的可选参数在包装器中也是可选的。在此变体中,必须指定所有参数。
    • 你认为可以扩展来包装成员函数吗?我试图在模板中添加一个类型名 T 和一个 T *t 作为模板参数。然后通过 t->f(args...) 调用该函数,但它根本不起作用
    • 这在 C++17 中出现错误:“错误:未定义模板的隐式实例化”
    【解决方案2】:
    #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;
    }
    

    http://rextester.com/ZZD18334

    【讨论】:

    • 谢谢!我将其更改为已接受的答案,因为它比@TC 更短,仅使用函数而不是结构,使用std::forward,并且(根据意见)更容易理解,因为您没有使用模板参数专业化。
    • 虽然我不得不承认 std::forward 只是在那里,因为我尝试了一些可以在 MSVC 下编译的东西,然后想“啊,搞砸了,不会发生......但是嘿,保持前进”;-)
    • 为看起来像调用普通函数的包装器声明别名的正确方法是什么? using a_wrapped=wrapper&lt;decltype(&amp;foo),&amp;foo&gt;; 之类的东西(但这个不起作用)。
    • 如果函数重载,此方法将无法通过 WRAPIT 宏工作。
    • 它似乎也不适用于您通过operator() 调用的课程。顺便说一句,在 C++17 中,您应该使用 std::invoke_type_t&lt;&gt; insead 或 std::result_of&lt;&gt;::type
    【解决方案3】:

    您可以尝试类似的方法(丑陋,但有效)

    #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;
    }
    

    【讨论】:

      【解决方案4】:

      你可能需要类似的东西

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

      你可以像void (*funcPtr)(int) = Wrapper&lt;void(int)&gt;(&amp;someFunction);这样使用

      【讨论】:

      • 感谢您的回复,但这并不能满足我在原始帖子中的要求。在您的解决方案中,如果我调用funcPtr(),它会调用someFunction。我希望它在调用someFunction 之前和之后调用一个函数。
      • @Eric,现在我明白你想要什么,但不明白你为什么要找这么难的方法。没有捕获的 Lambda 函数可以转换为 c 函数指针:void (*funcPtr)(int) = [](int a){ /*smth*/}; - 在 lambda 中调用包装函数和之前/之后例程
      • 我一开始就试过了。我相信 lambdas 只有在没有捕获的情况下才能转换为函数指针,并且为了使其通用,我需要编写一个 lambda 来捕获它要调用的函数,但也许有一种方法可以使我错过的工作.
      【解决方案5】:

      我认为这将是做你想做的事的简洁方式:

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

      并像这样使用它:

      my_interface.func_a = wrapper(foo);
      my_interface.func_a(1, 3);
      

      【讨论】:

      • 感谢您的回复,但这并不能满足我的要求。我需要能够在调用包装函数之前和之后在包装函数中做一些工作(在本例中为foo)。因此,包装函数需要具有与被包装函数相同的函数签名
      • 我不确定您是否可以从模板参数(即Ts... Args)中检索签名,但您可以将它们保存为延迟调用,如here 所示,然后在Wrapper::operator() 之前和之后执行操作使用保存的参数调用底层函数
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多