[C] Weitergabe variabler Anzahl an Argumenten

Gast #6605332
Lesenswert?

Hallo zusammen,

ich sitze an einem BareMetall-Programm für den (CortexM4 des i.MX8MM) 
und möchte der PRINTF-Funktion von NXP einen Loglevel hinzufügen. Die 
PRINTF-Funktion unterstützt eine variable Anzahl an Argumenten.

Dazu möchte ich ebenfalls eine Log-Funktion mit Variabler Anzahl von 
Argumenten implementieren, die nach dem Loglevel schaut und - sofern er 
paßt - einfach die Argumente 1:1 an die PRINTF-Funktion von NXP 
weitergibt.

Weiß jemand wie das geht? Eine Anzahl variabler Argumente einfach an 
einen Call weitergeben?

Vielen Dank für Eure Hilfe,
Christoph
Gast #6605348
Lesenswert?

Jörg W. schrieb:
> vprintf bzw. vfprintf is your friend

Danke Dir. Freunde schon, die Nützlichkeit sehe ich gerade nicht so. 
vprintf will auch printen, das muss jedoch die PRINTF-Funktion von nxp, 
welche es auf dem UART ausgibt und vfprintf möchte einen FILE-Pointer 
als Ziel beglücken.

Ich bewege mich bare-metall auf dem CortexM4. Programmiersprache ist C.
Moderator Persönliche Seite #6605353
Lesenswert?

Christoph F. schrieb:
> Ich bewege mich bare-metall auf dem CortexM4. Programmiersprache ist C.

Ja, und? Habe ich auch oft genug gemacht. Die Standardbibliothek bei 
ARM-GCC ist typischerweise ja eine newlib, und die hat die komplette 
printf-Familie an Bord. Man muss dann halt nur das Backend 
implementieren (_write in diesem Falle).

Heißt die Funktion wirklich PRINTF (in Großbuchstaben)? Dann hast du so 
keine Chance. Wenn sie kein "v"-Pendant dazu liefern, dem man eine 
va_list als Argument überreichen kann, ist deine Aufgabe so schlicht 
nicht lösbar.
Gast #6605368
Lesenswert?

Jörg W. schrieb ...:
> vprintf bzw. vfprintf is your friend

So grundsätzlich war das mit dem v und printf schon gut. Mit vsnprintf() 
aus dem Hause "stdio.h" bin ich nun glücklich geworden.

> Heißt die Funktion wirklich PRINTF (in Großbuchstaben)?
Ja. Ist leider so.

Herzlichen Dank für die schnellen und guten Tipps.

-Christoph
Moderator Persönliche Seite #6605375
Lesenswert?

Christoph F. schrieb:

> So grundsätzlich war das mit dem v und printf schon gut. Mit vsnprintf()
> aus dem Hause "stdio.h" bin ich nun glücklich geworden.

Naja gut, musst du halt erst einen Puffer bereitstellen und dann hoffen, 
dass er immer groß genug ist.

>> Heißt die Funktion wirklich PRINTF (in Großbuchstaben)?
> Ja. Ist leider so.

Würde ich wegwerfen und durch Standard-printf ersetzen bzw. habe dies 
auch so getan.
Moderator #6605395
Lesenswert?

Das PRINTF von NXP ist ein Macro, das wahlweise in DbgConsole_Printf
oder das gewöhnliche printf expandiert wird. Natürlich kann man das
printf auch direkt aufrufen.

Man könnte auch das Macro printf (also gleich wie die zugrundeliegende
Bibliotheksfunktion) nennen, nur ist dann nicht mehr direkt ersichtlich,
dass es sich um ein Macro handelt.
Moderator Persönliche Seite #6605403
Lesenswert?

Man könnte aber auch das Backend so umbauen, dass es die Funktion dieses 
DbgConsole_Printf abstrahiert. Dann braucht man den Makro-Kram nicht, 
und kann sogar zur Laufzeit umschalten, wenn man will.

Bei uns sieht das dann ungefähr so aus:
1
   freopen(":uart0", "r", stdin);
2
   freopen(":uart0", "w", stdout);
3
#ifdef DEBUG
4
   freopen(":uart1", "w", stderr);
5
#else
6
   freopen(":null", "w", stderr);
7
#endif

_open durchläuft dann für all Gerätedateinamen (mit Doppelpunkt 
beginnend) eine Liste von vorher registrierten Treibern. Alle anderen 
Namen gehen auf die SD-Karte, sofern eine solche in der Konfiguration 
vorhanden ist.
Moderator Persönliche Seite #6608161
Lesenswert?

Rolf M. schrieb:
> Jetzt könnte man da statt des : ein ∕dev∕ davor stellen und davon auch
> ein Directory-Listing anbieten, dann ist es schon fast wie auf einem
> Unix-System.

Ja, fand ich nur nicht weiter nützlich. Der einzelne Doppelpunkt tut's 
auch (und ist insbesondere viel einfacher zu analysieren als ein 
abschließender Doppelpunkt wie in "LPT1:" oder "NUL:"). Klar ist die 
newlib Posix-zentrisch aufgebaut, das sieht man an den 
Backend-Funktionen, auf denen sie aufsetzen möchte (die letztlich 
Posix-Syscalls sind), aber für eine simple MCU muss man das auch nicht 
übertreiben.

