2016-01-24 43 views
33

我想檢查某個類型是否來自特定的命名空間。以下是我想出了:檢查某個類型是否來自特定的命名空間

#include <utility> 

namespace helper 
{ 
    template <typename T, typename = void> 
    struct is_member_of_sample : std::false_type 
    { 
    }; 

    template <typename T> 
    struct is_member_of_sample< 
     T, 
     decltype(adl_is_member_of_sample(std::declval<T>()))> : std::true_type 
    { 
    }; 
} 

namespace sample 
{ 
    template <typename T> 
    auto adl_is_member_of_sample(T &&) -> void; 
} 

// -- Test it 

namespace sample 
{ 
    struct X; 
} 

struct Y; 

static_assert(helper::is_member_of_sample<sample::X>::value, ""); 
static_assert(not helper::is_member_of_sample<Y>::value, ""); 

int main(){} 

這工作得很好,只要沒有人增加了adl_is_member_of_sample自己的命名空間(甚至是全局命名空間)。當然,我將不得不爲每個我想測試的名稱空間創建一個這樣的構造。

有沒有更好的方法來檢查編譯時,如果一個類型是從一個特定的命名空間?


理由或「爲什麼我要的是」:

在EDSL,我檢查型性狀在編譯時看到某些表達式是否有效。其中一些類型的特徵相當簡單:如果一個類有一個using is_numeric = void,那麼我將它視爲一個數字表達式。工作正常。

is_numeric雖然很通用。其他人也可以使用它。因此,我想通過檢查該特徵是否來自預期的命名空間來支持特徵。

+0

此解決方案頗具侵擾性。爲了把它放到一個庫中,我想我將創建一個定義名稱空間的宏,它在'namespace xxx {'後面設置'adl_is_member_of_sample()'。 – Lingxi

+0

順便說一句,有沒有一個名稱爲''is_member_of_sample'模板使用的技術? – Lingxi

+0

@靈溪SFINAE。 –

回答

1

有一個(編譯器特定的)的方式來測試一個類型是否是在一定的命名空間,但是我會讓你決定是否比你還是不是更好:

#include <utility> 
#include <type_traits> 

namespace helper 
{ 
class ctstring 
{ 
public: 
    constexpr ctstring(const char* string) : _string(string) 
    { 
    } 

    constexpr const char* c_str() const 
    { 
    return _string; 
    } 

    constexpr bool begins_with(const ctstring other) const 
    { 
    return !*other.c_str() || 
      (*_string && *_string == *other.c_str() && 
      ctstring(_string + 1).begins_with(other.c_str() + 1)); 
    } 

private: 
    const char* _string; 
}; 

template <typename T> 
constexpr bool is_type_in_namespace(const ctstring name) 
{ 
#if defined(_MSC_VER) 
#define PRETTY_FUNCTION_OFFSET_1 \ 
    (sizeof("void __cdecl helper::is_type_in_namespace<struct ") - 1) 
#define PRETTY_FUNCTION_OFFSET_2 \ 
    (sizeof("void __cdecl helper::is_type_in_namespace<class ") - 1) 

    return ctstring(__FUNCSIG__ + PRETTY_FUNCTION_OFFSET_1).begins_with(name) || 
     ctstring(__FUNCSIG__ + PRETTY_FUNCTION_OFFSET_2).begins_with(name); 

#undef PRETTY_FUNCTION_OFFSET_1 
#undef PRETTY_FUNCTION_OFFSET_2 
#elif defined(__clang__) 
    return ctstring(__PRETTY_FUNCTION__ + 
        (sizeof("bool helper::is_type_in_namespace(const " 
          "helper::ctstring) [T = ") - 
        1)) 
    .begins_with(name); 
#elif defined(__GNUC__) 
    return ctstring(__PRETTY_FUNCTION__ + 
        (sizeof("constexpr bool " 
          "helper::is_type_in_namespace(helper::ctstring) " 
          "[with T = ") - 
        1)) 
    .begins_with(name); 
#else 
#error "Your compiler is not supported, yet." 
#endif 
} 
} 

// -- Test it 

namespace sample 
{ 
struct True_X; 

class True_Y; 

template <typename> 
class True_T; 

template <typename A> 
using True_U = True_T<A>; 
} 

struct False_X; 

class False_Y; 

template <typename> 
class False_T; 

template <typename A> 
using False_U = False_T<A>; 

