Forum: PC-Programmierung C Preprozessor-Trickserei


von Moriz (untertaucher)


Lesenswert?

Mit dem C Preprozessor kann man Symbole generieren, in deren Namen die 
Zeilennummer steckt, an der sie erzeugt wurden:
1
#define CAT(x, y)  x ## y
2
#define CAT2(x, y) CAT(x, y)
3
#define VARIABLE   CAT2(variable, __LINE__)
4
5
int VARIABLE;      // erzeugt variable5

So weit so schön. Aber kann man das noch weiter treiben?
Ich hätte gerne ein Macro, das in einem Header ganz oben im Quellfile 
definiert ist und in der Expansion ein Symbol einsetzt, das zur 
Definitionszeit des Macros noch gar nicht existiert:
1
// Header
2
#define SPEZIAL()    spezial(<MODULE_ID>, <__LINE__>)
3
4
// Ende des Headers
5
6
#define MODULE_ID    13
7
8
SPEZIAL()            // soll expandieren zu spezial(13, 8)

Geht sowas überhaupt und wenn ja, wie bekommt man es hin?

: Bearbeitet durch User
von Moriz (untertaucher)


Lesenswert?

Ich habs rausgefunden:
1
#define CAT(x, y) x, y
2
#define CAT2(x, y) CAT(x, y)
3
#define SPEZIAL()         spezial(CAT2(MODULE_ID, __LINE__))
4
5
#define MODULE_ID         51
6
7
SPEZIAL()                 // expandiert zu spezial(51, 7)

von Christian M. (likeme)


Lesenswert?

我不明白

von Thorsten S. (thosch)


Lesenswert?

Christian M. schrieb:
> 我不明白

很搞笑

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Der Klassiker ist
1
__FILE__, __LINE__

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Vielleicht kannst du statt einer Modul ID einfach das `static` keyword 
nehmen? Und schau dir eventuell auch mal `__attribute__((constructor))` 
an. Für innerhalb von Funktionen, schau dir scope blöcke und compound 
literale an. `{ int x; }`, `(int){123}`.

Wenn man die geschickt einsetzt, braucht man meistens gar keine Hacks 
mit solchen magischen Modulnummern mehr, und oft kann man auch ohne 
`__LINE__` auskommen.

von Moriz (untertaucher)


Lesenswert?

Daniel A. schrieb:
> Vielleicht kannst du statt einer Modul ID einfach das `static` keyword
> nehmen?

Ich habe nicht geschrieben, wofür ich das brauche… Das hole ich jetzt 
nach:

Mein Programmpaket enthält einen Header namens config.h, der in alle 
Module an erster Stelle nach den Library-Includes includet wird. Dort 
stehen die grundlegenden Generierungsparameter drin und unter anderem 
auch die Modul-IDs für jeden .c-File und die Fehlernummern, die in dem 
jeweiligen Modul vorkommen. (Die ModulID und Fehlernummer kann bei 
fatalen Fehlern mit einer LED signalisiert werden und anhand der Nummern 
findet man mittels config.h sehr schnell die Stelle, an der es gerumst 
hat.)

In jedem .c-Modul steht nach den includes ein
   #define MODULE_ID MODULE_MAIN_C
oder wie der Modul auch immer heißt. (In config.h ist MODULE_MAIN_C als 
Zahlwert definiert.)

So weit das Vorgeplänkel, das den Trick mit dem cpp lohnend macht. Nun 
zum aktuellen Problem: ich will den Watchdog so integrieren, dass er dem 
Programm mit einem Reset wieder auf die Beine hilft, wenn es sich 
irgenwo verlaufen hat.

Dummerweise hatte ich in etlichen Modulen und teilweise ganz tief in den 
Hardware-Dingen wdt_rest() Aufrufe eingebaut. Als ich nun die wdt_reset 
an kritischen Stellen im Programm testweise lahmlegte, hätte der WD 
einen Reset machen sollen – hat er aber nicht, weil eben irgendwo noch 
andere wdt_reset-Aufrufe waren…

Was tun?

