Präprozessor Kontrolle der Übergabe

Persönliche Seite #3152083
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
Gast #3152094
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.
Persönliche Seite #3152102
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...
#3152149
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.
#3152178
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".
Gast #3152232
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?
#3152247
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.
Gast #3152283
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.
#3153320
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.
Gast #3174373
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
Moderator Persönliche Seite #3174389
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.
#3174402
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.
Gast #3174514
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;
Moderator Persönliche Seite #3174555
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren