www.mikrocontroller.net

Forum: Compiler & IDEs C Preprocessor, Gluebefehl ##


Autor: sous (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frage: Warum macht der Präprozessor nicht das was ich will, bzw. kann 
ich ihn irgendwie dazu bringen, es doch zu tun?

Mein Beispiel-Quelltext (pre.c):
//---------------------------------------
#define PORTCHAR B

#define PORTDIR  DDR##PORTCHAR
#define OUTPORT  PORT##PORTCHAR
#define INPORT  PIN##PORTCHAR



int main()
{
  PORTDIR = 0xff;
  OUTPORT = 0x01;

  while(1);
  return 0;
}
//------------------------------------------

Wir durch folgenden Aufruf
avr-gcc pre.c -E >> pre_out.c

umgewandelt in das hier (pre_out.c):
//------------------------------------------

# 1 "prep.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "prep.c"
# 9 "prep.c"
int main()
{
 DDRPORTCHAR = 0xff;
 PORTPORTCHAR = 0x01;

 while(1);
 return 0;
}
//------------------------------------------

Was ich gerne gehabt hätte:
Dass da statt 'DDRPORTCHAR = 0xff;'
'DDRB = 0xff;' steht usw.

Mal abgesehen davon, dass mir im Nachhinein noch ein Hinderniss 
aufgefallen ist, selbst wenn er jetzt 'DDRB' aus 'DDR##PORTCHAR' macht, 
müsste er dieses dann auch noch durch die Definitionen des 
avr/io.h-Files austauschen (habe ich hier erstmal mit Absicht nicht 
includiert, ich wollte zunächst die reine Glue-Operation erforschen).

Kann jemand etwas dazu sagen oder weiß, wie man sowas richtig macht?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#define CONCATx(a,b) a##b
#define CONCAT(a,b) CONCATx(a,b)

#define PORTCHAR B

#define PORTDIR  CONCAT(DDR,PORTCHAR)
#define OUTPORT  CONCAT(PORT,PORTCHAR)
#define INPORT   CONCAT(PIN,PORTCHAR)

Autor: sous (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für Eure Hilfe!

@ Johannes M.:
Das funktioniert leider nicht:

aus
//----------------------------------
#define PORTCHAR B

#define PORTDIR1(x) DDR##(x)
#define PORTDIR2(x) DDR##x

int main()
{
  PORTDIR1(PORTCHAR) = 0xff;
  PORTDIR2(PORTCHAR) = 0xff;

  PORTDIR1(B) = 0xff;
  PORTDIR2(B) = 0xff;
}
//----------------------------------

wird

//----------------------------------
int main()
{
 DDR(B) = 0xff;
 DDRPORTCHAR = 0xff;

 DDR(B) = 0xff;
 DDRB = 0xff;
}
//----------------------------------

Autor: sous (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Stefan Ernst:

Juhu, das funktioniert!
Ich bin zwar leider g'rad zu blöd, zu verstehen, warum (so und nicht 
anders) und werde noch eine Weile darüber nachdenken aber auf jeden Fall 
kann man es so machen!

Vielen Dank!  :)


aus
//-------------------------------------
#define CONCATx(a,b) a##b
#define CONCAT(a,b) CONCATx(a,b)

#define PORTCHAR B

#define PORTDIR  CONCAT(DDR,PORTCHAR)
#define OUTPORT  CONCAT(PORT,PORTCHAR)
#define INPORT   CONCAT(PIN,PORTCHAR)


int main()
{
  PORTDIR = 0xff;
  OUTPORT = 0x01;
}

//-------------------------------------

wird (wie gewünscht)
//-------------------------------------
int main()
{
 DDRB = 0xff;
 PORTB = 0x01;
}
//-------------------------------------

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sous wrote:
> @ Johannes M.:
> Das funktioniert leider nicht:
Das habe ich auch gemerkt.

Autor: sous (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...und meine Befürchtung bzgl. des Ersetzens durch die io.h-Definitionen 
waren auch unbegründet:
Nach Einfügen von #include <avr/io.h> und Preprocessor-Aufruf mit der 
entsprechenden -mmcu-Spezifikation ergibt sich das hier:

int main()
{
 (*(volatile uint8_t *)((0x17) + 0x20)) = 0xff;
 (*(volatile uint8_t *)((0x18) + 0x20)) = 0x01;
}

und ich denke, das sieht gut aus!

Danke nochmals Euch beiden, die Ihr mir geantwortet habt!

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sous wrote:

> Ich bin zwar leider g'rad zu blöd, zu verstehen, warum (so und nicht
> anders) und werde noch eine Weile darüber nachdenken aber auf jeden Fall
> kann man es so machen!

Das muss man glaub ich auch nicht verstehen, es genügt, wenn man
weiß, wie's geht. ;-)

Autor: sous (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die aufmunternden Worte. :)
Ich bin aber so einer, der sich wohler fühlt, wenn er auch (wenigstens 
halbwegs) versteht, was er da tut....

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Das muss man glaub ich auch nicht verstehen, es genügt, wenn man weiß,
> wie's geht. ;-)

Man kann es temporär verstehen, wenn man sich den Präprozesorabschnitt
im C-Standard oder folgenden Abschnitt im GNU-CPP-Manual durchliest:

  http://gcc.gnu.org/onlinedocs/gcc-4.3.2/cpp/Argume...

Das Verständnis verblasst aber typischerweise schon nach etwa einer
Stunde, da die Reihenfolge der Expansion von Makros und Makroargumenten
etwas undurchsichtig ist, vor allem im Zusammenhang mit dem #- und dem
##-Operator ;-)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
yalu wrote:

> Das Verständnis verblasst aber typischerweise schon nach etwa einer
> Stunde, ...

Das deckt sich ziemlich mit meinen Erfahrungen beim Lesen dieser
Beschreibungen. ;-)  Seitdem versuche ich das gar nicht mehr zu
verstehen; mir genügt es vollauf zu wissen, dass es (irgendwie)
schon geht, dass dieses ,,es geht'' gesetzmäßig (bzw. standardgemäß)
ist und dass man es im Zweifelsfall durch Probieren rausfinden kann,
wie es genau anzustellen ist. :)

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mir ist es eigentlich auch lieber wenn ich den Dinge so verstehe, dass 
ich weiss wann was geht oder nicht geht und warum. Bei den Compilern 
selbst gelingt mir das auch einigermassen. Von ebendiesen Aspekten des 
C-Präprozessors kann ich das leider nicht behaupten.

