Méně známé skutečnosti o C a C++: Makra s proměnným počtem parametrů

Od Petr Zemek, 2010-02-06

S funkcemi, které berou proměnný počet parametrů ("nerdy speaking", mají proměnnou aritu), už se zřejmě každý setkal (nejznámějším příkladem je dvojice funkcí scanf() a printf()). V C99 byla zavedena podpora i pro makra s proměnným počtem parametrů (variadic macros, někdy překládáno jako "variadická makra"). Jak taková věc vypadá a k čemu to lze použít se dozvíte v následujícím příspěvku.

Pokud explicitně neřeknu jinak, tak se v následujícím textu se budu věnovat pouze C99 (v C++ by měly být až od nového standardu C++0x, ale různé překladače toto samozřejmě implementují jako rozšíření).

Makra s proměnným počtem parametrů (MPPP) používají stejnou syntaxi pro vyjádření proměnnosti jako funkce -- výpustku (ellipsis). Ta znamená, že kromě pevně stanovených parametrů můžete předat makru libovolný další počet argumentů oddělených čárkami. Pro přístup k dodatečným argumentům v MPPP se používá makro __VA_ARGS__, které se expanduje za argumenty předané v rámci výpustky. Je to tedy jednodušší přístup něž u funkcí, ale do jisté míry méně flexibilní (např. není umožněno snadné zjištění počtu předaných argumentů v rámci výpustky -- avšak existuje způsob, jak to zjistit).

Příklady použití

Kde se to dá použít? Uvedu dva příklady. Vemte si situaci, kdy chcete v rámci ladících výpisů vypisovat i název souboru a číslo řádku, na kterém výpis nastal. První, co vás napadne, je zřejmě následující:

#define debug(msg) fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg)

Co když ale budete chtít vypisovat i něco jiného, než řetězec (běžná záležitost)? Mohli byste uvažovat o použití inline funkce, ale zde byste nemohli přímo použít makra __FILE__ a __LINE__ (museli byste je předávat funkci jako parametry, což by bylo otravné). Řešením mohou být MPPP:

#define debug(...) fprintf(stderr, __VA_ARGS__)

Při tomto přístupu je pouze nutné dávat pozor na to, co makru předáváte. Musí to být formátovácí řetězec a argumenty dle tohoto řetězce. Např.

debug("%d, %d\n", var1, var2);

Druhý příklad je pro čistě dokumentační účely a používá se např. v knize Elements of Programming.

#define requires(...)

Tato konstrukce se pak používá u šablon k dokumentaci požadavků na typy. Cože, šablony v C? Kdepak, zde se mluví o C++. Bystrý čtenář ale jistě zaregistroval, že jsem psal, že v C++ MPPP nejsou. Ano, je tomu to. Kód v dané knížce spoléhá na rozšíření překladačů, že toto implementují (g++ vyhodí varování warning: anonymous variadic macros were introduced in C99). V tomto případě to ale příliš nevadí, protože kód v knížce je určen především pro výukové účely a možnost jeho překladu je až druhořadá.

Závěr

Makra s proměnným počtem parametrů existují v C od C99 a v C++ jako rozšíření překladačů. Existují situace, ve kterých jejich použití má opodstatnění, ale obecně byste se měli snažit makrům vyvarovat a používat místo nich funkce/šablony, které mají typovou kontrolu (pokud ale špatně použijete funkce s proměnným počtem parametrů, tak vám ani typová kontrola nepomůže :)). A jako perličku na závěr bych uvedl, že v C++0x by nás měla čekat podpora i pro šablony s proměnným počtem parametrů (variadic templates).

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
2 + 2 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.