Forum: Compiler & IDEs Präprozessor Kontrolle der Übergabe


von Steven (. (ovular) Benutzerseite


Lesenswert?

Hallo zusammen,

ich würde gerne noch während des Compilierens prüfen, ob ein Wert für 
ein Makro gültig ist.
1
//F_CPU ist 16MHz
2
#define prescaler 64
3
#define TasteZuZaelerwert(taste) floor((F_CPU/(prescaler*2*(440.0*pow(2,((taste-49.0)/12))))))
4
5
...
6
OCR2 = (TasteZuZaelerwert(50));
7
TCCR2 = 0x07&64; //starte Timer

Ich will hier Töne mit einem 8Bit Counter erzeugen, es soll aber geprüft 
werden ob der Wert auch in ein 8Bit register passt. Falls nicht soll ein 
Error ausgegeben werden.

Gibt es da überhaupt was?

Gruß Steven

von Hmm (Gast)


Lesenswert?

Grundsätzlich ja, wenn Du Dich auf integer-Arithmetik beschränkst und 
alle symbolischen Ausdrücke wiederum zum Zeitpunkt der Compilation 
bekannt sind.
pow zur Basis 2 lässt sich durchaus durch eine Schiebeoperation 
ersetzen.

Ich sehe da aber sowas wie "taste" dessen Definition bzw. Deklaration Du 
nicht angegeben hast.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Hmm schrieb:
> Ich sehe da aber sowas wie "taste" dessen Definition bzw. Deklaration Du
> nicht angegeben hast.

Das ist das Argument des Macros.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Danke für die Antwort!

Hmm schrieb:
> pow zur Basis 2 lässt sich durchaus durch eine Schiebeoperation
> ersetzen.

ich glaube das geht nicht, weil der Exponent rational ist?!
EDIT: achso ja, wenn ich Integer-Arithmetik benutzen würde. Muss mal 
sehen ob ich da was umformen kann, hab in die Richtung noch nicht 
nachgedacht...

Hmm schrieb:
> Ich sehe da aber sowas wie "taste" dessen Definition bzw. Deklaration Du
> nicht angegeben hast.

muss ich das? Kommt doch vom Makroaufruf...

von (prx) A. K. (prx)


Lesenswert?

Steven () schrieb:
> ich glaube das geht nicht, weil der Exponent rational ist?!

Das geht nicht, weil der Präprozessor keine Funktion pow() kennt. Weil 
er überhaupt keine Funktion kennt.

von Steven (. (ovular) Benutzerseite


Lesenswert?

A. K. schrieb:
> Das geht nicht, weil der Präprozessor keine Funktion pow() kennt. Weil
> er überhaupt keine Funktion kennt.

Ich habs schon ausprobiert, er rechnet es vorher aus und setzt die Zahl 
ein.
Wird scheinbar wegoptimiert.

Gruß Steven

von troll (Gast)


Lesenswert?

Google spuckt einiges aus, z.B. 
http://www.jaggersoft.com/pubs/CVu11_3.html , aber bei codepad kriege 
ich den Kram so auf die Schnelle nicht zum Laufen. Aber es muss gehen...

von (prx) A. K. (prx)


Lesenswert?

Steven () schrieb:
> Wird wegoptimiert

Im C Ausdruck ja, so der Compiler will. Das hat aber nichts mit dem 
Präprozessor zu tun, denn der rechnet nirgends ausser in #if.

Du willst aber eine Fehlermeldung des Präprozessors, wenn ich dich 
richtig verstanden habe. Das läuft auf irgendwas mit #if und #error raus 
und da geht es zumindest offiziell nicht.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Mhm...

wie würde ich es denn machen, wenn ich keine Funktion drinstehen hätte?

Mein problem ist das Attribut, glaube ich
1
#define irgendwas(x) x*2+3
2
#if (irgendwas>100)
3
#error ein error
4
#endif
so geht ja das wohl nicht, oder?

Jetzt guck ich mir mal die Assert.h an...


Gruß Steven

von (prx) A. K. (prx)


Lesenswert?

Steven () schrieb:
> wie würde ich es denn machen, wenn ich keine Funktion drinstehen hätte?

Ähnlich dumm aus der Wäsche sehen, da der Präprozessor keine 
Makro-Parameter kontrollieren kann. ;-)

Was du aber machen kannst: Den Ausdruck so schreiben, dass er im 
Fehlerfall durch 0 dividiert. Dann warnt bereits der Compiler, so er das 
zur Übersetzungszeit berechnet.

Das kann also sowas sein wie
  #define f(x) (((x) <= 10) ? ... : 0/0)
Du musst dann nur im Kopf haben, was "division by zero" bei
  t = f(11);
eigentlich bedeutet.

von Steven (. (ovular) Benutzerseite


Lesenswert?

A. K. schrieb:
> Ähnlich dumm aus der Wäsche sehen, da der Präprozessor keine
> Makro-Parameter kontrollieren kann. ;-)

Ja ich guck schon zu lange dumm, heute.
1
#define TasteZuZaelerwert(taste) ((taste>50) ? (floor((F_CPU/(prescaler*2*(440.0*pow(2,((taste-49.0)/12))))))) : 100/0)

er liefert eine "-1", kein Fehler erkannt...

von (prx) A. K. (prx)


Lesenswert?

Ich kriege eine Fehlermeldung.
1
#include <math.h>
2
3
#define F_CPU 1
4
#define prescaler 1
5
#define TasteZuZaelerwert(taste) ((taste>50) ? (floor((F_CPU/(prescaler*2*(440.0*pow(2,((taste-49.0)/12))))))) : 100/0)
6
7
int g(void)
8
{
9
        return TasteZuZaelerwert(50);
10
}
div2.c:9:9: warning: division by zero [-Wdiv-by-zero]


NB: Zähler schreibt man mit "h".

von Steven (. (ovular) Benutzerseite


Lesenswert?

Stimmt die Warnung bekomm ich auch, hab Sie nicht gesehen weil er Sie 
mich nicht explizit gezeigt hat :)

Am besten wäre halt ein Error, ich werd mich noch umschauen.

Aber Danke! Habe jetzt mal ne Vorstellung.

Gruß Steven

von (prx) A. K. (prx)


Lesenswert?

Steven () schrieb:
> Am besten wäre halt ein Error, ich werd mich noch umschauen.

Wenns weiter nichts ist: -Werror.

Warnings geflissentlich auszublenden ist nicht wirklich zu empfehlen.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Wenns nach mir ginge, würde dem Präprozessor solche Funktionen nicht 
schaden...

Gruß Steven

von (prx) A. K. (prx)


Lesenswert?

Steven () schrieb:
> Wenns nach mir ginge, würde dem Präprozessor solche Funktionen nicht
> schaden...

Dann schalte den Präprozessor m4 davor.

http://en.wikipedia.org/wiki/M4_(computer_language)

von Hmm (Gast)


Lesenswert?

>Hmm schrieb:
>> Ich sehe da aber sowas wie "taste" dessen Definition bzw. Deklaration Du
>> nicht angegeben hast.

Rufus Τ. Firefly schrieb:

>Das ist das Argument des Macros.

Steven () schrieb:
>muss ich das? Kommt doch vom Makroaufruf...

Ooops. Habe ich übersehen. Sorry.

A. K. (prx) schrieb:

>Du willst aber eine Fehlermeldung des Präprozessors, wenn ich dich
>richtig verstanden habe. Das läuft auf irgendwas mit #if und #error raus
>und da geht es zumindest offiziell nicht.

An sich ist #error schon Standard. K&R 2.Auflage, Seite 231
Oder habe ich Dich falsch verstanden?

von (prx) A. K. (prx)


Lesenswert?

Hmm schrieb:
> An sich ist #error schon Standard. K&R 2.Auflage, Seite 231
> Oder habe ich Dich falsch verstanden?

Zeig mal C Code, in dem der Präprozessor den Parameter eines Makros auf 
einen Bereich von 1..10 kontrolliert und widrigenfalls #error ausspuckt.

Zeig ausserdem im Sinn der Sache funktionsfähigen C Code, in dem der 
Präprozessor in der Bedingung eines #if Statements mit der C Funktion 
pow() rumrechnet.

von Hmm (Gast)


Lesenswert?

A.K. schrieb:

>Zeig mal ...

Warum sollte ich das tun? Ich bezog mich auf die zitierte Aussage von 
Dir.
Die hat weder was mit dem Parameter eines Makros zu tun noch mit pow in 
der Bedingung eines Makros. Sondern nur damit ob #error Standard ist.

von (prx) A. K. (prx)


Lesenswert?

Hmm schrieb:
> Oder habe ich Dich falsch verstanden?

Ja.

von Hmm (Gast)


Lesenswert?

Deine Antworten sind mir zu lakonisch, A.K.

von (prx) A. K. (prx)


Lesenswert?

Ich hatte Steven so verstanden, dass er etwas machen will, was auf die 
beiden gestellten Fragen hinaus läuft. Und bestritten, dass das mit dem 
Präprozessor geht. Nicht aber bestritten, dass es #error gibt.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Also eine Frage hab ich noch.

A. K. schrieb:
> ... funktionsfähigen C Code, in dem der
> Präprozessor in der Bedingung eines #if Statements mit der C Funktion
> pow() rumrechnet.

geht es denn ohne pow?

von Hmm (Gast)


Lesenswert?

Das habe ich gleich in der ersten Antwort geschrieben:

"pow zur Basis 2 lässt sich durchaus durch eine Schiebeoperation
ersetzen."

von Steven (. (ovular) Benutzerseite


Lesenswert?

Hallo Mhm, das hab ich auch nicht vergessen.

Aber kann man bei

#define einMakro(einWert) einWert*2

prüfen, ob der Wert auch in einem gewissen Bereich liegt?

von (prx) A. K. (prx)


Lesenswert?

Mit dem C-Präprozessor selbst kann man das nicht.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Danke

von Rolf M. (rmagnus)


Lesenswert?

Allerdings kann man den Compiler nutzen, um es zur Compilezeit zu 
prüfen, was letztendlich auch ausreichen sollte. Der Klassiker dafür 
ist, aus dem Wert eine Array-Größe zu berechnen, die negativ wird, wenn 
der Wert nicht paßt. Der Compiler bricht dann mit Fehler ab, da negative 
Array-Größen nicht erlaubt sind.
Hier mal ein aus dem Internet geklautes Beispiel:
1
#define ct_assert(e) ((void)sizeof(char[1 - 2*!(e)]))
2
3
#define einMakro(einWert) einWert*2
4
5
int main()
6
{
7
  ct_assert(einMakro(5) == 10); // Ok
8
  ct_assert(einMakro(5) == 11); // Fehler
9
}

Da kommt ein Fehler vom Compiler. In der aktuellen Version von C gibt es 
dafür dann noch _Static_assert.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Das schaut vielversprechend aus.
Werd ich nachher gleich mal testen

Dankeschön!

Gruß Steven

von Steven (. (ovular) Benutzerseite


Lesenswert?

Jetzt verstehe ich auch so langsam das Assert, was troll vorgeschlagen 
hatte.

http://www.wire.tu-bs.de/ADV/texte/inf3text/node5.html

von (prx) A. K. (prx)


Lesenswert?

Konsequent durchgezogen kann man das beispielsweise in der 
Programmiersprache D bewundern: http://dlang.org/dbc.html

von Preprozessor (Gast)


Lesenswert?

>Mit dem C-Präprozessor selbst kann man das nicht.
Falsch !

Mit dem Preprozessor geht einiges. Es gibt Leute, die schreiben 
komplette Programme mit Preprozessor-Makros.

Prüfen von Macro-Parametern auf Glültikgeit ist eine Kleinigkeit.

Allerdings ist der Makro-Verhau nicht gerade übersichtlich und einfach 
zu verstehen. Eine kleine Demonstration der Möglichkeiten findet sich 
z.B. hier:

http://cakoose.com/wiki/c_preprocessor_abuse  oder hier:
http://www.boost.org/doc/libs/1_53_0/libs/preprocessor/doc/index.html

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wenn es nicht unbedingt im Makro selbst getestet werden muss:
<util/setbaud.h> in der avr-libc implementiert eine vergleichbare
Parameterprüfung.  Das funktioniert, indem man einige Makros gemäß
Anleitung in der Doku zuerst setzen muss, und erst danach
<util/setbaud.h> inkludiert.  Dieses modelt dann allerlei
Augabe-Makros daraus um (und kann auch #warnings werfen), die man
hernach im Code benutzen kann.

Braucht man es nochmal mit anderen Eingabeparametern, kann man diese
per #undef und #define neu setzen und den Header nochmals inkludieren.
Damit ist er einer der wenigen Header (wie auch <assert.h>), die sich
ausdrücklich nicht gegen mehrfaches inkludieren schützen.

Soviel nur mal als Anregung, vielleicht wäre das ja für den TE eine
sinnvolle Alternative.

von (prx) A. K. (prx)


Lesenswert?

Preprozessor schrieb:
>>Mit dem C-Präprozessor selbst kann man das nicht.
> Falsch !
>
> Mit dem Preprozessor geht einiges. Es gibt Leute, die schreiben
> komplette Programme mit Preprozessor-Makros.

Soweit mir erinnerlich wollte er nicht wissen, was man sonst alles mit 
dem Präprozessor machen kann, sondern wie man ganz konkret sein 
beschriebenes Problem damit lösen kann. Und da stehe ich bis zum Beweis 
des Gegenteil dazu, dass man unter ausschliesslicher Nutzung des 
Präprozessors die Werte von Makroparametern nicht überprüfen kann.

Wie schon skizziert wurde geht es jedoch in Zusammenspiel mit dem 
Compiler, also beispielsweise dem Array mit negativer Anzahl Elemente.

Jörg Wunsch schrieb:
> Wenn es nicht unbedingt im Makro selbst getestet werden muss:

Eben. Werte von #defines zu testen ist kein Problem. Dazu sind #if 
Statements in der Lage. Aber Makroparameter - no way.

von Preprozessor (Gast)


Lesenswert?

In etwa so:

Der Ausdruck 2*(440*pow(2,((taste-49)/12))) muß für alle möglichen Werte 
von 'taste' von Hand berechnet vorberechnet werden.

Das Ergebnis der gesamten Berechnung schreibt man wieder in ein #define 
dessen Wert man dann mit #if überprüfen kann.

Innerhalb der #if-Anweisung kann der Preprozessor mit den 
Grundrechenarten (+,-,/,*) umgehen. Float und math. Funktionen gehen 
nicht direkt aber wie beschrieben über eine Tabelle.
1
#define F_CPU 16000000
2
#define prescaler 64
3
#define TasteZuZaehlerwert(taste) floor((F_CPU/(prescaler*2*(440*pow(2,((taste-49)/12))))))
4
5
/* Wertetabelle (gerundet) für  2*(440*pow(2,((taste-49)/12)))  */
6
#define  taste_50    147
7
#define  taste_51    293
8
#define  taste_52    660
9
10
#define  Temp_pp(x) taste_##x
11
#define  Temp(x)    Temp_pp(x)
12
13
#define  Registerwert (F_CPU/(prescaler*Temp(50)))
14
15
#if (Registerwert > 255)
16
#error "Fehler"
17
#endif
18
19
OCR2 = Registerwert;

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Preprozessor schrieb:
> Das Ergebnis der gesamten Berechnung schreibt man wieder in ein #define
> dessen Wert man dann mit #if überprüfen kann.

Das entspricht ungefähr dem, was ich für <util/setbaud.h> meine.

Ist aber weit davon entfernt, die Überprüfung eines Makro-Parameters
zu sein.  Außerdem musst du natürlich alle möglichen Werte für
taste_* auskodieren.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Hallo,

für das Beispiel hat mir eine Exceltabelle die nötigen Werte berechnet.

Ich fände es gut, wenn der standard Präprozessor etwas mächtiger wäre.

Gruß
Steven

von troll (Gast)


Lesenswert?

Steven () schrieb:
> Ich fände es gut, wenn der standard Präprozessor etwas mächtiger wäre.
Da bist du nicht der Einzige. Aber man kann ja dank makefiles immer noch 
irgendein Skript vor den Compiler schalten. Oder m4(?).

von Simon G. (Gast)


Lesenswert?

Ich nutze folgendes um die größe eines Arrays (empfaengermaske) zu 
überprüfen:
1
typedef int dummy[sizeof(empfaengermaske) == KANAL_ENDE ? 1 : -1];

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon G. schrieb:
> Ich nutze folgendes um die größe eines Arrays (empfaengermaske) zu
> überprüfen

Gut und schön, aber was hat das mit dem Präprozessor (und damit dem
Thema des Threads) zu tun?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Schreib doch einfach ein kleines Programm, das übersetzt und dann 
ausgefüht wird.

Im Fehlerfalle bricht make den Build-Prozess ab. Damit kommt der Fehler 
zwar nicht vom Präprozessor, aber das spielt im Endeffekt bestimmt nur 
eine untergeordnete Rolle.  Weiterer Vorteil ist, daß die avr-Quelle 
einfacher wird.

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.