Wie kann man printf() in eine Funktion oder ein Makro einbinden?

Lesezeit: 6 Minuten

Benutzeravatar von Vorac
Vorac

Das mag wie eine Interviewfrage klingen, ist aber tatsächlich ein praktisches Problem.

Ich arbeite mit einer eingebetteten Plattform und habe nur die Entsprechungen dieser Funktionen zur Verfügung:

  • printf()
  • snprintf()

Außerdem die printf() Die Implementierung (und Signatur) wird sich wahrscheinlich in naher Zukunft ändern, daher müssen sich Aufrufe in einem separaten Modul befinden, um später einfach migriert werden zu können.

Kann ich angesichts dessen Protokollierungsaufrufe in eine Funktion oder ein Makro einschließen? Das Ziel ist, dass mein Quellcode aufruft THAT_MACRO("Number of bunnies: %d", numBunnies); an tausend Stellen, aber Aufrufe der oben genannten Funktionen werden nur an einer einzigen Stelle gesehen.

Compiler: arm-gcc -std=c99

Bearbeiten: Nur um zu erwähnen, aber Best Practices nach 2000 und wahrscheinlich viel früher, Inline-Funktionen sind aus zahlreichen Gründen weitaus besser als Makros.

  • Unterstützt Ihr Compiler variadische Makros?

    – alk

    17. Dezember 2013 um 16:37 Uhr


  • Welche Compiler-Einschränkungen gibt es? Wenn dies auf einer Version von C vor C99 ausgeführt werden muss, wird es schwierig sein, es portabel als Makro zu bewerkstelligen.

    – ldav1s

    17. Dezember 2013 um 16:37 Uhr


  • @KerrekSB Ich dachte WARUM? Kommentare werden heutzutage blockiert?

    – Roddy

    17. Dezember 2013 um 16:41 Uhr

Dazu gibt es 2 Möglichkeiten:

  1. Variadrisches Makro

    #define my_printf(...) printf(__VA_ARGS__)
    
  2. Funktion, die weiterleitet va_args

    #include <stdarg.h>
    #include <stdio.h>
    
    void my_printf(const char *fmt, ...) {
        va_list args;
        va_start(args, fmt);
        vprintf(fmt, args);
        va_end(args);
    }
    

Es gibt auch vsnprintf, vfprintf und was immer dir einfällt stdio.

  • Darüber hinaus finden Sie einige Dokumentationen zu Makros (und variadischen Makros) hier.

    – jmlemetayer

    17. Dezember 2013 um 16:41 Uhr


  • @Roddy Ja, wenn Sie alle Argumente weiterleiten möchten. Ich würde davon abraten, da Sie auf diese Weise kein No-Op-Makro definieren können. Mit einem funktionsähnlichen Makro können Sie es immer ohne Operation machen, wenn Sie müssen.

    – Sergej L.

    17. Dezember 2013 um 16:42 Uhr

  • Ich wünschte, jemand hätte diese Antwort bearbeitet, damit ich meine positive Bewertung entfernen kann. Ich habe KEIN vprintf oder andere Fantasien. Eingebettet, wissen Sie.

    – Vorac

    18. Dezember 2013 um 12:37 Uhr

  • Sorry für den Ton. Ich kann dafür wirklich nicht die Standardbibliotheken verwenden. Es ist eine benutzerdefinierte ARM-basierte Plattform.

    – Vorac

    18. Dezember 2013 um 13:33 Uhr

  • Ich habe verschiedene Methoden wie diese ausprobiert, aber 1. Ich kann keine Makros erstellen, die über Namespaces funktionieren; #define Log::WriteLine(_Format, ...) printf(_Format, __VA_ARGS__) funktioniert nicht. Und 2. Es zeigt die nicht %s etc. in lindgrün, oder Eingabe validieren… und ist daher als Ersatz unbrauchbar. Wenn es eine Möglichkeit gibt, ein benutzerdefiniertes printf zu erhalten, das die %s etc. in Limonengrün, mit der für den Einsatz notwendigen Validierung?

    – Apachen

    18. Januar 2020 um 16:25 Uhr

Benutzeravatar von ldav1s
ldav1s

Da Sie C99 verwenden können, würde ich es in a packen Variadisches Makro:

#define TM_PRINTF(f_, ...) printf((f_), __VA_ARGS__)
#define TM_SNPRINTF(s_, sz_, f_, ...) snprintf((s_), (sz_), (f_), __VA_ARGS__)

da du das nicht gesagt hast vprintf oder so ähnlich. Wenn Sie so etwas haben, können Sie es in eine Funktion einpacken, wie sie Sergey L in seiner Antwort bereitgestellt hat.


Das obige TM_PRINTF funktioniert nicht mit einer leeren VA_ARGS-Liste. Zumindest in GCC kann man schreiben:

#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)

