Erstellen eines variadischen Makros (variable Anzahl von Argumenten)

196

Ich möchte ein Makro in C schreiben, das eine beliebige Anzahl von Parametern akzeptiert, keine bestimmte Anzahl

Beispiel:

#define macro( X )  something_complicated( whatever( X ) )

Wo Xist eine beliebige Anzahl von Parametern

Ich brauche das, weil whateveres überlastet ist und mit 2 oder 4 Parametern aufgerufen werden kann.

Ich habe zweimal versucht, das Makro zu definieren, aber die zweite Definition hat die erste überschrieben!

Der Compiler, mit dem ich arbeite, ist g ++ (genauer gesagt mingw)

hasen
quelle
8
Möchten Sie C oder C ++? Wenn Sie C verwenden, warum kompilieren Sie mit einem C ++ - Compiler? Um die richtigen variablen C99-Makros zu verwenden, sollten Sie mit einem C-Compiler kompilieren, der C99 (wie gcc) unterstützt, und nicht mit einem C ++ - Compiler, da C ++ keine variablen Standardmakros enthält.
Chris Lutz
Nun, ich nahm an, dass C ++ in dieser Hinsicht eine super Menge von C ist.
Hasen
tigcc.ticalc.org/doc/cpp.html#SEC13 enthält eine detaillierte Erläuterung der verschiedenen Makros.
Gnubie
Eine gute Erklärung und ein gutes Beispiel finden Sie hier http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
zafarulq
3
Für zukünftige Leser: C ist kein Subest von C ++. Sie teilen viele, viele Dinge, aber es gibt Regeln, die verhindern, dass sie Teil- und Obermenge voneinander sind.
Pharap

Antworten:

295

C99 Weg, auch vom VC ++ Compiler unterstützt.

#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__)
Alex B.
quelle
8
Ich glaube nicht, dass C99 das ## vor VA_ARGS benötigt . Das könnte nur VC ++ sein.
Chris Lutz
97
Der Grund für ## vor VA_ARGS ist, dass das vorhergehende Komma verschluckt wird, falls die Liste der variablen Argumente leer ist, z. FOO ("a") wird zu printf ("a") erweitert. Dies ist eine Erweiterung von gcc (und möglicherweise vc ++). Für C99 muss mindestens ein Argument anstelle der Auslassungspunkte vorhanden sein.
Jpalecek
108
##wird nicht benötigt und ist nicht tragbar. #define FOO(...) printf(__VA_ARGS__)erledigt die Arbeit auf tragbare Weise; Der fmtParameter kann in der Definition weggelassen werden.
Alecov
4
IIRC, das ## ist GCC-spezifisch und erlaubt das Übergeben von Null-Parametern
Mawg sagt, Monica
10
Die ## - Syntax funktioniert auch mit llvm / clang und dem Visual Studio Compiler. Es ist also möglicherweise nicht portabel, wird aber von den wichtigsten Compilern unterstützt.
K. Biermann
37

__VA_ARGS__ist der Standardweg, um es zu tun. Verwenden Sie keine compilerspezifischen Hacks, wenn Sie dies nicht müssen.

Ich bin wirklich genervt, dass ich den ursprünglichen Beitrag nicht kommentieren kann. In jedem Fall ist C ++ keine Obermenge von C. Es ist wirklich albern, Ihren C-Code mit einem C ++ - Compiler zu kompilieren. Tu nicht was Donny nicht tut.

cmccabe
quelle
8
"Es ist wirklich albern, Ihren C-Code mit einem C ++ - Compiler zu kompilieren" => Nicht von allen (einschließlich mir) berücksichtigt. Siehe zum Beispiel C ++ - Kernrichtlinien : CPL.1: Bevorzugen Sie C ++ gegenüber C , CPL.2: Wenn Sie C verwenden müssen, verwenden Sie die allgemeine Teilmenge von C und C ++ und kompilieren Sie den C-Code als C ++ . Es fällt mir schwer zu überlegen, welche "Nur-C-Ismen" man wirklich braucht, damit es sich lohnt, nicht in der kompatiblen Teilmenge zu programmieren, und die C- und C ++ - Komitees haben hart daran gearbeitet, diese kompatible Teilmenge verfügbar zu machen.
HostileFork sagt, vertraue SE
4
@HostileFork Fair enough, obwohl natürlich die C ++ Leute möchten Verwendung von C ++ fördern. Andere sind jedoch anderer Meinung; Linux Torvalds hat beispielsweise offenbar mehrere vorgeschlagene Linux-Kernel-Patches abgelehnt, die versuchen, den Bezeichner classdurch klasszu ersetzen, um das Kompilieren mit einem C ++ - Compiler zu ermöglichen. Beachten Sie auch, dass es einige Unterschiede gibt, die Sie stören werden. Zum Beispiel wird der ternäre Operator in beiden Sprachen nicht auf die gleiche Weise bewertet, und das inlineSchlüsselwort bedeutet etwas völlig anderes (wie ich aus einer anderen Frage gelernt habe).
Kyle Strand
3
Für wirklich plattformübergreifende Systemprojekte wie ein Betriebssystem möchten Sie unbedingt striktes C einhalten, da C-Compiler so viel häufiger sind. In eingebetteten Systemen gibt es immer noch Plattformen ohne C ++ - Compiler. (Es gibt Plattformen mit nur passablen C-Compilern!) C ++ - Compiler machen mich nervös, insbesondere für cyber-physische Systeme, und ich würde vermuten, dass ich nicht der einzige eingebettete Software- / C-Programmierer mit diesem Gefühl bin.
Downbeat
2
@downbeat Unabhängig davon, ob Sie C ++ für die Produktion verwenden oder nicht, wenn Sie sich Gedanken über die Genauigkeit machen, bietet Ihnen das Kompilieren mit C ++ magische Kräfte für die statische Analyse. Wenn Sie eine Abfrage haben, die Sie von einer C-Codebasis erstellen möchten, und sich fragen, ob bestimmte Typen auf bestimmte Weise verwendet werden, können Sie durch Erlernen der Verwendung von type_traits gezielte Tools dafür erstellen . Was Sie für ein statisches Analysetool von C viel Geld bezahlen würden, können Sie mit ein wenig C ++ - Know-how und dem Compiler
tun,
1
Ich spreche zur Frage von Linux. (Ich habe gerade bemerkt, dass dort "Linux Torvalds" ha steht!)
Downbeat
28

