7

so che è banale per verificare se un tipo è un'istanza di una classe template prendendo TIPO parametri, come spiegato qui: How can I check if a type is an instantiation of a given class template?C++ 11: Come verificare se un tipo è un'istanza di un dato modello di classe di parametri "eterogenei" NON TIPO?

Ma ... è possibile avere una variadic "is_instantiation_of__ntp < .. .> "(NTP per non tipo Params) che accetta i modelli con un numero qualsiasi di eterogeneoparametri NON-TYPE? Per esempio:

template<char*, bool, long, size_t, char> 
struct many_hetero_nontype_params_example {}; 

char HELLO_WORLD[] = "hello world"; 
using ManyHeteroNontypeParamsEx = many_hetero_nontype_params_example<HELLO_WORLD, false, -16, 777, 'x'>; 

ed essere in grado di usarlo come segue:

is_instantiation_of__ntp<char*, bool, long, size_t, char, many_hetero_nontype_params_example, ManyHeteroNontypeParamsEx>::value 

So che questo può essere implementato facilmente per elenchi di parametri NON-tipo che sono 1) "omogenei" (valori dello stesso tipo), o 2) pochi parametri (tale che una soluzione non variabile sia pratica). Ho anche scritto un banco di prova che dimostrano questi casi particolari (compilato con GCC 4.7.0) per dare una migliore idea di cosa sto parlando:

namespace test__is_instantiation_of__ 
{ 
    // is_instantiation_of 
    template< template<typename...> class Template, typename T > 
    struct is_instantiation_of : std::false_type {}; 
    template< template<typename...> class Template, typename... Args > 
    struct is_instantiation_of< Template, Template<Args...> > : std::true_type {}; 

    // is_instantiation_of__homogeneous_nontype_params__ 
    template< typename NTP, template<NTP...> class Template, typename T > 
    struct is_instantiation_of__homogeneous_nontype_params__ : std::false_type {}; 
    template< typename NTP, template<NTP...> class Template, NTP... Args > 
    struct is_instantiation_of__homogeneous_nontype_params__< NTP, Template, Template<Args...> > : std::true_type {}; 

    // is_instantiation_of__fixedcount_nontype_params__ 
    template< typename NTP1, typename NTP2, template<NTP1, NTP2> class Template, typename T > 
    struct is_instantiation_of__fixedcount_nontype_params__ : std::false_type {}; 
    template< typename NTP1, typename NTP2, template<NTP1, NTP2> class Template, NTP1 v1, NTP2 v2 > 
    struct is_instantiation_of__fixedcount_nontype_params__< NTP1, NTP2, Template, Template<v1, v2> > : std::true_type {}; 

    // type_params_example 
    template<typename T1, typename T2, typename T3> 
    struct type_params_example {}; 

    // homogeneous_nontype_params_example 
    template<bool B1, bool B2, bool B3, bool B4> 
    struct homogeneous_nontype_params_example {}; 

    // fixedcount_nontype_params_example 
    template<long L, char C> 
    struct fixedcount_nontype_params_example {}; 

    using /*.........*/ TypeParamsEx = /*..........*/ type_params_example<std::string, std::tuple<long, void*>, double>; 
    using HomogenousNontypeParamsEx = homogeneous_nontype_params_example<true, false, true, false>; 
    using FixedCountNontypeParamsEx = fixedcount_nontype_params_example<777, 'x'>; 

