Variadické makro v preprocesoru C - Variadic macro in the C preprocessor

Variadic Makro je rys některých počítač programovacích jazyků , zejména C preprocesor , přičemž makro může být prohlášen přijmout řadu měnících se z argumentů .

Proměnlivou argumentů makra byly zavedeny v roce 1999 v ISO / IEC 9899: 1999 ( C99 revizi) o C jazykové normy, a v roce 2011 ISO / IEC 14882: 2011 ( C ++ 11 ) revize C ++ jazykové normy. V C ++ 20 byla přidána podpora pro variadická makra bez argumentů .

Syntaxe deklarace

Syntaxe deklarace je podobná jako u variadických funkcí : sekvence tří teček... “ označuje, že je třeba předat jeden nebo více argumentů. Během rozšiřování maker je každý výskyt speciálního identifikátoru __VA_ARGS__ v seznamu nahrazení maker nahrazen předanými argumenty.

Kromě toho mohou být běžné argumenty maker uvedeny před ..., ale běžné argumenty nemusí být uvedeny za ....

Nejsou k dispozici žádné prostředky pro přístup k jednotlivým argumentům v seznamu proměnných argumentů ani pro zjištění, kolik bylo předáno. Lze však zapsat makra, která počítají počet předaných argumentů.

Jak C99 a C ++ 11 normy vyžadují alespoň jeden parametr, ale protože C ++ 20 toto omezení bylo zrušeno pomocí __VA_OPT__ funkční makro. __VA_OPT__ makro nahrazuje svém uvažování, když jsou přítomny argumenty, a vynechat jinak. Běžné kompilátory však před tímto přidáním také umožňují předávání nulových argumentů.

Pravidla preprocesoru C zabraňují rekurzivnímu rozšiřování názvů maker v argumentu __VA_OPT__ . Toto omezení je však možné obejít až do libovolného pevného počtu rekurzivních rozšíření.

Podpěra, podpora

Při kompilaci kódu C a C ++ podporuje několik kompilátorů makra s proměnnými argumenty: GNU Compiler Collection 3.0, Clang (všechny verze), Visual Studio 2005 , C ++ Builder 2006 a Oracle Solaris Studio (dříve Sun Studio) Forte Developer 6 aktualizace 2 (C ++ verze 5.3). GCC také podporuje taková makra při kompilaci Objective-C .

Podpora __VA_OPT__ makro na podporu nulové argumenty již v přidána GNU Compiler Collection 8, Clang 6 a Visual Studio 2019 .

Příklad

Pokud by byla požadována funkceprintf podobná , která by jako argument převzala číslo souboru a řádku, ze kterého byla volána, platí následující řešení. dbgprintf()

// Our implemented function
void realdbgprintf (const char *SourceFilename,
                    int SourceLineno,
                    const char *CFormatString,
                    ...);

// Due to limitations of the variadic macro support in C++11 the following
// straightforward solution can fail and should thus be avoided:
//
//   #define dbgprintf(cformat, ...) \
//     realdbgprintf (__FILE__, __LINE__, cformat, __VA_ARGS__)
//
// The reason is that
//
//   dbgprintf("Hallo")
//
// gets expanded to
//
//   realdbgprintf (__FILE__, __LINE__, "Hallo", )
//
// where the comma before the closing brace will result in a syntax error.
//
// GNU C++ supports a non-portable extension which solves this.
//
//   #define dbgprintf(cformat, ...) \
//     realdbgprintf (__FILE__, __LINE__, cformat, ##__VA_ARGS__)
//
// C++20 eventually supports the following syntax.
//
//   #define dbgprintf(cformat, ...) \
//     realdbgprintf (__FILE__, __LINE__, cformat __VA_OPT__(,) __VA_ARGS__)
//
// By using the 'cformat' string as part of the variadic arguments we can
// circumvent the abovementioned incompatibilities.  This is tricky but
// portable.
#define dbgprintf(...) realdbgprintf (__FILE__, __LINE__, __VA_ARGS__)

dbgprintf() dalo by se pak nazvat jako

dbgprintf ("Hello, world");

který se rozšiřuje na

realdbgprintf (__FILE__, __LINE__, "Hello, world");

Dalším příkladem je

dbgprintf("%d + %d = %d", 2, 2, 5);

který se rozšiřuje na

realdbgprintf(__FILE__, __LINE__, "%d + %d = %d", 2, 2, 5);

Bez variadic maker printfnení možné přímo psát obálky do . Standardní řešení je použít funkci stdargs C/C ++ a vprintfmísto toho mít volání funkce .

Koncová čárka

Existuje problém s přenositelností při generování koncové čárky s prázdnými argumenty pro variadická makra v C99 . Některé kompilátory (např. Visual Studio, pokud nepoužíváte nový preprocesor vyhovující standardu) bez potíží eliminují koncovou čárku. Jiné překladače (např .: GCC) podporují umístění ##před __VA_ARGS__ .

# define MYLOG(FormatLiteral, ...)  fprintf (stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, __VA_ARGS__)

Následující aplikace funguje

MYLOG("Too many balloons %u", 42);

který se rozšiřuje na

fprintf (stderr, "%s(%u): " "Too many balloons %u" "\n", __FILE__, __LINE__, 42);

což je ekvivalentní

fprintf (stderr, "%s(%u): Too many balloons %u\n", __FILE__, __LINE__, 42);

Ale podívejte se na tuto aplikaci:

MYLOG("Attention!");

který se rozšiřuje na

fprintf (stderr, "%s(%u): " "Attention!" "\n", __FILE__, __LINE__, );

který generuje chybu syntaxe s GCC.

GCC podporuje následující (nepřenosné) rozšíření:

# define MYLOG(FormatLiteral, ...)  fprintf (stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, ##__VA_ARGS__)

který odstraní koncovou čárku, když __VA_ARGS__ je prázdný.

Alternativy

Před existencí proměnných argumentů v C99 bylo zcela běžné používat dvojnásobně vnořené závorky k využití variabilního počtu argumentů, které by bylo možné printf()funkci dodat :

# define dbgprintf(x) realdbgprintf x

dbgprintf() lze pak nazvat jako:

dbgprintf (("Hello, world %d", 27));

který se rozšiřuje na:

realdbgprintf ("Hello, world %d", 27);

Reference

Viz také