Dazu kommt, dass der exakte Umgang des Präprozessors mit geschachtelten 
Makros und dem ## Operator im Standard nicht wirklich ins Gesicht 
springt. Ich ware nicht erstaunt, wenn eine kunstvoll zurecht 
geschnörkelte Version bei einem anderen Compiler nur zu einem hässlichen 
Ätsch! führt.

Bei AVRs verwende ich gerne Makros für Portzugriffe, so dass zentral
  #define ONEW_PORT A
  #define ONEW_PIN  2
konfiguriert wird, und im Code dann
  setPortBit(ONEW_PORT, ONEW_BIT);
  setDirBit(ONEW_PORT, ONEW_BIT);
für
  PORTA |= 1<<2;
  DDRA |= 1<<2;
steht.

Ein virtueller Preis gebührt dem, der mir daraus Microchips Konvention 
ähnlich
  portAbits.portAbit2 = 1
  trisAbits.trisAbit2 = 1
zimmern kann, bei dem das A links wie rechts mitten im Namen vorkommt. 
Müsste eigentlich irgendwie gehen, hab's aber nicht hinbekommen.

Drum also: viel Glück.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aus
setPortBit(ONEW_PORT, ONEW_BIT);
soll
portAbits.portAbit2 = 1;
werden?

#define CONCAT6x(a,b,c,d,e,f) a##b##c##d##e##f
#define CONCAT6(a,b,c,d,e,f) CONCAT6x(a,b,c,d,e,f)

#define setPortBit(a,b) (CONCAT6(port,a,bits.port,a,bit,b) = 1)

#define ONEW_PORT A
#define ONEW_BIT  2

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Ich ware nicht erstaunt, wenn eine kunstvoll zurecht geschnörkelte
> Version bei einem anderen Compiler nur zu einem hässlichen Ätsch!
> führt.

Das war in der Vergangenheit tatsächlich ein Problem. In einem
GNU-CPP-Manual aus dem vorigen Jahrtausend gab es eine Passage, wo sich
die GNU-Entwickler damit brüsteten, im Gegensatz zu den Wettbewerbern
den Standard verstanden zu haben. Sie führten ein Beispiel¹ an, was von
den C-Präprozessoren der meisten anderen Hersteller nicht korrekt
verarbeitet wurde.

Ich könnte mir gut vorstellen, dass diese Passage den anderen
Compilerentwicklern erstmals das "Obacht" zurief, das im Standard nicht
oder nicht laut genug zum Ausdruck kommt, und deswegen übersehen wurde.
Einige Jahre später hat's dann auch der MS-Compiler richtig gemacht.