(prx) A. K. schrieb:
>> Naja gut, musst du halt erst einen Puffer bereitstellen und dann hoffen,
>> dass er immer groß genug ist.
>
> So ein Tipp in dieser Zeit... hoffentlich kein IoT-Device. ;-))

Er schrieb ja was von vsnprintf(), also wird er wohl hoffentlich die 
passende Länge übergeben. Ist dann trotzdem doof, wenn von "Diese 
Meldung ist superla" die letzten Buchstaben abgeschnitten werden …
Gast #6608423
Lesenswert?

Jörg W. schrieb:
> Christoph F. schrieb:
>
>> Mit vsnprintf() aus dem Hause "stdio.h" bin ich nun glücklich geworden.
>
> Naja gut, musst du halt erst einen Puffer bereitstellen und dann hoffen,
> dass er immer groß genug ist.

Das tut die Funktion von NXP auch. Die vsnprintf() hat die Pufferlänge 
als Argument, sollte also - oberflächlich betrachtet - sicher sein und 
die Ausgaben kürzen.

Das SDK von NXP soll in dem Projekt unangetastet bleiben, damit es 
einfacher getauscht werden kann.

Es geht auch nur um ein paar eigene Hilfs-Ausgaben (debug/info/error) in 
der Geräteinternen i.MX8MM/CM4-Console, wo nur Entwickler dran kommen.

Für eine saubere Schnittstelle an die der Endkunde ebenfalls ran kommt, 
ist so etwas indiskutabel. Da würde ich direkt auf dem UART-Modul 
aufsetzen, sofern eine UART-Schnittstelle dafür wiederum überhaupt in 
Frage käme. Der i.MX8 hat da geschickteres im Bauch.

Danke euch, für mich passt es erst mal.
Moderator Persönliche Seite #6608501
Lesenswert?

Christoph F. schrieb:
> Das tut die Funktion von NXP auch. Die vsnprintf() hat die Pufferlänge
> als Argument, sollte also - oberflächlich betrachtet - sicher sein und
> die Ausgaben kürzen.

Ich wollte auch keine Pufferüberläufe unterstellen sondern eben eher den 
oben beschriebenen Fall ("640 Kilobyte genügen für jeden" :), bei dem 
dann vielleicht der interessante Teil der Ausgabe wegfällt, weil er 
nicht mehr gepasst hat.

Die normale printf-Familie hat das Problem nicht, weil sie nicht 
zwingend alles puffern muss, sondern dann ans Gerät rausschreibt.
Gast #6611629
Lesenswert?

Christoph F. schrieb:
> Hallo zusammen,
>
> ich sitze an einem BareMetall-Programm für den (CortexM4 des i.MX8MM)
> und möchte der PRINTF-Funktion von NXP einen Loglevel hinzufügen. Die
> PRINTF-Funktion unterstützt eine variable Anzahl an Argumenten.
>
> Dazu möchte ich ebenfalls eine Log-Funktion mit Variabler Anzahl von
> Argumenten implementieren, die nach dem Loglevel schaut und - sofern er
> paßt - einfach die Argumente 1:1 an die PRINTF-Funktion von NXP
> weitergibt.
>
> Weiß jemand wie das geht? Eine Anzahl variabler Argumente einfach an
> einen Call weitergeben?
>
> Vielen Dank für Eure Hilfe,
> Christoph
1
extern int log_level
2

3
const char *level_strings[] {
4
    "[DEBUG]: ",
5
    "[INFO ]: ",
6
    "[WARN ]: ",
7
    "[ERROR]: "
8
};
9

10
// kleiner Trick, damit die Funktion auch ohne Formatier-Parameter funktioniert.
11
#define LOG(level, __VA_ARGS__) LOG_(lovel, __VA_ARGS)
12

13
// PRINTF ist hier das nxp-Makro, kann aber auch printf() sein.
14
#define LOG_(level, fmt, ...) \
15
    if(level >= log_level) { \
16
        PRINTF("%s" fmt, log_level, __VA_ARGS__); \
17
    }

Sowas vielleicht (ungetestet)?
Gast #6611630
Lesenswert?

1
extern int log_level;
2

3
const char *level_strings[] {
4
    "[DEBUG]: ",
5
    "[INFO ]: ",
6
    "[WARN ]: ",
7
    "[ERROR]: "
8
};
9

10
// kleiner Trick, damit die Funktion auch ohne Formatier-Parameter funktioniert.
11

12
#define LOG(level, __VA_ARGS__) LOG_(lovel, __VA_ARGS)
13

14
// PRINTF ist hier das nxp-Makro, kann aber auch printf() sein.
15

16
#define LOG_(level, fmt, ...) \
17
    if(level >= log_level) { \
18
        PRINTF("%s" fmt, level_strings[log_level], __VA_ARGS__); \
19
    }

Ups, kann leider nicht editieren ;)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren