检测c++ lambda是否可以转换为函数指针

Detect if C++ lambda can be converted to function pointer

本文关键字:转换 函数 指针 c++ lambda 是否 检测      更新时间:2023-10-16

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

__thiscall稍微昂贵一些,所以我希望尽可能避免它,并且我也希望避免必须根据lambda类型使用不同的调用生成函数。

我尝试了许多方法来检测lambda类型通过模板和SFINAE和所有失败。

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

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

任何想法?

编辑我想有一个解决方案,适用于vs 2013/2015, gcc和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 contextn", 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: %pn", 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特性:

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

这样它也可以与泛型lambdas一起工作。


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

如果你不知道签名,这里有一个方法是检查+是否触发隐式转换的替代方法。这个测试利用std::is_assignable测试并验证lambda是否可分配给具有与lambda的函数调用操作符相同签名的函数指针。(就像使用一元操作符+的测试一样,这不适用于泛型lambda。但是在c++ 11中没有通用的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++ , 叮当声+ +

非捕获lambda有一个非常有趣的属性:它们可以转换为适当的函数指针,但是当您对它们应用一元operator +时,它们也可以隐式地这样做。因此:

template<class...> using void_t = void;
template <class T, class = void>
struct has_capture : std::true_type {};
template <class T>
struct has_capture<T, void_t<decltype(+std::declval<T>())>> : std::false_type {};
int main() {
    auto f1 = []{};
    auto f2 = [&f1]{};
    static_assert(!has_capture<decltype(f1)>{}, "");
    static_assert( has_capture<decltype(f2)>{}, "");
}

您正在使用的HasConversion方法是从c++ 03延续下来的。这里的想法是使用test重载的不同返回类型(例如,一个返回char,另一个返回long),并检查返回类型的sizeof()是否与您期望的匹配。

然而,一旦我们使用c++ 11,就会有更好的方法。例如,我们可以使用void_t:
template <typename... >
using void_t = void;

写一个类型特征:

template <typename T, typename = void>
struct has_operator_plus : std::false_type { };
template <typename T>
struct has_operator_plus<T, 
    void_t<decltype(+std::declval<T>())>>
: std::true_type { };
int main() {
    auto x = []{ return 5; };
    auto y = [x]{ return x(); };
    std::cout << has_operator_plus<decltype(x)>::value << std::endl; // 1
    std::cout << has_operator_plus<decltype(y)>::value << std::endl; // 0
}