Hallo.
Const möchte ich aus bestimmten Gründen mal nicht verwenden. Ich habe
aktuell viele mit #define definierte Werte, die sich gegeneinander auf
Plausibelität prüfen. Bei einem Schreib- oder sonstigem Fehler greift
der Compiler ins leere und gibt eine vorgesehene Meldung nicht aus,
die eigentlich kommen müsste (z.B. Wert zu groß).
Gibt es eine Möglichkeit, dem Compiler zu sagen, dass er mit Fehler
abbrechen soll, wenn er über eine Definition stolpert, die nicht
definiert ist?
Beispiel
1
#define TEST 123
2
#if (TETS > 100) // falsche Schreibweise
3
#error "Der Wert ist zu groß" // Diese Meldung bleibt natürlich aus
4
#endif // erzeugt aber auch keinen Fehler
5
6
voidsetup(){
7
}
8
9
voidloop(){
10
}
Es betrifft natürlich nur "wegoptimierte" Definitionen, auf die nicht
zugegriffen wird (zuküftige Ressourcen, die aber jetzt schon festgelegt
worden sind). Ich verwende GCC.
define schrieb:> Gibt es eine Möglichkeit, dem Compiler zu sagen, dass er mit Fehler> abbrechen soll, wenn er über eine Definition stolpert, die nicht> definiert ist?
Nein. Abgesehen von der Spitzfindigkeit, daß der Compiler #defines
sowieso nicht zu Gesicht bekommt (das macht der Präprozessor), würdest
Du damit annähernd jeden existierenden Code unbrauchbar machen, denn das
"Feature" undefinierter #defines wird intensiv genutzt -- dafür bietet
der Präprozessor eigens #ifdef bzw. #if defined.
Hängt vom Compiler ab. Unter gcc und clang kannst du mit -Wundef
kompilieren, dann gibt es eine Warnung oder -Werror=undef, dann bekommst
du einen Fehler.
define schrieb:> Gibt es eine Möglichkeit, dem Compiler zu sagen, dass er mit Fehler> abbrechen soll, wenn er über eine Definition stolpert, die nicht> definiert ist?
Ja, das geht (aber wie bereits geschrieben, macht das der Präprozessor).
Typische Pattern:
Ok, Hauptsache mal zu Wort gemeldet.
Die Sache ist, dass der Präprozessor nicht definierte Symbole zu 0
auswertet aber keinen Fehler erzeugt. Das ist historisch so. Das
bedeutet bei
1
#if TEST != 0
2
// tue A
3
#else
4
// tue B
5
#endif
kann sich der Entwickler nicht sicher sein, ob B eincompiliert wird,
weil TEST definiert und auf 0 gesetzt wurde, oder weil TEST gar nicht
definiert ist. Das macht bei großen Projekten Probleme, wenn irgendwo
ein #include vergessen wurde. Dann kann es passieren, dass verschiedene
Modulen mit inkonsistenter Konfiguration compiliert werden. Will man
nicht.
Wenn man statt dessen also einen Fehler möchte, kann man das so machen:
1
#if 1/defined(TEST) && (TEST != 0)
2
// tue A
3
#else
4
// tue B
5
#endif
Der Fehler entsteht, falls TEST nicht definiert ist, da der Präprozessor
bei der Division durch 0 abbricht (1/0). Ist TEST definiert, steht dort
(1/1) und somit
1
#if 1 && (TEST != 0)
Für das erfolgreiche compilieren muss TEST dann definiert sein und A
wird eincompiliert falls TEST != 0 und andernfalls wird B eincompiliert
falls TEST == 0. So wie man es erwarten würde - nur mit Fehler beim
compilieren, falls TEST nicht definiert ist.
x^2 schrieb:> Der Fehler entsteht, falls TEST nicht definiert ist, da der Präprozessor> bei der Division durch 0 abbricht (1/0). Ist TEST definiert, steht dort> (1/1) und somit
Ja genau. Wie gesagt:
ifndef und !defined() kennste?
Was soll der Vorteil von der Division durch Null sein?
Ich finde es ist v.a. bei Verschachtelung übersichtlicher. Es läßt sich
auch leicht automatisch prüfen (eine kleine regex reicht) ob es irgendwo
fehlt. Im Vergleich:
1
#ifdef TEST
2
#if TEST != 0
3
// tue A
4
#else
5
// tue B
6
#endif
7
#else
8
#error "Missing TEST"
9
#endif
1
#if 1/defined(TEST) && (TEST != 0)
2
// tue A
3
#else
4
// tue B
5
#endif
Nachteil ist sicher, dass man sich erst mal daran gewöhnen muss, um
nicht jedesmal bei der Zeile stutzig hängen zu bleiben.
x^2 schrieb:> #ifdef TEST> #if TEST != 0> // tue A> #else> // tue B> #endif> #else> #error "Missing TEST"> #endif
#ifndef TEST
#error "Missing TEST"
#elif TEST
// tue A
#else
// tue B
#endif
Vorteil: Man hat eine vernünftige Fehlermeldung, in der man auch noch
erklären kann, was zu tun ist um das zu beheben, statt einer Division
durch Null.
OK, aus den Infos kann ich was machen. danke.
Die Makrobefehle kenne ich alle. Es sind ja nicht viele.
#ifdef ist keine Lösung. Will man jede Deklaration mit ifdef
hinterprüfen, kommt man zu nichts anderem. Ab einer bestimmten
Programmgröße jedenfalls. Zumal sich da auch wieder Fehler einschleichen
können.
Die Plausichecks (natürlich vom Parser und nicht vom Compiler) sollen
Konfigurationsfehler verhindern, und tun das auch sehr effizient. Mir
ist es gestern aber passiert, dass ich eine Passage gelöscht habe,
wodurch bestimmte Deklarationen nicht mehr gebraucht wurden. Diese
blieben aber stehen, ich bin ja auch nur ein Mensch, und irgendwann habe
ich mich gefragt, wieso da keine Meldung kommt, wenn auf verwaiste
Ressourcen zugegriffen wird. Aber jetzt kenne ich ja den Grund. In
Includfiles stehen jede Menge ungenutzte defines, das darf natürlich
keinen Fehler geben. Wenn an auf diese defines zugreift, auf
Präprozessor- oder Parserebene, sollte aber doch wenigstens eine Meldung
kommen, statt den Wert 0 zugrunde zu legen. Das war mein Anliegen. Wenn
das nicht geht, komme ich damit auch zurecht, ist halt anstrengend bei
hunderten Zeilen Plausichecks. also danke nochmal.
Viele Wege führen offentlichtlich nach Rom.
Hdudzevdjfd schrieb:> Warum in C++ nicht constexpr und static_assert nehmen? Typischer> und the> C++ way. Defines braucht man in C++ ausschließlich für die include> guards.
Was ist mit bedingt compiliertem Code?