Forum: Compiler & IDEs Präprozessor und Mengenlehre


von Walter T. (nicolas)


Lesenswert?

Guten Morgen zusammen,
ich habe ein Problem, das eher in die Richtung Kuriosum einzuordnen ist: 
Ich muß im Präprozessor zwei Variablen a und b definieren, bei denen ich 
sicher sein muß, daß sie aus der gleichen Menge sind.
1
// Header-Datei mit Einstellungen
2
#define a 
3
#define b
4
5
[...]
6
7
// Tiefer im Code (keine Benutzereinstellungen)
8
#define M1 {0,1,2,3,4} // Mengenklammern
9
#define M2 {5,6,7,8}   // Jedes Element kommt nur einmal vor
10
#define M3 {10,11}
11
12
// Wenn a und b in verschiedenen Mengen sind, Fehler ausgeben
13
// Wenn a oder b in keiner Menge ist auch Fehler ausgeben
14
#error a und b sind ungueltige Parameter
Das ist natürlich total einfach zur Laufzeit zu bewerkstelligen - aber 
geht soetwas auch im Präprozessor?

Viele Grüße
W.T.

P.S.: Was ist eigentlich peinlicher: Viele Rechtschreibfehler oder viele 
Edits beim Postingstart?

von Oliver (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Ich muß

Sagt wer? Und wenn ja, warum?

Manche Probleme lassen sich halt nur durch vollständiges Ignorieren 
lösen.

Der C-Preprozessor ist für so etwas nicht gedacht, kann es nicht, und 
soll es auch nicht können.

Wenn du das unbedingt zur Compilezeit erkennen musst, wirst du einen 
selbstgebauten Pre-Preprozessor einsetzen müssen. Also schreib dir ein 
Script in der Scriptsprache deiner Wahl (oder auch in C), das dein 
Sourcefile parst, erkennt, was es erkennen soll, und bau das mit in 
deine toolschain ein.

Oliver

von (prx) A. K. (prx)


Lesenswert?

Walter Tarpan schrieb:
> P.S.: Was ist eigentlich peinlicher: Viele Rechtschreibfehler oder viele
> Edits beim Postingstart?

Dank intensiver Nutzung durch bestimmte User ;-) hatte Andreas wohl ein 
Einsehen und ersetzte die Liste von Edits durch den letzten Update.

Walter Tarpan schrieb:
> aber geht soetwas auch im Präprozessor?

Kommt drauf an in welchem. Im M4 hast du bessere Chancen als im CPP:
http://www.gnu.org/savannah-checkouts/gnu/m4/manual/m4-1.4.17/html_node/index.html

von Walter T. (nicolas)


Lesenswert?

Oliver schrieb:
> Sagt wer? Und wenn ja, warum?

Walter Tarpan schrieb:
> Ich muß im Präprozessor zwei Variablen a und b definieren

Dieser Teil ist Pflicht.

Walter Tarpan schrieb:
> bei denen ich
> sicher sein muß daß sie aus der gleichen Menge sind.

Dieser Teil ist auch Pflicht - aber problemlos der Verantwortung des 
Bearbeiters der Header-Datei anzulasten.

Das den Präprozessor überprüfen zu lassen ist Kür. Deswegen auch die 
Einleitung:

Walter Tarpan schrieb:
> ich habe ein Problem, das eher in die Richtung Kuriosum einzuordnen ist


A. K. schrieb:
> Kommt drauf an in welchem. Im M4 hast du bessere Chancen als im CPP

Puh, ich wußte gar nicht, daß es für die GCC-Toolkette mehrere 
Präprozessoren zur Auswahl gibt. Ich nutze den AVR-GCC von AtmelStudio 
6.1 (welche Version das ist, kann ich momentan nicht nachgucken). Aber 
das ist nebensächlich - das Thema ist deutlich interessanter als 
nützlich.

von (prx) A. K. (prx)


Lesenswert?

Walter Tarpan schrieb:
> Puh, ich wußte gar nicht, daß es für die GCC-Toolkette mehrere
> Präprozessoren zur Auswahl gibt.

Der M4 hat mit GCC nichts zu tun, lässt sich aber per Makefile 
vorschalten. Entstanden ist er im Zusammenhang mit Fortran.

von Peter D. (peda)


Lesenswert?

Es ist ein oft gemachter Irrtum, daß bei einem #define irgendwas 
berechnet oder geprüft wird.

Der Präprozessor ist ein reiner Textersetzer und prüft nichts.
Er kann nur im #if Integer-Konstanten vergleichen, sonst nichts.

Erst der Compiler nimmt sich den ersetzten Text zu Gemüte und versucht 
seine Syntaxregeln darauf anzuwenden.

von Walter T. (nicolas)


Lesenswert?

Peter Dannegger schrieb:
> Er kann nur im #if Integer-Konstanten vergleichen, sonst nichts

Naja, mehr soll er im Grunde genommen ja auch nicht. Integer-Konstanten 
vergleichen und bei Unzufriedenheit einen Compile-Fehler werfen.

Die defines mit den drei Mengen M1, M2, M3 waren nur zur 
Veranschaulichung, was ich mit "Mengen" überhaupt meine.

von Peter D. (peda)


Lesenswert?

Walter Tarpan schrieb:
> Naja, mehr soll er im Grunde genommen ja auch nicht. Integer-Konstanten
> vergleichen und bei Unzufriedenheit einen Compile-Fehler werfen.

Dann mußt Du ja einfach nur die Vergleiche hinschreiben.

Mir ist allerdings nicht klar, wie Du Dein Mengen-Dingens durch 
Vergleiche rauskriegen willst.
Vergleiche können nur x == y, x > y und x < y ermitteln.

Zusätzlich kann der Präprozessor noch #ifdef, d.h. wurde ein Symbol 
vorher definiert.

von Luther B. (luther-blissett)


Lesenswert?

Peter Dannegger schrieb:
> Es ist ein oft gemachter Irrtum, daß bei einem #define irgendwas
> berechnet oder geprüft wird.

Die Ausdrücke in #if conditionals werden aber vom Präprozessor 
evaluiert.

Man kann daher sowas machen:
1
/* foo.c */
2
#define ELEM(X) (1<<(X))
3
4
#define M1 (ELEM(0)|ELEM(1)|ELEM(2)|ELEM(3)|ELEM(4))
5
#define M2 (ELEM(5)|ELEM(6)|ELEM(7)|ELEM(8))
6
#define M3 (ELEM(10)|ELEM(11))
7
8
#define BOTH (ELEM(A)|ELEM(B))
9
10
#if ((BOTH & M1) != BOTH) && ((BOTH & M2) != BOTH) && ((BOTH & M3) != BOTH)
11
 #error "A und B müssen in der gleichen Menge sein"
12
#endif

und dann:
1
$ gcc -DA=5 -DB=6 -c foo.c 
2
$ gcc -DA=1 -DB=6 -c foo.c 
3
foo.c:13:3: error: #error "A und B müssen in der gleichen Menge sein"

von Walter T. (nicolas)


Lesenswert?

A. K. schrieb im Beitrag #3432531:
> Das war nur seine Art, es zu "veranschaulichen" - d.h. alle gezielt zu
> verwirren.

OK, die Schreibweise in Code-Blöcke zu packen hat vielleicht etwas 
Verwirrung gestiftet. Gemeint war: Es existieren drei (konstante) Mengen 
M1, M2, M3 mit:

M1 = {0,1,2,3,4}
M2 = {5,6,7,8}
M3 = {10,11}

und zwei Konstanten a und b. Wie ich die Mengen M1-M3 als Liste 
beschreibe ist auch egal. Es ist auch nicht zwangsläufig so, daß die 
Mengen direkt aufeinanderfolgende Zahlen enthalten. Erlaubt sind alle a 
und b, bei denen beide in der gleichen Menge sind, also:

a = 1,b = 0 -> erlaubt (beide in M1)
a = 1,b = 5 -> nicht erlaubt (beide in unterschiedlichen Mengen)
a = 1,b = 12 -> nicht erlaubt (nicht beide in der gleichen Menge)

Für jedes erlaubte a kann ich also prüfen, ob b erlaubt ist- was eine 
lange Vergleichsliste ist - zumal die "echte" Liste 5 Mengen und 128 
Werte hat.

In C läßt sich das komfortabel mit Schleifen testen.

Und im Präprozessor sorgt es wohl für Verwirrung :-)