Ich habe in die config.h folgendes Fragment eingefügt:
1
    #ifndef ALLOW_WDT_ACCESS
2
        #undef wdt_reset
3
        void __wdt_reset(uint8_t moduleID, uint16_t line);
4
5
        #define WDT_CAT(x, y)       x, y
6
        #define WDT_CAT2(x, y)      WDT_CAT(x, y)
7
8
        #define wdt_reset()         __wdt_reset(WDT_CAT2(MODULE_ID, __LINE__))
9
    #endif // ALLOW_WDT_ACCESS

Nur in watchdog.c ist ALLOW_WDT_ACCESS definiert – damit kann nur der 
ein echtes wdt_reset ausführen.

Alle andern Module expandieren statt dem üblichen wdt_reset aus 
avr/wdt.h zu
1
__wdt_reset(MODULE_ID, __LINE__)

Die Funktion, die statt des Macros aufgerufen wird sieht so aus:
1
void __wdt_reset(uint8_t moduleID, uint16_t line) {
2
    uart_puts_P(PSTR("wdt_reset: "));
3
    uintOut(moduleID);
4
    uart_putchar(',');
5
    uintOut(line);
6
    nl();
7
8
    wdt_reset();
9
}

Weil sie in watchdog.c liegt, wird dort wdt_reset aus avr/wdt.h 
ausgeführt.

Damit ist mit geringsten Eingriffen eine Trace-Funktion für wdt_reset 
implementiert, die sich natürlich mit noch weniger Handgriffen wieder 
deaktivieren lässt…

: Bearbeitet durch User
von Moriz (untertaucher)


Lesenswert?

Christian M. schrieb:
> 我不明白
无所谓

von Bauform B. (bauformb)


Lesenswert?


von Moriz (untertaucher)


Lesenswert?

_FUNCTION_ und Consorten sind für kleine Microcontroller eher 
ungeeignet.

von Bauform B. (bauformb)


Lesenswert?

Aber _func_ auch? Wenn der TO sowieso Text ausgibt?

von Moriz (untertaucher)


Lesenswert?

Ja auch. Überleg dir einfach mal, wie oft der Text in meinem Code 
vorkommt und wie oft _func_ bei einer an vielen Stellen aufgerufenen 
Funktion im Maschinencode vorkäme.

Auf "richtigen" Rechnern kann man sowas machen, auf einem 328, der nicht 
mehr so viel Platz im Flash hat, eher nicht.

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

Moriz schrieb:
> Damit ist mit geringsten Eingriffen eine Trace-Funktion für wdt_reset
> implementiert,

Wenn ich das richtig sehe, brauchst Du keine Makroexpansion dazwischen.

Wenn Du eine echte Funktion aufrufst, werden Makros von allein ersetzt.
1
 #define wdt_reset() __wdt_reset(MODULE_ID, __LINE__)

sollte reichen. CAT2 & Co brauchst Du nur für Function-Like-Makros mit 
Parametern.

von Moriz (untertaucher)


Lesenswert?

Bruno V. schrieb:
> Wenn ich das richtig sehe, brauchst Du keine Makroexpansion dazwischen.

Du hast Recht.

Damit vereinfacht sich das Fragment für config.h:
1
    #ifndef ALLOW_WDT_ACCESS
2
        #undef wdt_reset
3
        void __wdt_reset(uint8_t moduleID, uint16_t line);
4
5
        #define wdt_reset()         __wdt_reset(MODULE_ID, __LINE__)
6
    #endif // ALLOW_WDT_ACCESS

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Macros und kaskadierte Macros werden immer erst an der Stelle des 
Aufrufs expandiert.
Die Zeile und die Reihenfolge der Definitionen spielt keine Rolle.
Eine Ausnahme bildet #undef, das ist genau ab dieser Zeile gültig.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Peter D. schrieb:
> Eine Ausnahme bildet #undef, das ist genau ab dieser Zeile gültig.

Das ist aber keine Ausnahme, auch ein #define ist erst ab der Zeile 
gültig, in der es steht.

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
Noch kein Account? Hier anmelden.