C Preprozessor-Trickserei

OP #7366276
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?

#7366346
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.

OP #7366574
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…

OP #7366628
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.

#7366660
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.

Antwort schreiben

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.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.