Luther Blissett schrieb:
> Peter Dannegger schrieb:
>> Es ist ein oft gemachter Irrtum, daß bei einem #define irgendwas
>> berechnet oder geprüft wird.
>
> Die Ausdrücke in #if conditionals werden aber vom Präprozessor
> evaluiert.
>
> Man kann daher sowas machen:
> /* foo.c */
> #define ELEM(X) (1<<(X))
>
> #define M1 (ELEM(0)|ELEM(1)|ELEM(2)|ELEM(3)|ELEM(4))
> #define M2 (ELEM(5)|ELEM(6)|ELEM(7)|ELEM(8))
> #define M3 (ELEM(10)|ELEM(11))
>
> #define BOTH (ELEM(A)|ELEM(B))
>
> #if ((BOTH & M1) != BOTH) && ((BOTH & M2) != BOTH) && ((BOTH & M3) !=
> BOTH)
>  #error "A und B müssen in der gleichen Menge sein"
> #endif
>
> und dann:
> $ gcc -DA=5 -DB=6 -c foo.c
> $ gcc -DA=1 -DB=6 -c foo.c
> foo.c:13:3: error: #error "A und B müssen in der gleichen Menge sein"

Das muß ich mir mal in Ruhe anschauen und auf einem Blatt Papier 
auseinandernehmen.

von Oliver (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Das muß ich mir mal in Ruhe anschauen und auf einem Blatt Papier
> auseinandernehmen.

Das funktioniert leider nur mit Werten kleiner 64.

Wenn deine Mengen wirklich konstant sind, und sich auch nie ändern, geht 
es natürlich "zu Fuß":
1
#define A 64
2
#define B 59
3
4
//Mengen als Einzelelemente definieren
5
6
//Menge 1
7
#define M11 0
8
#define M12 1
9
#define M13 4
10
#define M14 7
11
12
//Menge 2
13
#define M21 62
14
#define M22 64
15
#define M23 77
16
17
//Merker
18
#define MENGEA -1
19
#define MENGEB -1
20
21
// und jetzt A für Element einzeln vergleichen, und Merker setzen, wenn in Menge
22
#if (A == M11) || (A == M12) || (A == M13)|| (A == M14)
23
#undef MENGEA
24
#define MENGEA 1
25
#elif (A == M21) || (A == M22) || (A == M23)
26
#undef MENGEA
27
#define MENGEA 2
28
#endif
29
30
// das gleiche mit B
31
#if (B == M11) || (B == M12) || (B == M13)|| (B == M14)
32
#undef MENGEB
33
#define MENGEB 1
34
#elif (B == M21) || (B == M22) || (B == M23)
35
#undef MENGEB
36
#define MENGEB  2
37
#endif
38
39
#if (MENGEA != MENGEB)
40
 #error "A und B muessen in der gleichen Menge sein"
41
#endif

von Luther B. (luther-blissett)


Lesenswert?

Oliver schrieb:
> Das funktioniert leider nur mit Werten kleiner 64.

Bei größeren Werten muss man die Mengen wohl auftrennen, also z.B. 
M1_LOW and M1_HIGH. Dann lautet die Bedingung für "A und B sind in M1" 
jetzt "A in M1_LOW oder A-32 in M1_HIGH und B in M1_LOW oder B-32 in 
M1_HIGH"

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Die Fragestellung ist mathematisch schon zweifelhaft.

a und b sind immer in der gleichen Menge M , etwa für M = { a, b }.

Ansonsten ist die Fragestellung nur dann sinnvoll, wenn die Mengen, um 
die es geht, disjunkt sind, d.h. eine Klasseneinteilung bzw. 
Äquivalenzrelation definieren.

von Peter D. (peda)


Lesenswert?

Vielleicht ist es einfacher, sich ein kleines DOS-Programm zu schreiben, 
was die Datei einliest und nach Deinen Regeln überprüft.

Oder man fügt die Überprüfung über einen Compile-Switch mit ein und läßt 
es im Simulator laufen.

von Walter T. (nicolas)


Lesenswert?

Peter Dannegger schrieb:
> Vielleicht ist es einfacher, sich ein kleines DOS-Programm zu schreiben,
> was die Datei einliest und nach Deinen Regeln überprüft.

Wie schon oben geschrieben: Es ist eine Frage rein aus Neugier. Wer in 
Headerdateien herumfummelt trägt auch die Verantwortung, das konsistent 
zu tun.

Die Plausibilitätsprüfung von Konstanten im Präprozessor ist einfach ein 
Thema, das mich interessiert.

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.