创建一个特征来检测C ++中的闭包类型

时间:2018-04-06 11:35:57

标签: c++ lambda c++17 template-meta-programming typetraits

标准定义了闭包类型如下:

  

[expr.prim.lambda.closure]   lambda表达式的类型(也是闭包的类型)   object)是一个唯一的,未命名的非联合类类型,称为闭包   类型,其属性如下所述。 [...]

是否有办法创建一个类型特征来检测闭包类型:

template <class T> 
struct is_closure {
    static constexpr bool value = /* something */
};

template <class T>
inline constexpr bool is_closure_v = is_closure<T>::value;

如果在纯粹的标准C ++中不可行,那么欢迎使用clang ++ - 5.X和g ++ - 7.X的内部函数。

编辑:使用某些编译器宏获取lambda类型的名称然后进行一些constexpr字符串处理是不是可行?

1 个答案:

答案 0 :(得分:6)

我很想看看是否真的有一个激励性的用例需要知道,具体来说,一个类型是否是一个闭包。我不确定是否有一个用例来确定是否一般一个类型可以用一些参数调用 - 而不是特别是可调用的特定的一组。这种打击让我觉得是智力手淫。

那说,我喜欢智力手淫!关闭类型的好处是它们不可命名。但是我们仍然有像__PRETTY_FUNCTION__这样的钩子给我们字符串形式的名称,因此它们必须是可打印的某种程度上 -  并且某种程度上必须与普通名称区分开来。我们来看看如何:

#include <iostream>

template <typename T>
void print(T) {
    std::cout << __PRETTY_FUNCTION__ << '\n';
}

namespace N { 
  void foo() {
    print([]{ return 1; }); 
  }
}

int main() {
    print([]{ return 1; }); 
    print([](auto, auto&&...){ return 2; }); 
    N::foo();
}

gcc打印:

void print(T) [with T = main()::<lambda()>]
void print(T) [with T = main()::<lambda(auto:1, auto:2&&, ...)>]
void print(T) [with T = N::foo()::<lambda()>]

clang print:

void print(T) [T = (lambda at bar.cxx:15:11)]
void print(T) [T = (lambda at bar.cxx:16:11)]
void print(T) [T = (lambda at bar.cxx:10:11)]

在这两种情况下,这些字符串都不能从非闭包类型中生成。我们不能拥有以<(gcc)开头的类型,并且我们不能使用lambda at(clang)这样的空格。因此,我们可以通过构建只查找适当子字符串的类型特征来利用它:

constexpr bool is_equal(const char* a, const char* b) {
    for (; *a && *b; ++a, ++b) {
        if (*a != *b) return false;
    }   
    return *b == '\0';
}

constexpr bool has_substr(const char* haystack, const char* needle) {
    for(; *haystack; ++haystack) {
        if (is_equal(haystack, needle)) {
            return true;
        }
    }   
    return false;
}

template <typename T>
constexpr bool closure_check() {
    return has_substr(__PRETTY_FUNCTION__,
#ifdef __clang__
            "(lambda at"
#else
            "::<lambda"
#endif
            );
}

template <typename T>
constexpr bool is_closure_v = closure_check<T>();

template <typename T>
struct is_closure
    : std::integral_constant<bool, is_closure_v<T>>
{ };

我没有经过彻底的测试,所以我不能自信地说它既没有误报也没有否定,但这似乎是正确的方法。

编辑:正如Jarod42指出的那样,目前这还不够,因为它至少没有考虑闭包类型作为模板参数:demo。这条线可能还有其他误报。

虽然,重申一下,我不清楚为什么这样的事情会有用。