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?
1 | #define CONCATx(a,b) a##b
|
2 | #define CONCAT(a,b) CONCATx(a,b)
|
3 | |
4 | #define PORTCHAR B
|
5 | |
6 | #define PORTDIR CONCAT(DDR,PORTCHAR)
|
7 | #define OUTPORT CONCAT(PORT,PORTCHAR)
|
8 | #define INPORT CONCAT(PIN,PORTCHAR)
|
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;
}
//----------------------------------
@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;
}
//-------------------------------------
sous wrote: > @ Johannes M.: > Das funktioniert leider nicht: Das habe ich auch gemerkt.
...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!
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. ;-)
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....
> 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/Argument-Prescan.html#Argument-Prescan 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 ;-)
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. :)
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.
Aus
1 | setPortBit(ONEW_PORT, ONEW_BIT); |
soll
1 | portAbits.portAbit2 = 1; |
werden?
1 | #define CONCAT6x(a,b,c,d,e,f) a##b##c##d##e##f
|
2 | #define CONCAT6(a,b,c,d,e,f) CONCAT6x(a,b,c,d,e,f)
|
3 | |
4 | #define setPortBit(a,b) (CONCAT6(port,a,bits.port,a,bit,b) = 1)
|
5 | |
6 | #define ONEW_PORT A
|
7 | #define ONEW_BIT 2
|
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.
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.
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.
1 | enum
|
2 | {
|
3 | PORT_LED = PORTC_2, |
4 | |
5 | PORT_SENSE = PORTC_3, |
6 | PORT_NIXIE_POWER = PORTC_5, |
7 | |
8 | PORT_PWMA = PORTB_1, |
9 | PORT_PWMB = PORTB_2, // SS |
10 | PORT_PWMC = SERPA2_0, |
11 | PORT_PWMD = SERPA2_1, |
12 | PORT_PWME = SERPA2_2, |
13 | ...
|
Das wird dann so verwendet:
1 | MAKE_OUT (PORT_LED); // Port als Ausgang |
2 | SET (PORT_LED); // Port HIGH |
3 | CLR (PORT_LED); // Port LOW |
4 | MAKE_IN (PORT_SENSE); // Port als Eingang... |
5 | SET (PORT_SENSE); // ...mit PullUp |
6 | if (IS_SET (PORT_SENSE)) // SENSE == 1 ? |
7 | TOGGLE (PORT_PWMA); // PWM.A = !PWM.A |
Aber die Makros dazu will man sich nicht wirklich antun ;-)
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?
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).
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
1 | #define ARGUMENT D, 4
|
2 | #define CONCAT(a,b) a##b
|
3 | #define SET_ARG(x, y) CONCAT(PRE, x) = y;
|
4 | #define SET_ARGUMENT(x) SET_ARG(x)
|
5 | |
6 | SET_ARGUMENT(ARGUMENT) |
Der Zwischenschritt ist notwendig um dem Präprozessor Gelegenheit zu geben x durch D, 4 zu ersetzen.
Danke, klappt perfekt, leider verstehe ich es nicht (die Zwischenstufe). Ich dachte bei sowas wird einfach blind der "Text ersetzt"?
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
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.