Forum: PC-Programmierung C++ Compiler Einstellung zu defines


von define (Gast)


Lesenswert?

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
void setup() {
7
}
8
9
void loop() {
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.

von Vincent H. (vinci)


Lesenswert?

So etwa?
1
#ifndef X
2
#error "bla"
3
#endif

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

define schrieb:
> Const möchte ich aus bestimmten Gründen mal nicht verwenden.

Und die wären?
1
#ifndef TEST
2
#error "..."
3
#endif

oder
1
#if !defined(TEST) || TEST > 100
2
#error "..."
3
#endif

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Luther B. (luther-blissett)


Lesenswert?

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.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Alternativ static_assert nehmen (portabel):
1
static_assert (TEST <= 100, "Test zu gross");
Das prüft wirklich der Compiler, und dazu muss der Ausdruck syntaktisch 
korrekt sein.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

1
-Wundef
2
3
    Warn if an undefined identifier is evaluated in an #if directive. Such identifiers are replaced with zero.

Im gegebenen Fall würde das tatsächlich helfen.

von x^2 (Gast)


Lesenswert?

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:
1
#if 1/defined(TEST)
2
// do something if TEST is defined
3
#endif
1
#if 1/defined(TEST) && (TEST != 0)
2
// do something if TEST evaluates to TRUE
3
#else
4
// do something if TEST evaluates to FALSE
5
#endif

von Nein (Gast)


Lesenswert?

x^2 schrieb:
> Typische Pattern:#if 1/defined(TEST)

Nein.
Absolut untypisch.

Den Not-Operator kennste?

von x^2 (Gast)


Lesenswert?

Nein schrieb:
> x^2 schrieb:
>> Typische Pattern:#if 1/defined(TEST)
>
> Nein.
> Absolut untypisch.
>
> Den Not-Operator kennste?

Klar. Wieso?

von x^2 (Gast)


Lesenswert?

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.

von Nein (Gast)


Lesenswert?

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?

von x^2 (Gast)


Lesenswert?

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.

von Nein (Gast)


Lesenswert?

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.

von define (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Hdudzevdjfd (Gast)


Lesenswert?

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.

von Nein (Gast)


Lesenswert?

1
$ cat t.c 
2
#if FOO
3
#endif
4
int main(void){}
5
6
7
8
$ gcc -Wundef -Werror=undef -o t t.c
9
t.c:1:5: error: "FOO" is not defined, evaluates to 0 [-Werror=undef]
10
 #if FOO
11
     ^~~
12
cc1: some warnings being treated as errors

von x^2 (Gast)


Lesenswert?

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?

von Niklas Gürtler (Gast)


Lesenswert?

x^2 schrieb:
> Was ist mit bedingt compiliertem Code?

Ist i.A. besser mit Template-Spezialisierung.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.