模板函数,可以接受lambda或函数指针,并推导传递给另一个模板的参数 [英] Template function that can take lambda or function pointer and deduce arguments for passing to another template

查看:165
本文介绍了模板函数,可以接受lambda或函数指针,并推导传递给另一个模板的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我维护一个开源无锁线程库,专为高速并行循环展开而设计,用于几种商业视频游戏。它具有令人难以置信的低开销,大约8个时钟用于创建消息和大约500(每个线程,包括延迟)整个分派和远程执行开销。我说这首先解释为什么我不简单地使用使用std :: function和bind。

I maintain an open-source lockless threading library designed for high speed parallel loop unrolling which is used in a couple commercial video games. It has incredibly low-overhead, around 8 clocks for creating a message and around 500 (per thread, including latency) for the entire dispatch and remote execute overhead. I say this first to explain why I don't simply use use std::function and bind.

该库将函数调用和函数调用的参数打包到一个消息中(Functor<>类型)。然后使用参数的副本远程调用它。

The library packages a function call and the arguments to the function call in a message (of type Functor<>). Then calls it remotely with copies of the arguments.

我最近使用STL样式模板元编程而不是最初使用的日期C风格的宏重写了库用过的。令人惊讶的是,这只增加了两个tick的开销,但我不知道如何创建一个包含lambda和函数指针的包装函数。

I have recently rewritten the library to package remote calls using STL style template meta programming rather than the dated C style macros it originally used. Amazingly this only increased the overhead by two ticks but I cannot figure out how to create a packaging function that takes both lambdas and function pointers.

希望的用法:

CreateFunctor([](int a, int b){doSomething(a, b)}, 1, 2);

CreateFunctor(&doSomething, 1, 2);

目前,我必须将这些案例分成两个独立的函数(CreateFunctor和CreateFunctorLambda)。如果我可以结合它们,我可以将我的包装和调度阶段合并成一个整洁的函数调用并简化API。

Currently I have to divide these cases into two separate functions, (CreateFunctor and CreateFunctorLambda). If I could combine them I would be able to merge my packaging and dispatching stages into one neat function call and simplify the API.

问题是, lambda的参数似乎不能与用于推导函数的参数的代码共享模板覆盖。我尝试使用enable_if它仍然执行lambda版本的:: *部分,并导致编译器错误与函数指针。

The problem is that the code for deducing the arguments of a lambda cannot seem to share a template override with the code for deducing the arguments of a function. I've tried using enable_if and it still executes the ::* portion of the lambda version and causes compiler errors with function pointers.

相关代码段:

template <typename... Arguments>
inline Functor<Arguments...> CreateFunctor(void(*func)(Arguments...))
{
    return Functor<Arguments...>(func);
};

template <typename... Arguments>
inline Functor<Arguments...> CreateFunctor(void(*func)(Arguments...), Arguments ... arg)
{
    Functor<Arguments...> ret(func);
    ret.Set(arg...);
    return ret;
};

// template to grab function type that lambda can be cast to
// from http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda
template <class T>
struct deduce_lambda_arguments
    : public deduce_lambda_arguments<typename std::enable_if<std::is_class<T>::value, decltype(&T::operator())>::type>
{};

template <class ClassType, typename... Args>
struct deduce_lambda_arguments<void(ClassType::*)(Args...) const>
    // we specialize for pointers to member function
{
    typedef void(*pointer_cast_type)(Args...);
    typedef Functor<Args...> functor_type;
};

template <typename F, typename... Args>
inline auto CreateFunctorLambda(F f, Args... arg) -> typename deduce_lambda_arguments<F>::functor_type
{
    deduce_lambda_arguments<F>::functor_type ret((deduce_lambda_arguments<F>::pointer_cast_type) f);
    ret.Set(arg...);
    return ret;
};

template <typename F>
inline auto CreateFunctorLambda(F f) -> typename deduce_lambda_arguments<F>::functor_type
{
    return deduce_lambda_arguments<F>::functor_type((deduce_lambda_arguments<F>::pointer_cast_type) f);
};


推荐答案

诀窍是确保你从不评估& T :: operator()用于那些你想要的支持,但是不是lambdas。一种方法是添加一个额外的模板参数,并专门用于:

The trick is to ensure that you never evaluate &T::operator() for things-that-you-want-to-support-but-are-not-lambdas. One way to do this is adding an extra template parameter and specializing on that:

template <class T, bool = std::is_class<T>::value>
struct compute_functor_type
    : public compute_functor_type<decltype(&T::operator())>
{};

template <class ClassType, typename... Args>
struct compute_functor_type<void(ClassType::*)(Args...) const, false>
{
    typedef void(*pointer_cast_type)(Args...);
    typedef Functor<Args...> functor_type;
};

template <class ClassType, typename... Args>
struct compute_functor_type<void(ClassType::*)(Args...), false>
{
    typedef void(*pointer_cast_type)(Args...);
    typedef Functor<Args...> functor_type;
};

template <typename... Args>
struct compute_functor_type<void(*)(Args...), false>
{
    typedef void(*pointer_cast_type)(Args...);
    typedef Functor<Args...> functor_type;
};


template <typename F, typename... Args>
inline auto CreateFunctor(F f, Args... arg) 
         -> typename compute_functor_type<F>::functor_type
{
    typename compute_functor_type<F>::functor_type ret((typename compute_functor_type<F>::pointer_cast_type) f);
    ret.Set(arg...);
    return ret;
};

template <typename F>
inline auto CreateFunctor(F f) -> typename compute_functor_type<F>::functor_type
{
    return typename compute_functor_type<F>::functor_type((typename compute_functor_type<F>::pointer_cast_type) f);
};






不必使用MSVC的人:


Saving the (way shorter) original approach here for people who don't have to use MSVC:

因为你只关心无捕获的非通用lambda,并将它们转换为函数指针,这很容易。

Since you only care about captureless non-generic lambdas and convert them to function pointers anyway, this is easy.

从函数指针创建匹配的 Functor 类型:

Create a matching Functor type from a function pointer:

template<class... Args>
Functor<Args...> make_functor(void (*f)(Args...)) { return {f}; }

并通过一元函数强制转换为函数指针 + 运算符:

And force conversion to function pointer via the unary + operator:

template <class F>
inline auto CreateFunctor(F f) -> decltype(make_functor(+f))
{
    return make_functor(+f);
}

template <class F, typename... Arguments>
inline auto CreateFunctor(F f, Arguments ... arg) -> decltype(make_functor(+f))
{
    auto ret = make_functor(+f);
    ret.Set(arg...);
    return ret;
}

这篇关于模板函数,可以接受lambda或函数指针,并推导传递给另一个模板的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