检测是否可以将C ++ lambda转换为函数指针 [英] Detect if C++ lambda can be converted to function pointer

查看:1703
本文介绍了检测是否可以将C ++ lambda转换为函数指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码生成汇编的JIT想法我正在努力。我使用元编程通过分析函数类型生成调用,然后生成正确的程序集来调用它。我最近想要添加lambda支持,lambdas有两个版本,非捕获(正常__cdecl函数调用)和捕获(__thiscall,成员函数调用与lambda对象作为上下文)。

I have some code that generates assembly for a JIT idea I'm working on. I use meta-programming to generate calls by analyzing the function type and then generating the correct assembly to call it. I recently wanted to add lambda support, and lambdas have two versions, non-capturing (normal __cdecl function call) and capturing (__thiscall, member-function call with the lambda object as context).

__ thiscall稍贵一点,所以我想尽可能避免它,我也想避免使用不同的调用生成函数,类型。

__thiscall is slightly more expensive so I'd like to avoid it whenever possible, and I'd also like to avoid having to use different call generation functions depending on the lambda type.

我尝试了很多方法来通过模板和SFINAE检测lambda类型,并且都失败了。

I tried many ways to detect the lambda type via templates and SFINAE and all failed.

非捕获lambdas有一个 :: operator function_type * ,可以使用它来将它们转换为函数指针,而捕获lambdas不。

Non-capturing lambdas have an ::operator function_type* which one can use to convert them to function pointers, while capturing lambdas don't.

相关C ++规范: http: //en.cppreference.com/w/cpp/language/lambda

任何想法?

编辑
我想要一个适用于vs 2013/2015,gcc和clang的解决方案

edit I'd like to have a solution that works for vs 2013/2015, gcc and clang

测试代码

#include <utility>

    //this doesn't work
    template < class C, class T >
    struct HasConversion {
        static int test(decltype(std::declval<C>().operator T*, bool()) bar) {
            return 1;
        }

        static int test(...) {
            return 0;
        }
    };

    template <class C>
    void lambda_pointer(C lambda) {
        int(*function)() = lambda;

        printf("Lambda function: %p without context\n", function);
    }

    template <class C>
    void lambda_pointer_ctx(C lambda) {
        int(C::*function)() const = &C::operator();

        void* context = &lambda;

        printf("Lambda function: %p with context: %p\n", function, context);
    }

    int main() {
        int a;

        auto l1 = [] {
            return 5;
        };

        auto l2 = [a] {
            return a;
        };


        //non capturing case

        //works as expected
        lambda_pointer(l1);

        //works as expected (ctx is meaningless and not used)
        lambda_pointer_ctx(l1);



        //lambda with capture (needs context)

        //fails as expected
        lambda_pointer(l1);

        //works as expected (ctx is a pointer to class containing the captures)
        lambda_pointer_ctx(l1);

        /*
        //this doesn't work :<
        typedef int afunct() const;

        HasConversion<decltype(l1), afunct>::test(0);
        HasConversion<decltype(l2), afunct>::test(0);
        */


        return 0;
    }


推荐答案

您希望将lambda转换为的函数,您可以利用 std :: is_assignable trait:

If you know the signature of a function you want your lambda to be converted to, you can leverage the std::is_assignable trait:

auto lambda = [] (char, double) -> int { return 0; };
using signature = int(char, double);
static_assert(std::is_assignable<signature*&, decltype(lambda)>::value, "!");

DEMO

这种方法也可以与通用lambdas一起使用。

This way it can work also with generic lambdas.


我想要一个解决方案,适用于vs 2013/2015,gcc和clang

I'd like to have a solution that works for vs 2013/2015, gcc and clang

如果你不知道签名,这里是一个替代方法来检查 + 触发隐式转换。这一个利用 std :: is_assignable 测试,并验证lambda是否可分配给与lambda的函数调用操作符具有相同签名的函数指针。 (就像使用一元运算符plus的测试,这不适用于通用lambdas。但是在C ++ 11中没有通用的lambdas)。

If you don't know the signature, here's an approach that is an alternative to checking whether + triggers an implicit conversion. This one exploits the std::is_assignable test and verifies whether a lambda is assignable to a function pointer with the same signature as the lambda's function call operator. (Just like a test with unary operator plus, this doesn't work with generic lambdas. But in C++11 there are no generic lambdas).

#include <type_traits>

template <typename T>
struct identity { using type = T; };

template <typename...>
using void_t = void;

template <typename F>
struct call_operator;

template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...)> : identity<R(A...)> {};

template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...) const> : identity<R(A...)> {};

template <typename F>
using call_operator_t = typename call_operator<F>::type;

template <typename, typename = void_t<>>
struct is_convertible_to_function
    : std::false_type {};

template <typename L>
struct is_convertible_to_function<L, void_t<decltype(&L::operator())>>
    : std::is_assignable<call_operator_t<decltype(&L::operator())>*&, L> {};

测试:

int main()
{
    auto x = [] { return 5; };
    auto y = [x] { return x(); };

    static_assert(is_convertible_to_function<decltype(x)>::value, "!");
    static_assert(!is_convertible_to_function<decltype(y)>::value, "!");
}

GCC VC ++ Clang ++

这篇关于检测是否可以将C ++ lambda转换为函数指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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