    void run() 
    { 
     using std::cout; 
     using std::endl; 

     if (is_instantiation_of<type_params_example, TypeParamsEx>::value) { 
      cout << "[TypeParamsEx] specializes [type_params_example]" << endl; 
     } 
     if (is_instantiation_of<type_params_example, HomogenousNontypeParamsEx>::value) { 
      cout << "[HomogenousNontypeParamsEx] specializes [type_params_example]" << endl; 
     } 
     if (is_instantiation_of<type_params_example, FixedCountNontypeParamsEx>::value) { 
      cout << "[FixedCountNontypeParamsEx] specializes [type_params_example]" << endl; 
     } 

     if (is_instantiation_of__homogeneous_nontype_params__<bool, homogeneous_nontype_params_example, TypeParamsEx>::value) { 
      cout << "[TypeParamsEx] specializes [homogeneous_nontype_params_example]" << endl; 
     } 
     if (is_instantiation_of__homogeneous_nontype_params__<bool, homogeneous_nontype_params_example, HomogenousNontypeParamsEx>::value) { 
      cout << "[HomogenousNontypeParamsEx] specializes [homogeneous_nontype_params_example]" << endl; 
     } 
     if (is_instantiation_of__homogeneous_nontype_params__<bool, homogeneous_nontype_params_example, FixedCountNontypeParamsEx>::value) { 
      cout << "[FixedCountNontypeParamsEx] specializes [homogeneous_nontype_params_example]" << endl; 
     } 

     if (is_instantiation_of__fixedcount_nontype_params__<long, char, fixedcount_nontype_params_example, TypeParamsEx>::value) { 
      cout << "[TypeParamsEx] specializes [fixedcount_nontype_params_example]" << endl; 
     } 
     if (is_instantiation_of__fixedcount_nontype_params__<long, char, fixedcount_nontype_params_example, HomogenousNontypeParamsEx>::value) { 
      cout << "[HomogenousNontypeParamsEx] specializes [fixedcount_nontype_params_example]" << endl; 
     } 
     if (is_instantiation_of__fixedcount_nontype_params__<long, char, fixedcount_nontype_params_example, FixedCountNontypeParamsEx>::value) { 
      cout << "[FixedCountNontypeParamsEx] specializes [fixedcount_nontype_params_example]" << endl; 
     } 
    } 
} 

Come previsto, l'uscita si ottiene è:

[TypeParamsEx] specializes [type_params_example] 
[HomeogenousNonTypeParamsEx] specializes [homogeneous_nontype_params_example] 
[FixedCountNonTypeParamsEx] specializes [fixedcount_nontype_params_example] 

Il problema è nessuno di quei modelli funziona per molti_hetero_nontype_params_example (sopra). I.e: un singolo, variadico "is_instantiation_of__ntp" che accetta i template con un numero qualsiasi di parametri non-tipo eterogenei.

Penso che se i modelli primari non avessero bisogno di avere i pacchetti di parametri alla fine dell'elenco dei parametri del modello, questo sarebbe facile da implementare. O se fosse possibile usare un approccio wrapper-struct/nested-struct. Qui sono i miei (non) tentativi:

namespace test__is_instantiation_of__nontypes__ 
{ 
    template<char*, bool, long, size_t, char> 
    struct many_hetero_nontype_params_example {}; 

    char HELLO_WORLD[] = "hello world"; 
    using ManyHeteroNontypeParamsEx = many_hetero_nontype_params_example<HELLO_WORLD, false, -16, 777, 'x'>; 

    /* 
    * is_instantiation_of__nontypes_v1__ (version 1) 
    * if uncommented, syntax error as expected ... 
    * error: parameter pack 'NTPs' must be at the end of the template parameter list 
    * error: parameter pack argument 'NTPs ...' must be at the end of the template argument list 
    */ 
    //template< typename... NTPs, template<NTPs...> class Template, typename T > 
    //struct is_instantiation_of__nontypes_v1__ : std::true_type {}; 
    //template< typename... NTPs, template<NTPs...> class Template, NTPs... NonTypeArgs > 
    //struct is_instantiation_of__nontypes_v1__< NTPs..., Template, Template<NonTypeArgs...> > : std::true_type {}; 

    /* 
    * is_instantiation_of__nontypes_v2__ (version 2) 
    * no syntax error (but see instantiation errors below) 
    */ 
    template<typename... NTPs> 
    struct is_instantiation_of__nontypes_v2__ 
    { 
     template< template<NTPs...> class Template, typename T > 
     struct impl : std::false_type {}; 

     template< template<NTPs...> class Template, NTPs... NonTypeArgs > 
     struct impl< Template, Template<NonTypeArgs...> > : std::true_type {}; 
    }; 

    void run() 
    { 
     /* 
     * uncommented since "v1" template has syntax error, but this is how it would be used ... 
     */ 
     //if (is_instantiation_of__nontypes_v1__<char*, bool, long, size_t, char, many_hetero_nontype_params_example, ManyHeteroNontypeParamsEx>::value) { 
     // std::cout << "yes" << std::endl; 
     //} 

     /* 
     * "v2" template has no syntax error, but the following attempt to use it results in these errors ... 
     * 
     * error: type/value mismatch at argument 1 in template parameter list for 'template<class ... NTPs> template<template<template<NTPs ...<anonymous> > class Template, class T> template<class ... NTPs> template<NTPs ...<anonymous> > class Template, class T> struct is_instantiation_of__nontypes_v2__<NTPs>::impl' 
     * error: expected a template of type 'template<class ... NTPs> template<NTPs ...<anonymous> > class Template', got 'template<char* <anonymous>, bool <anonymous>, long int <anonymous>, long unsigned int <anonymous>, char <anonymous> > struct many_hetero_nontype_params_example' 
     */ 
     //if (is_instantiation_of__nontypes_v2__<char*, bool, long, size_t, char>::impl<many_hetero_nontype_params_example, ManyHeteroNontypeParamsEx>::value) { 
     // std::cout << "yes" << std::endl; 
     //} 
    } 
} 

