Forum: PC-Programmierung [C] Weitergabe variabler Anzahl an Argumenten


von Christoph F. (Firma: Fautronix GmbH) (fautronix)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

vprintf bzw. vfprintf is your friend

von mh (Gast)


Lesenswert?

Christoph F. schrieb:
> Weiß jemand wie das geht? Eine Anzahl variabler Argumente einfach an
> einen Call weitergeben?

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

Hier noch etwas zum Nachlesen 
https://en.cppreference.com/w/c/io/vfprintf (mit Beispiel).

von Christoph F. (Firma: Fautronix GmbH) (fautronix)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

: Bearbeitet durch Moderator
von Christoph F. (Firma: Fautronix GmbH) (fautronix)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Yalu X. (yalu) (Moderator)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Jörg W. 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. ;-))

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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 …

von Christoph F. (Firma: Fautronix GmbH) (fautronix)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Sebastian (Gast)


Lesenswert?

Christoph F. schrieb:
> Dazu möchte ich ebenfalls eine Log-Funktion mit Variabler Anzahl von
> Argumenten implementieren

Es ist einfacher, wenn du ein log-Makro implementierst. Dann brauchst du 
auch keine "v" Variante der NXP Funktion.

von Jemand (Gast)


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)?

von Jemand (Gast)


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 ;)

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.