2016-01-24 5 views
33

タイプが特定のネームスペースのものかどうかを確認したいと思います。これは、限り誰もが自分の名前空間(あるいはグローバル名前空間)へadl_is_member_of_sampleを追加しないと正常に動作しますタイプが特定のネームスペースのものかどうか確認してください

#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(){} 

:ここに私が思い付いたものです。もちろん、テストしたい名前空間ごとにそのような構造を作成する必要があります。

タイプが特定の名前空間のものであるかどうかをコンパイル時に確認する方法はありますか?


理由または「なぜ私が欲しいでしょうが、その」:

EDSLでは、私は特定の表現が有効であるかどうかを確認するために、コンパイル時に型の特徴をチェックしています。これらの型特性のいくつかはかなりシンプルです:クラスにusing is_numeric = voidがある場合、それを数値式として扱います。正常に動作します。

is_numericはかなり一般的です。他の人もそれを使うかもしれない。そこで私は、型が予想される名前空間のものであるかどうかをチェックすることで、その特性を裏付けることを考えました。

+0

このソリューションは非常に邪魔です。これをライブラリに入れるために、namespaceを定義するためのマクロを作成すると思います。 'namespace xxx {'の後ろに 'adl_is_member_of_sample()'を設定します。 – Lingxi

+0

ちなみに、is_member_of_sampleテンプレートで使用したテクニックの名前はありますか? – Lingxi

+0

@ Lingxi 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でテストしました。

思考:

  • テストでは、名前空間のサンプルおよび任意のサブ名前空間のクラスや構造体を受け入れます。
  • 解決策の欠点はありません
  • CV修飾子を削除するには、std :: decay_tをビルドします。
  • は明らかにコードが必要です> = 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

関連する問題