Cara membuat makro variadik (jumlah variabel argumen)


196

Saya ingin menulis makro di C yang menerima sejumlah parameter, bukan angka tertentu

contoh:

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

di mana Xsejumlah parameter

Saya perlu ini karena whateverkelebihan beban dan dapat dipanggil dengan 2 atau 4 parameter.

Saya mencoba mendefinisikan makro dua kali, tetapi definisi kedua menimpa yang pertama!

Kompiler yang saya gunakan adalah g ++ (lebih khusus, mingw)


8
Anda ingin C atau C ++? Jika Anda menggunakan C, mengapa Anda mengkompilasi dengan kompiler C ++? Untuk menggunakan macro variadic C99 yang tepat, Anda harus mengkompilasi dengan kompiler C yang mendukung C99 (seperti gcc), bukan kompiler C ++, karena C ++ tidak memiliki macro variadic standar.
Chris Lutz

Nah, saya berasumsi C ++ adalah satu set super C dalam hal ini ..
Hasen

tigcc.ticalc.org/doc/cpp.html#SEC13 memiliki penjelasan terperinci tentang makro variadic.
Gnubie

Penjelasan dan contoh yang baik ada di sini http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
zafarulq

3
Untuk pembaca masa depan: C bukan subest dari C ++. Mereka berbagi banyak hal, tetapi ada aturan yang menghentikan mereka menjadi bagian dan superset satu sama lain.
Pharap

Jawaban:


295

Cara C99, juga didukung oleh VC ++ compiler.

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

8
Saya tidak berpikir C99 membutuhkan ## sebelum VA_ARGS . Itu mungkin saja VC ++.
Chris Lutz

97
Alasan ## sebelum VA_ARGS adalah karena ia menelan koma sebelumnya jika daftar variabel-argumen kosong, misalnya. FOO ("a") memperluas ke printf ("a"). Ini adalah ekstensi dari gcc (dan vc ++, mungkin), C99 membutuhkan setidaknya satu argumen untuk menggantikan ellipsis.
jpalecek

108
##tidak diperlukan dan tidak portabel. #define FOO(...) printf(__VA_ARGS__)melakukan pekerjaan dengan cara portabel; yang fmtparameter dapat dihilangkan dari definisi.
alecov

4
IIRC, ## adalah khusus GCC dan memungkinkan melewati parameter nol
Mawg mengatakan mengembalikan Monica

10
Sintaks ## - juga berfungsi dengan llvm / clang dan kompiler Visual Studio. Jadi mungkin tidak portabel, tetapi didukung oleh kompiler utama.
K. Biermann

37

__VA_ARGS__adalah cara standar untuk melakukannya. Jangan gunakan hack khusus-kompiler jika Anda tidak perlu.

Saya benar-benar kesal karena saya tidak dapat mengomentari posting asli. Bagaimanapun, C ++ bukan superset dari C. Ini benar-benar konyol untuk mengkompilasi kode C Anda dengan kompiler C ++. Jangan lakukan apa yang Donny tidak lakukan.


8
"Sungguh konyol mengkompilasi kode C Anda dengan kompiler C ++" => Tidak dipertimbangkan oleh semua orang (termasuk saya). Lihat misalnya pedoman inti C ++: CPL.1: Lebih suka C ++ ke C , CPL.2: Jika Anda harus menggunakan C, gunakan subset umum C dan C ++, dan kompilasi kode C sebagai C ++ . Saya sulit sekali memikirkan "C-only-isme" apa yang benar-benar dibutuhkan untuk membuatnya tidak diprogram dalam subset yang kompatibel, dan komite C dan C ++ telah bekerja keras untuk membuat subset yang kompatibel tersedia.
HostileFork berkata jangan percaya

4
@HostileFork Cukup adil, meskipun tentu saja orang -orang C ++ ingin mendorong penggunaan C ++. Namun, yang lain tidak setuju; Linux Torvalds, misalnya, tampaknya menolak beberapa tambalan kernel Linux yang diusulkan yang mencoba untuk mengganti pengidentifikasi classdengan klassmengizinkan kompilasi dengan kompiler C ++. Perhatikan juga bahwa ada beberapa perbedaan yang akan membuat Anda marah; misalnya, operator ternary tidak dievaluasi dengan cara yang sama dalam kedua bahasa, dan inlinekata kunci berarti sesuatu yang sama sekali berbeda (seperti yang saya pelajari dari pertanyaan yang berbeda).
Kyle Strand