———————————————
¹) Ich glaube, es hatte etwas mit dem Stringifizierungsmakro zu tun, das
   mit dem hier besprochenen verwandt ist.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst wrote:

 #define setPortBit(a,b) (CONCAT6(port,a,bits.port,a,bit,b) = 1)

Danke. Ich war da wohl etwas zu kompliziert unterwegs.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich verwende auch solche Makros, um die Definition eines Ports an 
einer zentralen Stelle zu habn. So ist es in der Entwicklungsphase 
einfach umzustellen, vor allem auch zwischen normalen Ports wie PORTC_3 
und virtuellen Ports (SERPA), die z.B. über SPI an einen 
seriell-parallel-Wandler wie 74*595 ausgegeben werden.
enum
{
  PORT_LED = PORTC_2,
  
  PORT_SENSE = PORTC_3,
  PORT_NIXIE_POWER = PORTC_5,
  
  PORT_PWMA = PORTB_1,
  PORT_PWMB = PORTB_2,  // SS
  PORT_PWMC = SERPA2_0,
  PORT_PWMD = SERPA2_1,
  PORT_PWME = SERPA2_2,
...

Das wird dann so verwendet:
   MAKE_OUT (PORT_LED); // Port als Ausgang
   SET      (PORT_LED); // Port HIGH
   CLR      (PORT_LED); // Port LOW
   MAKE_IN (PORT_SENSE); // Port als Eingang...
   SET     (PORT_SENSE); // ...mit PullUp
   if (IS_SET (PORT_SENSE)) // SENSE == 1 ?
       TOGGLE (PORT_PWMA); // PWM.A = !PWM.A

Aber die Makros dazu will man sich nicht wirklich antun ;-)

Autor: bronkopavel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nabend,

hab folgendes Problem:

#define ARGUMENT                   (D, 4)
#define CONCAT(a,b)                 a##b
#define SET_ARGUMENT(x, y)    CONCAT(PRE, x)  = y;

SET_ARGUMENT(ARGUMENT)

Irgendwie hab ich da nen Fehler, der Compiler sagt, das ich im nur 1 
Argument liefere nicht 2?!

Hat einer ne Idee?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich sehe da auch nur ein Argument. Ist eine Frage, in welcher 
Reihenfolge die Ersetzungen des Präprozessors stattfinden.

Und wenn man Pech hat, macht der Präprozessor das auch noch falch 
(Microchip C18).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bronkopavel schrieb:
> Nabend,
>
> hab folgendes Problem:
>
> #define ARGUMENT                   (D, 4)
> #define CONCAT(a,b)                 a##b
> #define SET_ARGUMENT(x, y)    CONCAT(PRE, x)  = y;
>
> SET_ARGUMENT(ARGUMENT)
>

Du musst eine Zwischenstufe einlegen und die Klammern bei ARGUMENT 
entfernen
#define ARGUMENT                   D, 4
#define CONCAT(a,b)                 a##b
#define SET_ARG(x, y)    CONCAT(PRE, x)  = y;
#define SET_ARGUMENT(x)  SET_ARG(x)

SET_ARGUMENT(ARGUMENT)

Der Zwischenschritt ist notwendig um dem Präprozessor Gelegenheit zu 
geben x durch D, 4 zu ersetzen.

Autor: bronkopavel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke,

klappt perfekt, leider verstehe ich es nicht (die Zwischenstufe).
Ich dachte bei sowas wird einfach blind der "Text ersetzt"?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bronkopavel schrieb:
> klappt perfekt, leider verstehe ich es nicht (die Zwischenstufe).

Meine Annahme:
Die Überprüfung der Argumentenzahl wird nur bei der ersten 
Macroexpansion gemacht.
D.h. egal wieviel Macros geschachtelt sind, es reicht nur beim ersten 
mal die richtige Argumentenzahl vorzugaukeln.


Peter

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bronkopavel schrieb:
> Danke,
>
> klappt perfekt, leider verstehe ich es nicht (die Zwischenstufe).
> Ich dachte bei sowas wird einfach blind der "Text ersetzt"?

Schon.
Die Frage ist, wie A.K. schon angedeutet hat, welche Ersetzung zuerst 
gemacht wird. Die Regeln dafür sind etwas kompliziert und 
undurchsichtig. Würde mich wundern, wenn es auf diesem Planeten mehr als 
eine Handvoll Leute gibt, die sie komplett verstanden haben :-) Ich 
gehöre auf jeden Fall nicht dazu. Aber mit der Attitüde "Wenns direkt 
nicht geht, für ein Zwischendefine ein", kommt man eigentlich in den 
meisten Fällen ganz gut zurecht.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.