Ich denke nicht, dass das möglich ist, man könnte es mit Doppelparens vortäuschen ... solange man die Argumente nicht einzeln braucht.

#define macro(ARGS) some_complicated (whatever ARGS)
// ...
macro((a,b,c))
macro((d,e))
nervös
quelle
21
Während es möglich ist, ein variadisches Makro zu haben, ist die Verwendung von Doppelklammern ein guter Rat.
David Rodríguez - Dribeas
2
Der XC-Compiler von Microchip unterstützt keine variadischen Makros. Daher ist dieser Trick in doppelten Klammern das Beste, was Sie tun können.
Gbmhunter
10
#define DEBUG

#ifdef DEBUG
  #define PRINT print
#else
  #define PRINT(...) ((void)0) //strip out PRINT instructions from code
#endif 

void print(const char *fmt, ...) {

    va_list args;
    va_start(args, fmt);
    vsprintf(str, fmt, args);
        va_end(args);

        printf("%s\n", str);

}

int main() {
   PRINT("[%s %d, %d] Hello World", "March", 26, 2009);
   return 0;
}

Wenn der Compiler verschiedene Makros nicht versteht, können Sie PRINT auch mit einer der folgenden Optionen entfernen:

#define PRINT //

oder

#define PRINT if(0)print

Der erste kommentiert die PRINT-Anweisungen aus, der zweite verhindert den PRINT-Befehl aufgrund einer NULL-if-Bedingung. Wenn die Optimierung festgelegt ist, sollte der Compiler niemals ausgeführte Anweisungen entfernen, z. B.: If (0) print ("Hallo Welt"); oder ((nichtig) 0);

jpalecek
quelle
8
#define PRINT // ersetzt PRINT nicht durch //
bitc
8
#define PRINT if (0) print ist ebenfalls keine gute Idee, da der aufrufende Code möglicherweise ein eigenes else-if zum Aufrufen von PRINT hat. Besser ist: #define PRINT if (true); else print
bitc
3
Der Standard "nichts tun, anmutig" ist do {} while (0)
vonbrand
Die richtige ifVersion von "Nicht tun", die die Codestruktur berücksichtigt, lautet: if (0) { your_code } elseEin Semikolon, nachdem Ihre Makroerweiterung das beendet hat else. Die whileVersion sieht folgendermaßen aus: while(0) { your_code } Das Problem mit der do..whileVersion ist, dass der Code do { your_code } while (0)garantiert einmal ausgeführt wird. In allen drei Fällen your_codeist es richtig , wenn es leer ist do nothing gracefully.
Jesse Chisholm
4

Hier für g ++ erklärt, obwohl es Teil von C99 ist, sollte es für alle funktionieren

http://www.delorie.com/gnu/docs/gcc/gcc_44.html

schnelles Beispiel:

#define debug(format, args...) fprintf (stderr, format, args)
DarenW
quelle
3
Die variadischen Makros von GCC sind keine variablen C99-Makros. GCC verfügt über verschiedene C99-Makros, die jedoch von G ++ nicht unterstützt werden, da C99 nicht Teil von C ++ ist.
Chris Lutz
1
Tatsächlich kompiliert g ++ C99-Makros in C ++ - Dateien. Es wird jedoch eine Warnung ausgegeben, wenn es mit '-pedantic' kompiliert wird.
Alex B
2
Es ist nicht C99. C99 VA_ARGS- Makro verwenden).
qrdl
1
C ++ 11 unterstützt __VA_ARGS__auch als Erweiterung, obwohl sie auch von Compilern in früheren Versionen unterstützt werden.
Ethouris
1
Dies funktioniert nicht für printf ("hi"); wo es keine var args gibt. Gibt es eine generische Möglichkeit, dies zu beheben?
BTR Naidu