3
Untuk proyek sistem lintas platform yang benar-benar seperti sistem operasi, Anda benar-benar ingin mematuhi C yang ketat, karena kompiler C jauh lebih umum. Di embedded system, masih ada platform tanpa kompiler C ++. (Ada platform dengan hanya kompiler C yang bisa dilewati!) Kompiler C ++ membuat saya gugup, terutama untuk sistem cyber-fisik, dan saya kira saya bukan satu-satunya programmer perangkat lunak / C tertanam dengan perasaan itu.
suram

2
@downbeat Apakah Anda menggunakan C ++ untuk produksi atau tidak, jika Anda sangat khawatir, maka dapat dikompilasi dengan C ++ memberi Anda kekuatan ajaib untuk analisis statis. Jika Anda memiliki kueri yang ingin Anda buat dari basis kode C ... bertanya-tanya apakah jenis tertentu digunakan dengan cara tertentu, mempelajari cara menggunakan type_traits dapat membuat alat yang ditargetkan untuknya. Apa yang akan Anda bayar banyak untuk alat analisis statis C untuk dilakukan dapat dilakukan dengan sedikit C ++ tahu bagaimana dan kompiler yang sudah Anda miliki ...
HostileFork mengatakan jangan percaya SE

1
Saya berbicara dengan pertanyaan tentang Linux. (Saya hanya memperhatikan bahwa dikatakan "Linux Torvalds" ha!)
suram

28

Saya tidak berpikir itu mungkin, Anda bisa berpura-pura dengan double parens ... asalkan Anda tidak memerlukan argumen secara individual.

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

21
Meskipun dimungkinkan untuk memiliki makro variadik, menggunakan tanda kurung ganda adalah saran yang bagus.
David Rodríguez - dribeas

2
Kompilator XC oleh Microchip tidak mendukung macro variadic, jadi trik kurung ganda ini adalah yang terbaik yang dapat Anda lakukan.
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;
}

Jika kompiler tidak memahami makro variadic, Anda juga dapat menghapus PRINT dengan salah satu dari berikut ini:

#define PRINT //

atau

#define PRINT if(0)print

Komentar pertama mengeluarkan instruksi PRINT, yang kedua mencegah instruksi PRINT karena kondisi NULL if. Jika optimisasi diatur, kompiler harus menghapus instruksi yang tidak pernah dijalankan seperti: if (0) print ("hello world"); atau ((tidak berlaku) 0);


8
#define PRINT // tidak akan mengganti PRINT dengan //
bitc

8
#define PRINT if (0) print bukan ide yang baik juga karena kode panggilan mungkin memiliki yang lain-jika untuk memanggil PRINT. Lebih baik adalah: #define PRINT if (true);
selain itu

3
Standar "tidak melakukan apa pun, dengan anggun" adalah melakukan {} sementara (0)
vonbrand

Versi yang tepat ifdari "jangan lakukan ini" yang memperhitungkan struktur kode adalah: if (0) { your_code } elsetitik koma setelah ekspansi makro Anda mengakhiri else. The whileVersi terlihat seperti: while(0) { your_code } Masalah dengan do..whileversi adalah bahwa kode dalam do { your_code } while (0)dilakukan sekali, dijamin. Dalam ketiga kasus, jika your_codekosong, itu layak do nothing gracefully.
Jesse Chisholm

4

menjelaskan untuk g ++ di sini, meskipun itu adalah bagian dari C99 jadi harus bekerja untuk semua orang

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

contoh cepat:

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

3
Makro variadic GCC bukan makro variadic C99. GCC memiliki makro variadic C99, tetapi G ++ tidak mendukungnya, karena C99 bukan bagian dari C ++.
Chris Lutz

1
Sebenarnya g ++ akan mengkompilasi macro C99 dalam file C ++. Akan ada peringatan, jika dikompilasi dengan '-pedantic'.
Alex B

2
Itu bukan C99. C99 menggunakan VA_ARGS makro).
qrdl

1
C ++ 11 juga mendukung __VA_ARGS__, meskipun mereka didukung oleh kompiler di versi sebelumnya juga, sebagai ekstensi.
Ethouris

1
Ini gagal berfungsi untuk printf ("hai"); di mana tidak ada var args. Adakah cara umum untuk memperbaikinya?
BTR Naidu
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.