Die beiden ##-Zeichen entfernen das überschüssige Komma vor ihnen, wenn __VA_ARGS__ ist leer.

  • TM_PRINTF("Starting ei-oh!"); Erträge error: expected expression before ')' token. Ohne das Format-String-Argument funktioniert es. Müssen variadische Argumente nicht null sein?

    – Vorac

    18. Dezember 2013 um 12:50 Uhr

  • Es sieht so aus, als ob der obige Fehler nicht ohne gcc-Erweiterungen behoben werden kann: gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

    – Vorac

    18. Dezember 2013 um 13:07 Uhr

  • Sie können das Problem lösen, indem Sie den _f-Teil entfernen und nur das varidac-Ding behalten. Funktioniert für mich ohne Probleme, aber ich weiß nicht, ob es außerhalb des Standards liegt

    – Lesto

    30. September 2016 um 10:12 Uhr


  • @lesto, ja es “funktioniert”, aber entfernen f_ machen würden TM_PRINTF() zulässig. Wenn dieser String im Code wäre, würde sich der Compiler später darüber beschweren printf() was nicht mit seiner Funktionssignatur übereinstimmt. Es ist besser, den Compiler zu beschweren TM_PRINTF in diesem Fall, da das besser sichtbar ist.

    – ldav1s

    30. September 2016 um 14:39 Uhr

  • @ldav1s Ich habe nicht verstanden, was Sie mit einem “variadischen” Makro gemeint haben. Wie magisch diese Ellipse ... leite sie weiter __VA_ARGS__ ?

    – John Strood

    29. Oktober 2016 um 17:04 Uhr

Wenn Sie damit leben können, den Anruf einzubinden zwei Klammern, du kannst es so machen:

#define THAT_MACRO(pargs)    printf pargs

Dann benutze es:

THAT_MACRO(("This is a string: %s\n", "foo"));
           ^
           |
          OMG

Dies funktioniert, da aus Sicht des Präprozessors die gesamte Liste der Argumente zu einem Makroargument wird, das durch die Klammer ersetzt wird.

Das ist besser als einfach nur zu tun

#define THAT_MACRO printf

Da es Ihnen erlaubt, es zu definieren:

#define THAT_MACRO(pargs)  /* nothing */

Dadurch werden die Makroargumente “aufgefressen”, sie werden niemals Teil des kompilierten Codes sein.

AKTUALISIEREN Natürlich ist diese Technik in C99 veraltet, verwenden Sie einfach ein Variadic-Makro und seien Sie glücklich.

  • OMG danke für die tolle Antwort. Ich schätze, Sie werden in den nächsten Jahren Upvotes von all den armen C89-Programmierern da draußen bekommen 🙂

    – Vorac

    18. Dezember 2013 um 12:51 Uhr

  • Ein wichtiger Nebeneffekt dieser Technik: Alle Anrufe zu THAT_MACRO benötigt doppelte Klammern, auch bei Aufrufen mit einem Argument, z THAT_MACRO(("Foo Bar")). – Mit Liebe, ein armer C89-Programmierer von einigen Jahren später.

    – drmüller

    19. Juli 2016 um 16:02 Uhr

#define TM_PRINTF(f_, ...) printf((f_), ##__VA_ARGS__)

Das ## Token wird die Nutzung ermöglichen TM_PRINTF("aaa");

Benutzeravatar von EKons
EKons

#define PRINTF(...) printf(__VA_ARGS__)

Das funktioniert so:

Es definiert das parametrisierte Makro PRINTF so, dass es (bis zu) unendliche Argumente akzeptiert, und verarbeitet es dann vor PRINTF(...) zu printf(__VA_ARGS__). __VA_ARGS__ wird in parametrisierten Makrodefinitionen verwendet, um die angegebenen Argumente zu bezeichnen (weil Sie keine unendlichen Argumente benennen können, oder?).

Benutzeravatar der Community
Gemeinschaft

Begrenzte Bibliothek? Eingebettetes System? Brauchen Sie so viel Leistung wie möglich? Kein Problem!

Wie in dieser Antwort auf diese Frage gezeigt, können Sie die Assemblersprache verwenden, um Funktionen, die VA_LIST nicht akzeptieren, in solche zu verpacken, die dies tun, und Ihr eigenes vprintf zu geringen Kosten implementieren!

Während dies funktioniert und mit ziemlicher Sicherheit zu der gewünschten Leistung und Abstraktion führt, würde ich nur empfehlen, dass Sie sich eine Standardbibliothek mit mehr Funktionen besorgen, vielleicht indem Sie Teile davon schneiden uClibc. Eine solche Lösung ist sicherlich eine tragbarere und insgesamt nützlichere Antwort als die Verwendung von Montage, es sei denn, Sie benötigen unbedingt jeden Zyklus.

Dafür gibt es schließlich solche Projekte.

Benutzeravatar von Behzad
Behzad

Dies ist eine leicht modifizierte Version der hervorragenden Antwort von @ ldav1, die die Zeit vor dem Protokoll druckt:

#define TM_PRINTF(f_, ...)                                                                            \
{                                                                                                 \
    struct tm _tm123_;                                                                            \
    struct timeval _xxtv123_;                                                                     \
    gettimeofday(&_xxtv123_, NULL);                                                               \
    localtime_r(&_xxtv123_.tv_sec, &_tm123_);                                                     \
    printf("%2d:%2d:%2d.%d\t", _tm123_.tm_hour, _tm123_.tm_min, _tm123_.tm_sec, _xxtv123_.tv_usec); \
    printf((f_), ##__VA_ARGS__);                                                                  \
};

1407680cookie-checkWie kann man printf() in eine Funktion oder ein Makro einbinden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy