Is it possible to detect namespace membership in C++?

前端 未结 2 745
小鲜肉
小鲜肉 2020-12-19 16:46

For C++ types, the header gives us many useful compile-time reflection capabilities. E.g. std::is_base_of::value de

相关标签:
2条回答
  • 2020-12-19 17:38

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

    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.

    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).

    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>())
    

    A small printing utility:

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

    Creating the checkers with the macro:

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

    Checking it for the following types:

    namespace other {
        struct B {};
    }
    
    struct C {};
    
    namespace findme {
        struct A {};
    
        namespace inner {
            struct A {};
        }
        create_ns_checker(inner)
    }
    

    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
        }
    }
    

    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();
    }
    

    Code: https://godbolt.org/z/8Ed89v

    0 讨论(0)
  • 2020-12-19 17:38

    A namespace is not a valid template parameter, so it could never be a class trait. Perhaps you can do something obscure with macros though. You could maybe inject functions in the test namespace and use ADL together with a sizeof/decltype trick to see which overload gets picked.

    0 讨论(0)
提交回复
热议问题