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)
Voir static_assert
(C++ 0x uniquement); si sur une version plus ancienne, voir Boost's StaticAssert
.
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.
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.
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;
}
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.
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.
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;
}
Je choisirais l'un des static_asserts disponibles.
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
Je suggère d’examiner le mécanisme d’assertion statique de la bibliothèque Eigen:
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