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 printf
není možné přímo psát obálky do . Standardní řešení je použít funkci stdargs C/C ++ a vprintf
mí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);