2009-03-25 26 views
158

Chcę napisać makro w C, który akceptuje dowolną liczbę parametrów, nie określonego numeruJak zrobić zmiennej liczbie argumentów makra (zmienna liczba argumentów)

przykład:

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

gdzie X jest dowolna liczba parametrów

Potrzebuję tego, ponieważ whatever jest przeciążony i można go wywołać z 2 lub 4 parametrami.

Próbowałem dwukrotnie zdefiniować makro, ale druga definicja zastąpiła pierwszą!

Kompilator pracuję z jest g ++ (bardziej konkretnie, MinGW)

+6

Czy chcesz C lub C++? Jeśli używasz C, to dlaczego kompilujesz się z kompilatorem C++? Aby użyć właściwych makr warstwowych C99, powinieneś kompilować z kompilatorem C obsługującym C99 (jak gcc), a nie kompilatorem C++, ponieważ C++ nie ma standardowych makr variadic. –

+0

Cóż, założyłem, że C++ jest super zbiorem C pod tym względem .. – hasen

+0

http://tigcc.ticalc.org/doc/cpp.html#SEC13 zawiera szczegółowe wyjaśnienie makr variadic. – Gnubie

Odpowiedz

244

Sposób C99, obsługiwany również przez kompilator VC++.

#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__) 
+6

Nie sądzę, że C99 wymaga ## przed __VA_ARGS__. To może być po prostu VC++. –

+85

Powodem ## before __VA_ARGS__ jest to, że pochłania poprzedni przecinek w przypadku, gdy lista zmiennych-zmiennych jest pusta, np. FOO ("a") rozwija się do printf ("a"). Jest to rozszerzenie gcc (i może vC++), C99 wymaga obecności co najmniej jednego argumentu zamiast elipsy. – jpalecek

+83

'## 'nie jest potrzebny i nie jest przenośny. '#define FOO (...) printf (__ VA_ARGS __)' wykonuje zadanie w przenośny sposób; Parametr 'fmt' można pominąć w definicji. – alecov

4

wyjaśnione na g ++ tutaj, chociaż jest to część C99 więc powinien działać dla wszystkich

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

szybkie przykład :

#define debug(format, args...) fprintf (stderr, format, args) 
+2

Makra variadic GCC nie są makrami warstwowymi C99. GCC _has_ C99 makra warstwowe, ale G ++ nie obsługuje ich, ponieważ C99 nie jest częścią C++. –

+0

Właściwie g ++ skompiluje makra C99 w plikach C++. Wyda ostrzeżenie, jeśli skompilowane z "-pedantic". –

+2

To nie jest C99. C99 używa makra __VA_ARGS__). – qrdl

22

Nie sądzę, że to możliwe, można udawać to z podwójnymi parenami ... tak długo, jak długo nie potrzebujesz argumentów indywidualnie.

#define macro(ARGS) some_complicated (whatever ARGS) 
// ... 
macro((a,b,c)) 
macro((d,e)) 
+0

C99 dodaje makra variadic. –

+16

O ile możliwe jest posiadanie makro wariasu, dobrym pomysłem jest użycie podwójnego nawiasu. –

+1

Kompilator XC od Microchip nie obsługuje makr variadycznych, więc sztuczka z podwójnym nawiasem jest najlepsza. – gbmhunter

8
#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; 
} 

Jeśli kompilator nie rozumie o zmiennej liczbie argumentów makra, można też rozebrać się nadruk z jednej z następujących:

#define PRINT // 

lub

#define PRINT if(0)print 

Pierwsze komentarze out instrukcje PRINT, druga zapobiega instrukcji PRINT z powodu NULL, jeśli warunek. Jeśli ustawiona jest optymalizacja, kompilator powinien rozebrać nieukończone instrukcje takie jak: if (0) print ("hello world"); lub ((void) 0);

+6

#define PRINT // nie zastąpi PRINT przez // – bitc

+7

#define PRINT jeśli (0) print nie jest dobrym pomysłem, ponieważ kod wywołujący może mieć swój własny kod jeśli chcesz zadzwonić do PRINT. Lepsze jest: #define PRINT if (true); else print – bitc

+2

Standard "nic nie rób, z wdziękiem" to do {} while (0) – vonbrand

26

__VA_ARGS__ to standardowy sposób, aby to zrobić. Jeśli nie musisz, nie używaj hacków specyficznych dla kompilatora.

Naprawdę jestem zirytowany, że nie mogę komentować oryginalnego wpisu. W każdym razie C++ nie jest nadzbiorem C. Naprawdę głupio jest skompilować swój kod C za pomocą kompilatora C++. Nie rób tego, co Donny nie robi.

+3

* "Naprawdę głupio jest skompilować swój kod C za pomocą kompilatora C++" * => Nie jest to uważane przez wszystkich (łącznie ze mną). Zobacz na przykład podstawowe wskazówki C++: ** CPL.1: Preferuj C++ do C **, [** CPL.2: Jeśli musisz użyć C, użyj wspólnego podzbioru C i C++ i skompiluj kod C jako C++ * *] (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rcpl-subset). Ciężko mi myśleć o tym, co "C-only-isms" naprawdę trzeba zrobić, aby było warto nie programować w kompatybilnym podzbiorze, a komitety C i C++ ciężko pracowały nad udostępnieniem tego kompatybilnego podzestawu. – HostileFork

+1

@HostileFork Wystarczająco fair, chociaż * oczywiście * ludzie C++ chcieliby zachęcić do używania C++. Inni jednak się nie zgadzają; Linux Torvalds, na przykład, najwyraźniej odrzucił wiele proponowanych łat jądra Linuxa, które próbują zastąpić identyfikator 'class' przez' klass', aby umożliwić kompilację z kompilatorem C++. Zwróć też uwagę, że istnieją pewne różnice, które mogą cię podnieść; na przykład operator trójskładnikowy nie jest oceniany w ten sam sposób w obu językach, a słowo kluczowe "inline" oznacza coś zupełnie innego (jak dowiedziałem się z innego pytania). –

+0

Dla naprawdę wieloplatformowych projektów systemowych, takich jak system operacyjny, naprawdę chcesz przestrzegać ścisłego C, ponieważ kompilatory C są o wiele bardziej powszechne.W systemach wbudowanych wciąż istnieją platformy bez kompilatorów C++. (Istnieją platformy z łatwymi kompilatorami C!) Kompilatory C++ denerwują mnie, szczególnie w systemach cyber-fizycznych, i domyślam się, że nie jestem jedynym programistą wbudowanym/C z tym uczuciem. – downbeat

Powiązane problemy