Pertanyaan sederhana yang tidak dapat saya temukan jawabannya di internet. Dalam makro argumen variadic, bagaimana menemukan jumlah argumen? Saya setuju dengan boost preprocessor, jika ada solusinya.
Jika itu membuat perbedaan, saya mencoba untuk mengubah jumlah variabel argumen makro untuk meningkatkan urutan, daftar, atau larik preprocessor untuk pemrosesan ulang lebih lanjut.
c++
c
c-preprocessor
variadic-macros
Anycorn
sumber
sumber
__typeof__
untuk membuatnya berfungsi setidaknya pada beberapa kompilerJawaban:
Ini sebenarnya bergantung pada kompilator, dan tidak didukung oleh standar apa pun.
Namun di sini Anda memiliki penerapan makro yang menghitung:
sumber
#define EXPAND(x) x
#define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N
#define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, 9,8,7,6,5,4,3,2,1,0))
``PP_NARG()
gagal mengembalikan 0. SolusiGET_ARG_COUNT()
&Y_TUPLE_SIZE()
berfungsi.PP_NARG()
Gagal untuk kembali 0" ... belum tentu masalah. Seseorang dapat mengatakan bahwaPP_NARG()
harus mengembalikan 1 untuk alasan yang samaPP_NARG(,)
harus mengembalikan 2. Mendeteksi 0 mungkin memang berguna dalam beberapa kasus, tetapi solusinya tampaknya kurang umum (mengharuskan token pertama itu dapat ditempel; yang mungkin atau mungkin tidak baik-baik saja tergantung untuk apa Anda menggunakannya), atau khusus implementasi (seperti membutuhkan trik gnu-menghapus-tempelkan koma).Saya biasanya menggunakan makro ini untuk menemukan sejumlah parameter:
Contoh lengkap:
Ini adalah kode C99 yang sepenuhnya valid. Ini memiliki satu kelemahan, meskipun - Anda tidak dapat menjalankan makro
SUM()
tanpa params, tetapi GCC memiliki solusi untuk itu - lihat di sini .Jadi dalam kasus GCC, Anda perlu menentukan makro seperti ini:
dan itu akan bekerja bahkan dengan daftar parameter kosong
sumber
sizeof(int) != sizeof(void *)
?{__VA_ARGS__}
keint[]
, itu adilint[]
, terlepas dari konten sebenarnya__VA_ARGS__
##
diperlukan di VS2017 karena kosong__VA_ARGS__
akan secara otomatis menghapus koma sebelumnya.Jika Anda menggunakan C ++ 11, dan Anda memerlukan nilai sebagai konstanta waktu kompilasi C ++, solusi yang sangat elegan adalah ini:
Harap diperhatikan: penghitungan terjadi seluruhnya pada waktu kompilasi, dan nilainya dapat digunakan setiap kali integer waktu kompilasi diperlukan, misalnya sebagai parameter template untuk std :: array.
sumber
sizeof((int[]){__VA_ARGS__})/sizeof(int)
disarankan di atas, ini berfungsi bahkan ketika semua argumen tidak dapat diterapkanint
.#define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
Untuk kenyamanan, berikut adalah implementasi yang berfungsi untuk 0 hingga 70 argumen, dan berfungsi di Visual Studio, GCC, dan Clang . Saya yakin ini akan berfungsi di Visual Studio 2010 dan yang lebih baru, tetapi hanya mengujinya di VS2013.
sumber
__VA_ARGS__
" (yang dalam C ++, secara teknis merupakan ekstensi kompilator (standar de facto yang hampir universal ) hingga C ++ 20). Sebagian besar (? Semua) kompiler memungkinkan nol-panjang, tapi tersedak pada trailing koma jika daftar adalah kosong (dan kelebihan##
sebagai proto sebuah__VA_OPT__
, untuk menghapus koma dalam kasus ini); Versi ekstensi MSVC tidak hanya mencekik koma (tetapi akan tersedak jika kelebihan beban##
). Bandingkan MSVCunused, __VA_ARGS__
dengan non-MSVC0, ## __VA_ARGS__
; tidak ada yang lebih tepat, masalahnya adalah mereka berbeda.Ada beberapa solusi C ++ 11 untuk menemukan jumlah argumen pada waktu kompilasi, tetapi saya terkejut melihat bahwa tidak ada yang menyarankan sesuatu yang sederhana seperti:
Ini juga tidak memerlukan penyertaan
<tuple>
header.sumber
VA_COUNT(&,^,%)
. Juga, jika Anda menghitung melalui funtion, saya tidak melihat ada gunanya membuat makro.ini berfungsi dengan 0 argumen dengan gcc / llvm. [tautan bodoh]
Visual Studio tampaknya mengabaikan operator ## yang digunakan untuk menggunakan argumen kosong. Anda mungkin bisa menyiasatinya dengan sesuatu seperti
sumber
##__VA_ARGS__
memakan koma sebelumnya jika__VA_ARGS__
kosong adalah ekstensi GCC. Ini bukan perilaku standar.Dengan ekstensi D3D:
Bekerja untuk 0 - 32 argumen. Batas ini dapat dengan mudah diperpanjang.
EDIT: Versi sederhana (bekerja di VS2015 14.0.25431.01 Pembaruan 3 & gcc 7.4.0) hingga 100 argumen untuk disalin & ditempel:
sumber
Y_TUPLE_SIZE("Hello")
, membuatnya sangat tidak layak. Saya setuju dengan @osirisgothra.Saya berasumsi bahwa setiap argumen ke VA_ARGS akan dipisahkan dengan koma. Jika demikian, saya pikir ini seharusnya berfungsi sebagai cara yang cukup bersih untuk melakukan ini.
Bekerja untuk saya di godbolt untuk clang 4 dan GCC 5.1. Ini akan menghitung pada waktu kompilasi, tetapi tidak akan mengevaluasi preprocessor. Jadi jika Anda mencoba melakukan sesuatu seperti membuat FOR_EACH , ini tidak akan berhasil.
sumber
NUMARGS(hello, world = 2, ohmy42, !@#$%^&*()-+=)
!!! Setiap string arg tidak dapat memiliki beberapa simbol lain seperti','
meskipunint count = NUMARGS( foo(1, 2) );
menghasilkan 2 daripada 1. godbolt.org/z/kpBuOmdi sini cara sederhana untuk menghitung 0 atau lebih argumen dari VA_ARGS , contoh saya mengasumsikan maksimal 5 variabel, tetapi Anda dapat menambahkan lebih banyak jika Anda mau.
sumber
VA_ARGS_NUM
digunakan dengan makro: jika saya memiliki#define TEST
(yaitu kosongTEST
) danVA_ARGS_NUM(TEST)
tidak mengembalikan 0 (nol) saat digunakan dalam#if
:(Anda dapat merangkai dan menghitung token:
sumber
Boost Preprocessor sebenarnya memiliki ini pada Boost 1.49, sebagai
BOOST_PP_VARIADIC_SIZE(...)
. Ini berfungsi hingga ukuran 64.Di bawah tenda, pada dasarnya sama dengan jawaban Kornel Kisielewicz .
sumber
__VA_OPT__
atau ekstensi kompilator untuk##__VA_ARGS__
menghapus koma sebelumnya, misalnya: godbolt.org/z/X7OvnKSaya telah menemukan jawaban di sini masih belum lengkap.
Implementasi portabel terdekat yang saya temukan dari sini adalah: C ++ preprocessor __VA_ARGS__ jumlah argumen
Tapi itu tidak bekerja dengan argumen nol di GCC tanpa setidaknya
-std=gnu++11
parameter baris perintah.Jadi saya memutuskan untuk menggabungkan solusi ini dengan itu: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
https://godbolt.org/z/3idaKd
c++11
,msvc 2015
,gcc 4.7.1
,clang 3.0
sumber