2015-07-30 19 views
5

Ho un contatore di compilazione che ho utilizzato per anni, ispirato allo these answers. Funziona in C++ 03/11 e per quanto ho testato, relativamente pure sulle principali compilatori:Contatore del tempo di compilazione nella classe template

namespace meta 
{ 
    template<unsigned int n> struct Count { char data[n]; }; 
    template<int n> struct ICount : public ICount<n-1> {}; 
    template<> struct ICount<0> {}; 

    #define MAX_COUNT 64 
    #define MAKE_COUNTER(_tag_) \ 
     static ::meta::Count<1> _counter ## _tag_ (::meta::ICount<1>) 
    #define GET_COUNT(_tag_) \ 
     (sizeof(_counter ## _tag_ (::meta::ICount<MAX_COUNT + 1>())) - 1) 
    #define INC_COUNT(_tag_) \ 
     static ::meta::Count<GET_COUNT(_tag_) + 2> _counter ## _tag_ (::meta::ICount<2 + GET_COUNT(_tag_)>) 
} 

Il seguente test compiles and runs perfectly (expected uscita è 0 1 2 3):

struct Test 
{ 
    MAKE_COUNTER(uu); 

    static const unsigned int a = GET_COUNT(uu); 
    INC_COUNT(uu); 
    static const unsigned int b = GET_COUNT(uu); 
    INC_COUNT(uu); 
    static const unsigned int c = GET_COUNT(uu); 
    INC_COUNT(uu); 
    static const unsigned int d = GET_COUNT(uu); 

}; 

template<typename T> 
void test() 
{ 
    std::cout << T::a << " " << T::b << " " << T::c << " " << T::d << "\n"; 
} 

int main() 
{ 
    test<Test>(); 
} 

Tuttavia, Ho trovato un caso in cui vedo un comportamento molto strano con clang e gcc. Se si modifica Test come struttura del modello, prendendo un esempio per esempio (template<int> struct Test e test<Test<42> >() in main), clang and gcc both fail to compile, si lamenta che sto ridefinendo la funzione contatore (mentre msvc lo compila senza problemi). Per qualche motivo il compilatore non riesce a calcolare una sizeof in una classe template.

clang trovare l'errore nel terzo INC_COUNT, mentre gcc lo trova nella seconda.

ho ampliato manualmente questa macro, e:

  • per clang, dà

    static ::meta::Count<GET_COUNT(uu)+2> _counteruu(::meta::ICount<(sizeof(_counteruu(::meta::ICount<65>())) - 1)+2>); 
    //               ^          ^
    

    rimuovendo le parentesi sottolineate risolve il problema.

  • per gcc: spostare il +2 prima della sizeof è l'unico lavoro intorno

La nota triste è che queste soluzioni alternative sembrano non funzionare quando incluso nelle macro. È come se il compilatore dimenticasse solo come calcolare il risultato di sizeof dopo un po 'di tempo ...

Perché sta succedendo? Sto facendo qualcosa di sbagliato, o sono solo bug del compilatore (dato che clang e gcc non riportano nemmeno la stessa riga)?

Nota: so there is a gcc bug about this counter. La domanda non riguarda questo bug.

+0

(Ignora tutti i miei commenti precedenti, che ho ora eliminato.Essi non erano corretti.Ho ampliato manualmente alcuni dei modelli e non ricordavo abbastanza correttamente quali esperimenti avevo fatto.) –

risposta

6

Il codice è mal formato, non è richiesta alcuna diagnostica. §3.3.7/1, secondo punto :

Un nome N utilizzato in una classe S s'intende la stessa dichiarazione nel suo contesto e quando rivalutati nell'ambito completamento di S. No diagnosi è necessaria per una violazione di questa regola.

Si utilizza la risoluzione di sovraccarico per selezionare il sovraccarico appropriato di _counteruu. Tuttavia, nell'inizializzatore di ad es. a, viene selezionato un sovraccarico (= dichiarazione) che non verrebbe selezionato se si eseguisse la risoluzione di sovraccarico alla fine di Test, ad esempio nell'inizializzatore di d. Quindi _counteruu fa riferimento a un'altra dichiarazione distinta quando viene rivalutata nell'ambito completo di Test.

di presentarsi in cui si chiede esattamente mi riferisco a, prendere in considerazione la definizione pre-elaborato di Test:

struct Test 
{ 
    // (1) 
    static ::meta::Count<1> _counteruu (::meta::ICount<1>); 
    static const unsigned int a = (sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1); 
    // (2) 
    static ::meta::Count<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2> _counteruu (::meta::ICount<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2>); 
    static const unsigned int b = (sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1); 
    // (3) 
    static ::meta::Count<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2> _counteruu (::meta::ICount<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2>); 
    static const unsigned int c = (sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1); 
    // (4) 
    static ::meta::Count<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2> _counteruu (::meta::ICount<(sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1) + 2>); 
    static const unsigned int d = (sizeof(_counteruu (::meta::ICount<64 + 1>())) - 1); 

}; 

rendimenti semplificazione

struct Test 
{ 
    // (1) 
    static ::meta::Count<1> _counteruu (::meta::ICount<1>); 
    static const unsigned int a = (sizeof(_counteruu (::meta::ICount<65>())) - 1); 
    // (2) 
    static ::meta::Count<2> _counteruu (::meta::ICount<2>); 
    static const unsigned int b = (sizeof(_counteruu (::meta::ICount<65>())) - 1); 
    // (3) 
    static ::meta::Count<3> _counteruu (::meta::ICount<3>); 
    static const unsigned int c = (sizeof(_counteruu (::meta::ICount<65>())) - 1); 
    // (4) 
    static ::meta::Count<4> _counteruu (::meta::ICount<4>); 
    static const unsigned int d = (sizeof(_counteruu (::meta::ICount<65>())) - 1); 
}; 

Possiamo chiaramente vedere come il meccanismo funziona ora: Sovraccarico la risoluzione preferirà l'ultimo sovraccarico aggiunto quando ICount<viene passato un numero sufficientemente grande> a causa del modo in cui le conversioni derivate-base sono r anked. Tuttavia, la chiamata nell'inizializzatore di a selezionerà il primo sovraccarico; Ma rivalutare questo inizializzatore selezionerebbe l'ultimo.


Questo punto proiettile esisteva in C++ 03 pure, ma in §3.3.6.

+0

Ok, non sapevo questo punto, quindi questo codice era sempre mal formato. È interessante notare che clang consente ancora una "ridefinizione" prima di lanciare un errore. – Synxis

Problemi correlati