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?
@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;
}
//-------------------------------------
...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. ;-)
> 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.
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.
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:
> 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.