Wie kann man printf() in eine Funktion oder ein Makro einbinden?
Lesezeit: 6 Minuten
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?
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
ldav1s
Da Sie C99 verwenden können, würde ich es in a packen Variadisches Makro:
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:
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?
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.
Das ## Token wird die Nutzung ermöglichen TM_PRINTF("aaa");
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?).
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.
Behzad
Dies ist eine leicht modifizierte Version der hervorragenden Antwort von @ ldav1, die die Zeit vor dem Protokoll druckt:
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