是否可以检测 C++ 中的命名空间成员资格?

2022-01-11 00:00:00 macros language-lawyer reflection c++ c++11

对于 C++ 类型,<type_traits> 标头为我们提供了许多有用的编译时反射功能.例如.std::is_base_of<B, D>::value 在编译时确定 B 是否是 D 的基类.

For C++ types, the <type_traits> header gives us many useful compile-time reflection capabilities. E.g. std::is_base_of<B, D>::value determines at compile-time whether B is a base class of D.

我想知道是否可以按照类似的方式检测命名空间成员资格?例如.给定一个类型为 T 的命名空间 N,有没有办法确定 T 是否包含在 NIS_NAMESPACE_MEMBER_OF(T,N) 形式的宏表达式.

I wonder if it would be possible to detect namespace membership along similar lines? E.g. given a namespace N with a type T, is there a way to determine whether T is contained within N using a macro expression of the form IS_NAMESPACE_MEMBER_OF(T,N).

我更喜欢通过任何类型的 SFINAE/ADL 类型的技巧来获得编译时答案.或者,如果不可能,某种推理为什么标准不允许这样做.

I'd prefer a compile-time answer through any sort of SFINAE / ADL type of trick. Or, if it isn't possible, some sort of reasoning why the Standard would not allow this.

一个不可移植的运行时破解方法是为 N 正则表达式 typeid(T).name(),但这相当乏味且不适合编译-时间.

A non-portable and run-time hack would be to regex typeid(T).name() for N, but this is rather tedious and not at compile-time.

EDIT1:正如 K-ballo 所指出的,命名空间不能用作模板参数,因此类型特征似乎是不可能的.

EDIT1: as pointed out by K-ballo, a namespace cannot be used as a template parameter so a type-trait seems impossible.

EDIT2:这是 K-ballo 暗示的骨架:在那里可以(或不能?)进行哪些漂亮的测试?

EDIT2: here's the skeleton as hinted to by K-ballo: what nifty test can (or cannot?) be cooked up there?

#define IS_NAMESPACE_MEMBER_OF(T, N) 
                                     
// global declaration                
void test(T);                        
                                     
// namespace declaration             
namespace N {                        
    void test(T);                    
}                                    
                                     
// some clever name lookup / sizeof / SFINAE test!     

推荐答案

您可以测试 namespace 是否可以通过 ADL 从类型中访问(由编译器查找).

You can test whether the namespace is accessible (looked up by the compiler), via ADL, from the type.

假设我们要检查类型 A 是否来自 namespace foo,我们可以尝试使用只出现在 foo 中的类型(例如通用函数 foo::foo_inner_func(T&&))通过使用 A 来查看我们是否到达命名空间.如果我们在 SFINAE 上下文中执行此操作,则可以得到我们正在寻找的答案:namespace foo 是否可以通过 A 访问.

Suppose that we want to check if type A comes from namespace foo, we can try to use a type that appears only in foo (e.g. a generic function foo::foo_inner_func(T&&)) via the use of A to see if we reach the namespace. If we do it in a SFINAE context then this can result with the answer we are looking for: whether namespace foo is accessible via A.

在许多情况下,这将是类型是否属于此命名空间的答案,但在某些情况下,它可能会识别 ADL 可访问的命名空间,即使该类型并非来自此命名空间.例如,如果 A 来自 namespace foo 并且从 A 派生的 B 来自另一个命名空间,B 仍然看见";foo 通过 ADL.还有 std::vector<A> 看见"foo 通过 ADL(也看到"std 通过 ADL).

In many cases that would be the answer of whether the type belongs to this namespace, but in some cases it may identify a namespace as accessible by ADL even though the type doesn't come from this namespace. For example if A is from namespace foo and B which derives from A is from another namespace, B still "sees" foo via ADL. Also std::vector<A> "sees" foo via ADL (and also "sees" std via ADL).

这里已经介绍了使用 ADL 的想法:检查如果一个类型来自特定的命名空间.

The idea of using ADL was already presented here: Check if a type is from a particular namespace.

这是允许查询任何类型(几乎)任何命名空间(几乎)的宏版本:

Here is the macro version that allows querying any type (almost) for any namespace (almost):

#define create_ns_checker(ns) 
namespace ns { 
    template <typename T> 
    constexpr std::true_type ns##FindmeNsADLHelper(T&&); 
} 
namespace ns##_type_traits { 
    class ns##SecondBestMatchType {}; 
    class ns##BestExactMatchType : public ns##SecondBestMatchType {}; 
    namespace helpers { 
        template <typename T> 
        auto TestNs(ns##_type_traits::ns##BestExactMatchType) 
               -> decltype(ns##FindmeNsADLHelper(std::declval<T>())); 
        template <typename T> 
        auto TestNs(ns##_type_traits::ns##SecondBestMatchType) 
               -> std::false_type; 
    } 
    template <typename T> 
    constexpr bool ns##IsFindmeNs() { 
        return decltype(helpers::TestNs<std::decay_t<T>> 
                           (ns##BestExactMatchType{}))::value; 
    } 
}

#define is_in_ns(Type, ns) 
(ns##_type_traits::ns##IsFindmeNs<Type>())

一个小型打印工具:

#define print_is_in_ns(Type, ns) 
[]() { 
    std::cout << #Type << " in " << #ns << ": "  
              << is_in_ns(Type, ns) << std::endl; 
}()

使用宏创建检查器:

create_ns_checker(findme)
create_ns_checker(other)
create_ns_checker(std)

检查以下类型:

namespace other {
    struct B {};
}

struct C {};

namespace findme {
    struct A {};

    namespace inner {
        struct A {};
    }
    create_ns_checker(inner)
}

在 findme 上下文中测试:

Testing in findme context:

namespace findme {
    void test() {
        using namespace other;
        // add the below in and the results change, as it should!
          // using inner::A;
        using std::string;
        std::cout << std::boolalpha;
        print_is_in_ns(int, std);          // false
        print_is_in_ns(string, std);       // true
        print_is_in_ns(A, findme);         // true
        print_is_in_ns(A, inner);          // false
        print_is_in_ns(inner::A, findme);  // false
        print_is_in_ns(inner::A, inner);   // true
        print_is_in_ns(B, findme);         // false
        print_is_in_ns(B, other);          // true
        print_is_in_ns(C, findme);         // false
    }
}

在 main 中测试:

Testing in main:

int main() {
    using std::string;
    using findme::A;
    std::cout << std::boolalpha;
    print_is_in_ns(int, std);                 // false
    print_is_in_ns(string, std);              // true
    print_is_in_ns(string, findme);           // false
    print_is_in_ns(findme::A, findme);        // true
    print_is_in_ns(findme::inner::A, findme); // false
    print_is_in_ns(other::B, findme);         // false
    print_is_in_ns(other::B, other);          // true
    print_is_in_ns(C, findme);                // false
    print_is_in_ns(std::vector<A>, findme); // falsely says true :-(
    print_is_in_ns(std::vector<A>, std);      // true
    std::cout << "-----------------" << std::endl;
    findme::test();
}

代码:https://godbolt.org/z/8Ed89v

相关文章