sauberes #define

Gast #364339
Lesenswert?

Hallo Forenuser.
ich habe mir aus den Codebeispielen diese schönen Zeilen zum setzen und
löschen von Bits kopiert
  #define setbit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
Nun möchte ich aber dieses weitertreiben, so dass ich Proceduren auch
auf anderen Ports oder anderen Prozessoren benutzen kann.
also z.B. einen gepulsten Port für IR-Kommunikation.
Wenn ich das in dieser Form mache
 #define ir_transmit PORTA,PA6
gibts einen Fehler bei
setbit(ir_transmit);
mit der Begründung
macro "setbit" requires 2 arguments, but only 1 given
Die Fehlermeldung ist klar.
Versteht der Compiler nicht, dass es sich bei ir_transmit um 2
Argumente handelt?
Wie macht man es richtig?
Gast #364341
Lesenswert?

Das SETBIT ist selbst schon ein Makro. Der Präprozessor merkt sich eine
Liste von allen Defines zum späteren ersetzen. Wenn im Sourcecode die
vorgesehene Ersetzung von SETBIT vor der Ersetzung von IR_TRANSMIT
abgearbeitet wird, kommt die Fehlermeldung. Du müsstest zusehen, dass
das define von IR_TRANSMIT dem Präprozessor zuerst bekannt wird.

Tipp: Wenn man Makros grundsätzlich nur in Grossbuchstaben schreibt,
sieht man eher was im Sourcecode los ist.
Gast #364342
Lesenswert?

"Schräg, ja, ich weiß. :-)"


Wow !

Die Typüberprüfung erfolgt also nur bei der ersten Macroexpansion und
läßt sich durch 3-fach Macros überlisten.

Ist das nun ein Bug oder ein Feature des AVR-GCC ?

Ist das auch bei anderen Compilern so ?


Peter



P.S.:
Muß ich gleich mal ausprobieren.
Gast #364344
Lesenswert?

> Ist das nun ein Bug oder ein Feature des AVR-GCC ?

Kein Bug! Schau mal da:

http://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html#Macro-Arguments

Wichtig ist folgender Abschnitt:

All arguments to a macro are completely macro-expanded before they are
substituted into the macro body. After substitution, the complete text
is scanned again for macros to expand, including the arguments. This
rule may seem strange, but it is carefully designed so you need not
worry about whether any function call is actually a macro invocation.
You can run into trouble if you try to be too clever, though. See
Argument Prescan, for detailed discussion.

> Ist das auch bei anderen Compilern so ?

Das ist das im ISO-Standard so vorgesehen. Allerdings gab es in der
Vergangenheit etliche Compiler bzw. Präprozessoren (einschließlich
derjenigen von Microsoft), die diese Regel falsch umsetzten, so dass
das entweder beim Konstrukt von flyingwolf fälschlicherweise kein
Fehler oder bei dem von Jörg fälschlicherweise ein Fehler ausgegeben
wurde.
Gast #364346
Lesenswert?

Die Variante von Jörg funktioniert auch bei Digital Mars, nicht jedoch
bei Microsoft (warning C4003: Nicht genügend übergebene Parameter für
das Makro 'setbit2').

Eine Variante, die aber bei allen korrekten Präprozessoren
funktionieren sollte, und die sich elegant auf Funktionen auf die
PINA/DDRA Register erweitert:
1
#define ir_transmit_PORT  A
2
#define ir_transmit_PIN   6
3

4
#define p_port(x)  x##_PORT
5
#define p_pin(x)  x##_PIN
6
#define p_in(x)  PIN##x
7
#define p_out(x)  PORT##x
8
#define p_dir(x)  DDR##x
9
#define pp_in(x)  p_in(x)
10
#define pp_out(x)  p_out(x)
11
#define pp_dir(x)  p_dir(x)
12

13
#define testbit(x) (pp_in(p_port(x)) & 1<<p_pin(x))
14
#define setbit(x)   (pp_out(p_port(x)) |= 1<<p_pin(x))
15
#define clrbit(x)   (pp_out(p_port(x)) &= ~(1<<p_pin(x)))
16
#define setout(x)   (pp_dir(p_port(x)) |= 1<<p_pin(x))
17
#define setinp(x)   (pp_dir(p_port(x)) &= ~(1<<p_pin(x)))
18

19
void
20
f(void)
21
{
22
    if (testbit(ir_transmit)) {
23
  do_something();
24
    }
25
    setout(ir_transmit);
26
    setbit(ir_transmit);
27
}

Resultat ist:
1
void
2
f(void)
3
{
4
    if ((PINA & 1<<6)) {
5
        do_something();
6
    }
7
    (DDRA |= 1<<6);
8
    (PORTA |= 1<<6);
9
}
Gast #364349
Lesenswert?

Bitfelder = struct?
Da habe ich letztens was interessantes gesehen. Alle Variablen waren in
einer Struktur definiert auch die 1-Bit-Vars und dann wurde die ganze
Structur ins EEProm geschrieben.
Gast #364353
Lesenswert?

Solange es um einzelne Bits geht, kann der Compiler was draus machen -
wenngleich ich auch schon abschreckende Beispiele gesehen habe (weiss
bloss nicht mehr wo).

Der Haken bei Bitfeldern liegt aber wesentlich auch darin, dass man
keine Chancen hat, es besser zu machen. Jedenfalls nicht solange man
bei den Bitfeldern bleibt und nicht nebenbei noch die klassischen
Register und Bits pflegt. Sowas wie
   ControlRegister |= 1<<Enable | 1<<CrashAndBurn;
ist bei Bitfeldern nicht drin, und
   ControlRegister.Enable = 1;
   ControlRegister.CrashAndBurn = 1;
darf bei dem natürlich als "volatile" deklarierten ControlRegister
kein Compiler legal zusammenfassen (wenngleich SDCC sowas ähnliches
fertigbringt). Mit CBI/SBI ist das ok, u.U. sogar besser, bei nicht
bitweise adressierbaren Adressen zieht sich das jedoch arg in die
Länge.

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