web-dev-qa-db-fra.com

Assertion à la compilation?

Est-il possible d'affirmer que deux expressions constantes sont égales au moment de la compilation? 

par exemple. Je veux que cela cause une erreur lors de la compilation

enum { foo=263, bar=264 };
SOME_EXPRESSION(foo,bar)

mais je veux que cela ne cause pas d'erreur

enum { foo=263, bar=263 };
SOME_EXPRESSION(foo,bar)

edit: ce qui précède a été simplifié. Ma situation ressemble plus à

some_other_file_I_dont_control.h:

class X
{
public:
   enum { foo=263 };
}

mon_fichier.h:

enum { bar=something+somethingelse }; // bar should equal X::foo
SOME_EXPRESSION(X::foo, bar)
32
Jason S

Voir static_assert (C++ 0x uniquement); si sur une version plus ancienne, voir Boost's StaticAssert .

33
Mehrdad

Oui. Vous pouvez le faire avec des spécialisations de modèles sur le type bool, comme ceci:

// empty default template
template <bool b>
struct StaticAssert {};

// template specialized on true
template <>
struct StaticAssert<true>
{
    static void assert() {}
};

int f()
{
    StaticAssert<1==1>::assert();   // compiles fine, assert() member found
    StaticAssert<1==2>::assert();   // compile failure, no assert() member for StaticAssert<false>
}

Le code est fondamentalement de la mémoire, peut avoir besoin de quelques ajustements.

34
Chad

Pour une autre version d'une assertion statique, que vous pouvez glorifier en ajoutant un meilleur nom, vous pouvez utiliser:

// name must be a valid identifier
#define STATIC_ASSERT( condition, name )\
    typedef char assert_failed_ ## name [ (condition) ? 1 : -1 ];

Et utiliser comme:

STATIC_ASSERT( x == y, constants_must_be_same );

Le compilateur déclenchera une erreur similaire à:

size of array 'assert_failed_constants_must_be_same' is negative

Ce qui ne semble pas très utile, mais il indiquera la ligne exacte de l'assertion, et après un moment, vous commencerez à traiter ce message d'erreur comme static assert failed

Une autre possibilité pour Windows est C_ASSERT , qui est défini si Windows.h est inclus. 

6
Benjamin Herreid

vous pouvez définir votre propre assertion statique, de cette façon: 

#include <iostream>
template <bool b> class ClassStaticAssert;
template <>
class ClassStaticAssert<true>{static const bool value = true;};
#define STATIC_ASSERT(e) (ClassStaticAssert<e>())
int main()
{
    STATIC_ASSERT(0);
    return 0;
}
2
Dhia Hassen
template <int a, int b>
inline void static_assert_equal()
{
    typedef char enum_values_must_be_equal[a == b ? 1 : -1];
    (void) sizeof(enum_values_must_be_equal);
}

int main()
{
    enum { foo = 1, bar = 2, fum = foo };
    static_assert_equal<foo, fum>(); // compiles ok
    static_assert_equal<foo, bar>(); // fails at compile time
    return 0;
}

Cela provient du checked_delete idiome.

1
Andreas Spindler

Semblable à la solution de iammillind, qui n’était malheureusement utile que lors de l'exécution: 

template <int A, int B>
class VALUES { 
};

// specialization to provide safe passage for equal values        
template <int X>
class VALUES<X, X> {
public:
   static void MY_VALUES_ARE_EQUAL() {}
};


#define ASSERT_EQUALITY(a, b)    \
{    \
   typedef VALUES<a, b> COMPILE_TIME_ASSERTION;       \
   COMPILE_TIME_ASSERTION::VALUES_ARE_EQUAL();     \
}

int main() {
   ASSERT_EQUALITY(1, 1);    // compiles just fine
   ASSERT_EQUALITY(1, 2);    // ERROR!
   // . . . 
 }

La bonne chose à ce sujet est qu’il fournit un message de compilation Nice. Mon compilateur vous dit: 

'VALUES_ARE_EQUAL' n'est pas membre de 'COMPILE_TIME_ASSERTION {aka VALUES <1, 2>}'

Vous n'avez pas besoin du typedef. Sans pour autant: 

'VALUES_ARE_EQUAL' n'est pas un membre de 'VALUES <1, 2>' 

Bien sûr, il existe de nombreuses autres façons de générer des messages utiles. Pour les fou rire: 

// these give use some tips in the compiler warnings
class COMPILE_TIME_EQUALITY_ASSERTION {} compiler_message; 
class EQUAL_VALUES_ONLY_PLEASE {};


template <int A, int B>
class VALUES {
public:
   static void AreEqual(EQUAL_VALUES_ONLY_PLEASE) {}
};

template <int X>
class VALUES<X, X>
{
public:
   static void AreEqual(COMPILE_TIME_EQUALITY_ASSERTION) {}
};


#define ASSERT_EQUALITY(a, b)                                   \
{                                                               \
   VALUES<a, b>::AreEqual(compiler_message);                             \
}

int main() {
    ASSERT_EQUALITY(1, 1) // a-okay
    ASSERT_EQUALITY(1, 2) // ERROR!
}

Je reçois les erreurs de compilation suivantes: 

no matching function for call to:
‘VALUES<1,2>::AreEqual(COMPILE_TIME_EQUALITY_ASSERTION&)' 
candidate is:
static void VALUES<\A, B>::AreEqual(EQUAL_VALUES_ONLY_PLEASE) [with int A = 1, int B = 2]

des combinaisons de fonctions membres statiques/constructeurs/affectation de champs/confidentialité et spécifications de modèles peuvent produire des résultats différents, peut-être plus appropriés à votre situation. 

1
Dodgie

Il y a aussi l'astuce d'utiliser une instruction switch (..). Un peu vieux style cependant. L'entrée de cas foo == bar doit être évaluée au moment de la compilation et s'il s'avère faux, l'instruction switch provoquera une erreur. Le compilateur le réduira également à "rien".

{ 
  bool x=false; 
  switch (x) {
  case foo == bar:
    break;
  case false:
    // Compile time test that foo == bar
    break;
}
1
epatel

Je choisirais l'un des static_asserts disponibles.

  • boost :: static_assert
  • C++ 0x static_assert

Mais juste parce que je n'ai jamais essayé auparavant, j'ai écrit ceci:

enum { foo=263, bar=264 };

template<bool test>
struct CompileAssert
{
    bool assert() {}
};

template<>
struct CompileAssert<false>  {}; // fail on false.

int main()
{
    CompileAssert<foo != bar>().assert();  // Now I have seen Chad above I like his static 
    CompileAssert<foo == bar>().assert();  // method better than using a normal method.
}                                          // But I tried zero length arrays first did 
                                           // not seem to work
0
Martin York

Je suggère d’examiner le mécanisme d’assertion statique de la bibliothèque Eigen:

http://eigen.tuxfamily.org/dox/StaticAssert_8h_source.html

0
linello

Vous pouvez faire de la magie de préprocesseur comme

#define FOO_VALUE 263
#define BAR_VALUE 264

enum {foo=FOO_VALUE, bar=BAR_VALUE}

#if !(FOO_VALUE == BAR_VALUE)
#error "Not equal"
#endif
0
marc