...

è una soluzione variadic per questo anche possibile?

Grazie in anticipo.

+0

Penso di aver frainteso la tua domanda nella mia risposta originale, mi dispiace. Lo eliminerà e fornirà una risposta migliore. –

+0

La tua risposta originale ha spiegato perché non esiste una soluzione ovvia a questo problema, come dichiarare un pacchetto di parametri "typename ... ScalarTypes" per poi dichiarare il template template "template class TheTemplate" nello stesso parametro-list. Non copre altre possibili soluzioni, come il tentativo is_instantiation_of__nontypes_v2__ che ho postato, che utilizza una struct per dichiarare il parameter-pack per il parametro template-template dichiarato nella lista-parametri della struttura annidata. – etherice

+0

Se avessi potuto spiegare perché quel tentativo di "v2" non avrebbe funzionato (in particolare, gli errori di gcc sul tentativo di usarlo), sarei stato abbastanza convinto che questo non potesse essere fatto e avessi accettato la risposta. Ma sarebbe bello saperlo con certezza. Grazie per aver esaminato questo. – etherice

risposta

5

Probabilmente hai colpito un bug del compilatore. Ho cercato di ridurre questo ad un esempio più semplice:

#include <iostream> 

template<bool, char> struct A { }; 

template<typename... Ts> 
struct test 
{ 
    template<typename T> 
    struct impl : std::false_type {}; 

    template<Ts... Args> 
    struct impl<A<Args...>> : std::true_type {}; 
}; 

int main() 
{ 
    using IA = A<false, 'x'>; 
    std::cout << ((test<bool, char>::impl<IA>::value) ? "Y" : "N"); 
} 

GCC 4.7.2 compila questo, ma il programma compilato prints the wrong output (N). D'altra parte, Clang 3.2 ottiene questo diritto e il programma compilato prints the correct output (Y).

Ecco una versione leggermente modificata del programma di cui sopra, in cui il modello test classe molto simile il vostro modello di classe is_instantiation_of__nontypes_v2__:

#include <iostream> 

template<bool, char> struct A {}; 

template<typename... Ts> 
struct test 
{ 
    template<template<Ts...> class TT, typename T> 
    struct impl : std::false_type {}; 

    template<template<Ts...> class TT, Ts... Args> 
    struct impl<TT, TT<Args...>> : std::true_type {}; 
}; 

int main() 
{ 
    using IA = A<false, 'x'>; 
    std::cout << ((test<bool, char>::impl<A, IA>::value) ? "Y" : "N"); 
} 

Mentre Clang compila questo e il programma compilato stampa l'output corretto (Y), GCC emette il seguente errore di compilazione:

expected a template of type 'template<class ... Ts> template<Ts ...<anonymous> > class TT' , got 'template<bool <anonymous>, char <anonymous> > struct A' .

sembra che GCC non riconoscere che il primo parametro template dovrebbe avere un elenco di argomenti template dato dall'espansione di Ts. Pertanto, mi sembra che questo sia un bug di GCC.

+0

Corretto riguardo alla mancanza di "literal_type ..." argomento-pack che rende questo più difficile. Tuttavia, potrebbe essere ancora possibile se esistesse un modo per dichiarare "typename ... ScalarTypes" nell'elenco dei parametri del template e quindi definire il template template come "template class TheTemplate" ... The il problema è che il parameter-pack deve trovarsi alla fine della lista (dopo che TheTemplate è stato dichiarato). Puoi vedere i miei "tentativi falliti" alla fine del post ... Sto cercando una tecnica simile che funzioni davvero. "is_instantiation_of__nontypes_v2__" si avvicina credo. – etherice

+0

@etherice: ho cercato di commentare un po 'questo nell'ultima modifica alla mia risposta. –

+0

Quella regola (che hai incoraggiato) è applicabile al tentativo "v1", ma non ** sembra ** applicabile al tentativo di "v2". "typename ... NTPs" è dichiarato nella lista dei parametri del template della struct allegata, non della struct annidata. Come ho affermato, viene compilato, ma si vedano gli errori di gcc quando provo ad usarlo. Puoi spiegare l'errore che produce? – etherice