2011-01-28 3 views
1

Ceci fait partie d'un tour d'assertion statique. Je ne peux pas comprendre comment fonctionne la classe non spécialisée. Quelqu'un peut-il me l'expliquer?Que signifie ce code?

EDIT: code complet avec macro: (prise de http://www.skynet.ie/~caolan/Fragments/C++StaticAssert.html)

#ifndef STATICASSERT_HXX 
#define STATICASSERT_HXX 
/* 
Lifted direct from: 
Modern C++ Design: Generic Programming and Design Patterns Applied 
Section 2.1 
by Andrei Alexandrescu 
*/ 
namespace ww 
{ 
    template<bool> class compile_time_check 
    { 
    public: 
     compile_time_check(...) {} 
    }; 

    template<> class compile_time_check<false> 
    { 
    }; 
} 

    /* 
    Similiar to assert, StaticAssert is only in operation when NDEBUG is not 
    defined. It will test its first argument at compile time and on failure 
    report the error message of the second argument, which must be a valid c++ 
    classname. i.e. no spaces, punctuation or reserved keywords. 
    */ 
#ifndef NDEBUG 
# define StaticAssert(test, errormsg)       \ 
    do {              \ 
     struct ERROR_##errormsg {};        \ 
     typedef ww::compile_time_check< (test) != 0 > tmplimpl; \ 
     tmplimpl aTemp = tmplimpl(ERROR_##errormsg());   \ 
     sizeof(aTemp);           \ 
    } while (0) 
#else 
# define StaticAssert(test, errormsg)       \ 
    do {} while (0) 
#endif 

#endif 

Répondre

5

Les appels macro ce code d'une manière similaire à ceci:

compile_time_check<static expression> temp(Error_Some_Struct_here); 

Ainsi, par exemple, vous pouvez faire ceci:

compile_time_check<sizeof(Foo) < sizeof(Bar)> temp(Error_Foo_must_be_smaller_than_Bar); 

Lorsque sizeof(Foo) est inférieure à sizeof(Bar), le modèle serait instancier la version non spécialisée:

template<bool> class compile_time_check 
{ 
public: 
    compile_time_check(...) {} //What is this? 
}; 

et le code essentiellement « compile à » une instanciation de cette classe:

compile_time_check temp(Error_Foo_must_be_smaller_than_Bar); 

qui, étant vide et ne rien faire, le compilateur peut supprimer le code comme mort. Bam, pas de temps d'exécution, terminé.

Si, d'autre part, sizeof(Foo) est supérieur ou égal à sizeof(Bar), il serait plutôt instancier la version spécialisée:

template<> class compile_time_check<false> 
{ 
}; 

et il tenterait d'appeler le constructeur compile_time_check::compile_time_check(struct), mais comme il n » t existe, c'est une erreur de compilation. Ce qui est ce que vous voulez, car l'affirmation statique ne devrait compiler que si l'affirmation est vraie.

La raison pour le constructeur de prendre une liste de paramètres variadique est, je crois, deux fois:

  1. Pour vous assurer que cela ne remet pas le constructeur par défaut, la version spécialisée aurait. Son variadique de sorte que vous pouvez passer dans toute structure comme une erreur "chaîne". Alternativement, cela pourrait avoir été modélisé et le constructeur aurait pu prendre un objet template comme argument à la place.
  2. Pour que le message d'erreur puisse être transmis. Lorsque l'assertion est vraie, elle est ignorée et rien ne se passe et le code est supprimé par l'optimiseur du compilateur. Si le assert est faux, cependant, la chaîne d'erreur devrait apparaître dans le message d'erreur. Quelque chose comme constructor not found for compile_time_check::compile_time_check(ERROR_Assertion_error_blah()) peut-être.

Une alternative, sans modèle (je crois que son souvent utilisé en C), assert statique que je l'ai vu quelque part est le suivant:

#define compile_time_assert(pred) switch(0){case: 0: case pred:;} 

Cela fonctionne parce que si pred est faux, le code finirait comme switch(0){case: 0: case 0:;} et deux étiquettes de cas avec la même constante est une erreur. In depth explanation here.

+0

@Dan: Voir mes modifications. Pourquoi la macro a-t-elle une ligne 'sizeof (aTemp);' – nakiya

+0

@Dan En fait 'compile_time_check :: compile_time_check()' compilerait car le constructeur par défaut est disponible. C'est pourquoi une structure avec un nom looong est en cours de passation. – ssmir

+0

ssmir, bien sûr. Merci d'avoir fait remarquer cela. J'ai écrit ça avant de voir le code complet. Dans le code maintenant posté, il ne serait pas compilé sans la structure qui est passée. Je vais éditer la réponse. – Dan

3

Se débarrasser de l'espace de noms pour le moment (car il est fondamentalement hors de propos), ce que vous avez est:

template<bool> class compile_time_check 
{ 
public: 
    compile_time_check(...) {} //What is this? 
}; 

Ce bien est un modèle de classe. Il a un paramètre de type non-type de type bool, et un constructeur variadique, donc il acceptera n'importe quel argument.

template<> class compile_time_check<false> 
{ 
}; 

Ceci est une spécialisation du modèle précédent pour la valeur false. En tant que tel, lorsque vous instanciez compile_time_check<false> checker;, il utilisera celui-ci. Cette spécialisation a un constructeur par défaut (qui n'est jamais utilisé) mais aucun constructeur qui acceptera un paramètre.

Le but est que ce n'est jamais utilisé d'ici:

typedef ww::compile_time_check< (test) != 0 > tmplimpl; \ 
    tmplimpl aTemp = tmplimpl(ERROR_##errormsg());   \ 
    sizeof(aTemp);           \ 

Dans ce cas, nous instancier un objet tmplimpl avec un paramètre cteur. Si le paramètre template est true, cela fonctionnera, mais si le paramètre template est false, il utilisera la spécialisation ci-dessus qui n'a qu'un ctor par défaut, pas un qui prendra un argument - donc le compilateur imprimera un message d'erreur disant quelque chose comme:

error: no ctor found for compile_time_check<condition>("<error message>"); 

La sizeof(aTemp); suivante est là pour forcer cet objet à évaluer au moment de la compilation, donc nous voyons cela comme un message d'erreur du compilateur au lieu d'une erreur de linker dire que compile_time_check::compile_time_check<false>() est un non résolu externe.

+0

+1 pour expliquer 'sizeof (aTemp)' – Dan