2016-01-24 14 views
33

Vorrei verificare se un tipo proviene da un determinato spazio dei nomi. Ecco quello che mi si avvicinò con:Verificare se un tipo proviene da un determinato spazio dei nomi

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

Questo funziona bene finché non si aggiunge adl_is_member_of_sample al proprio spazio dei nomi (o anche il namespace globale). E, naturalmente, dovrei creare un tale costrutto per ogni spazio dei nomi che voglio testare.

Esiste un modo migliore per verificare in fase di compilazione se un tipo proviene da un determinato spazio dei nomi?


Razionale o "Perché dovrei volere che":

In un EDSL, sto controllando tratti del tipo in fase di compilazione per vedere se certe espressioni sono validi o meno. Alcuni di questi caratteri sono abbastanza semplici: se una classe ha un using is_numeric = void, allora lo considero come un'espressione numerica. Funziona bene.

is_numeric è piuttosto generico. Altri potrebbero usarlo pure. Ho quindi pensato di supportare il tratto con un controllo che il tipo provenga dallo spazio dei nomi previsto.

+0

Questa soluzione è piuttosto invadente. Per inserire questo in una libreria, credo che creerò una macro per definire gli spazi dei nomi, che imposta 'adl_is_member_of_sample()' in posizione dopo 'namespace xxx {'. – Lingxi

+0

A proposito, c'è un nome per la tecnica che hai usato con il modello 'is_member_of_sample'? – Lingxi

+0

@Lingxi SFINAE. –

risposta

1

C'è un modo (compilatore specifico) per verificare se un tipo è in un certo spazio dei nomi, ma ti lascio decidere se è migliore della tua o no :

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

ho provato questo per MSVC2015 e un po 'a caso compilatore Clang on-line e GCC 6.1.0.

Pensieri:

  • Il test accetta le classi e le strutture da campioni spazio dei nomi e qualsiasi sub-namespace.
  • Non presenta gli svantaggi della soluzione
  • Si potrebbe voler creare in std :: decay_t per rimuovere i qualificatori di CV.
  • Ovviamente il codice richiede> = C++ 14 Edit: Non più, C++ 11 è abbastanza
  • nessuno piace macro Edit: rimosso la maggior parte delle macro
  • il codice non è molto portabile e molto probabilmente ha bisogno di rami aggiuntivi per alcuni compilatori e versioni del compilatore.Spetta alle vostre esigenze se la soluzione è accettabile

Edit: codice di refactoring per renderlo più chiaro e aggiunto il supporto GCC. Inoltre, lo spazio dei nomi da testare può ora essere passato come parametro

+0

Questo sembra abbastanza promettente. Mi ci vorrà un po 'di tempo per pensarci. – Rumburak

+0

Fantastico! Questo è oltre ciò che speravo. Se altri compilatori dovessero essere necessari un giorno, il percorso dovrebbe essere molto chiaro ora. Davvero un'ottima idea. – Rumburak

-1

Sfortunatamente questa tecnica funziona solo per i tipi non di modello. Per i tipi di modello, ADL controlla anche gli spazi dei nomi degli argomenti del modello. Quindi raccoglie un elenco di modelli di classi o funzioni (a seconda del contesto in cui viene richiamata ADL) e seleziona il miglior candidato.

Una soluzione migliore sarebbe aggiungere un controllo esplicito ai tipi di appartenenza in uno spazio dei nomi che si desidera controllare. Ad esempio, è possibile derivare tutti i tipi da una determinata classe o aggiungere un membro speciale a ciascuno di essi. Questo sarebbe molto più chiaro, più facile da capire e mantenere la soluzione.

+0

Come discusso nei commenti: Una domanda interessante è che cosa significa "X è un membro del namespace Y"? Quindi ADL può o non può essere un problema. L'ereditarietà o l'etichettatura d'altra parte non è una risposta alla domanda. Sarebbe una risposta a "Come posso taggare un gruppo di modelli o classi"? E non funzionerebbe per nessuna classe tranne quelle che scrivo. – Rumburak

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

dovrebbe stampare

namespace::class::function. 
+2

Certo, ma cosa c'entra con lo scoprire lo spazio dei nomi di un determinato tipo al momento della compilazione? – Rumburak

Problemi correlati