void test1() 
{ 
    static_assert(helper::is_type_in_namespace<sample::True_X>("sample::"), "1"); 
    static_assert(helper::is_type_in_namespace<sample::True_Y>("sample::"), "2"); 
    static_assert(helper::is_type_in_namespace<sample::True_T<int>>("sample::"), "3"); 
    static_assert(helper::is_type_in_namespace<sample::True_U<int>>("sample::"), "4"); 
    static_assert(!helper::is_type_in_namespace<False_X>("sample::"), "5"); 
    static_assert(!helper::is_type_in_namespace<False_Y>("sample::"), "6"); 
    static_assert(!helper::is_type_in_namespace<False_T<int>>("sample::"), "7"); 
    static_assert(!helper::is_type_in_namespace<False_U<int>>("sample::"), "8"); 
} 

namespace sample 
{ 
void test2() 
{ 
    static_assert(helper::is_type_in_namespace<True_X>("sample::"), "1"); 
    static_assert(helper::is_type_in_namespace<True_Y>("sample::"), "2"); 
    static_assert(helper::is_type_in_namespace<True_T<int>>("sample::"), "3"); 
    static_assert(helper::is_type_in_namespace<True_U<int>>("sample::"), "4"); 
    static_assert(!helper::is_type_in_namespace<::False_X>("sample::"), "5"); 
    static_assert(!helper::is_type_in_namespace<::False_Y>("sample::"), "6"); 
    static_assert(!helper::is_type_in_namespace<::False_T<int>>("sample::"), "7"); 
    static_assert(!helper::is_type_in_namespace<::False_U<int>>("sample::"), "8"); 
} 

namespace inner 
{ 
void test3() 
{ 
    static_assert(helper::is_type_in_namespace<::sample::True_X>("sample::"), "1"); 
    static_assert(helper::is_type_in_namespace<::sample::True_Y>("sample::"), "2"); 
    static_assert(helper::is_type_in_namespace<::sample::True_T<int>>("sample::"), "3"); 
    static_assert(helper::is_type_in_namespace<::sample::True_U<int>>("sample::"), "4"); 
    static_assert(!helper::is_type_in_namespace<::False_X>("sample::"), "5"); 
    static_assert(!helper::is_type_in_namespace<::False_Y>("sample::"), "6"); 
    static_assert(!helper::is_type_in_namespace<::False_T<int>>("sample::"), "7"); 
    static_assert(!helper::is_type_in_namespace<::False_U<int>>("sample::"), "8"); 
} 
} 
} 

void test4() 
{ 
    using namespace sample; 

    static_assert(helper::is_type_in_namespace<True_X>("sample::"), "1"); 
    static_assert(helper::is_type_in_namespace<True_Y>("sample::"), "2"); 
    static_assert(helper::is_type_in_namespace<True_T<int>>("sample::"), "3"); 
    static_assert(helper::is_type_in_namespace<True_U<int>>("sample::"), "4"); 
} 

int main(int argc, char* argv[]) 
{ 
    test1(); 
    sample::test2(); 
    sample::inner::test3(); 
    test4(); 
    return 0; 
} 

我測試了這個MSVC2015和一些隨機的在線Clang編譯器和GCC 6.1.0。

思想:

  • 測試接受來自命名空間樣品和任何子名字空間類和結構。
  • 它不會遭受您的解決方案的缺點
  • 您可能想要構建std :: decay_t以刪除CV限定符。
  • 顯然代碼需要> = C++ 14 編輯:沒有任何更多,C++ 11是足夠
  • 沒有人喜歡宏編輯:除去了大部分的宏
  • 的代碼不是非常便攜並且很可能需要某些編譯器和編譯器版本的附加分支。它是由你的要求,如果該解決方案是可以接受的

編輯:重構代碼,使其更清晰,並添加GCC支持。此外,要測試的名稱空間現在可以作爲參數傳遞

+0

這看起來很有希望。我需要一些時間來思考。 – Rumburak

+0

太棒了!這超出了我的期望。如果有一天需要其他編譯器,現在路徑應該非常清晰。確實非常酷的想法。 – Rumburak

-1

不幸的是,這種技術只適用於非模板類型。對於模板類型,ADL還會檢查模板參數的名稱空間。然後它收集一個類或函數模板的列表(取決於從中調用ADL的上下文)並選擇最佳候選。

更好的解決方案是將顯式檢查添加到要檢查名稱空間中的成員身份的類型。例如,您可以從某個類派生所有類型,或者爲每個類添加一個特殊成員。這會更清晰,更易於理解和維護解決方案。

+0

正如在評論中所討論的:一個有趣的問題是「X是名稱空間Y的成員」是什麼意思?因此ADL可能會或可能不會成爲問題。另一方面,繼承或標籤並不是問題的答案。這將是「我如何標記一堆模板或類」的答案?除了那些我寫的,它不適用於任何課程。 – Rumburak

-3
std::cout << "I am " << __PRETTY_FUNCTION__ << " function." << std::endl; 

應打印

namespace::class::function. 
+2

當然,但是在編譯時計算給定類型的命名空間有什麼關係? – Rumburak

相關問題