Mungkin kelebihan beban fungsi, parameter default, templat variadic atau mungkin idiom parameter bernama adalah apa yang Anda cari
smoothware
Harap perbarui jawaban yang Anda pilih menjadi jawaban yang paling disukai dengan solusi aktual, bukan jawaban yang disukai rendah yang mengatakanNo you can't
Albert Renshaw
Jawaban:
156
Inilah salah satu cara untuk melakukannya. Ini menggunakan daftar argumen dua kali, pertama untuk membentuk nama makro pembantu, dan kemudian meneruskan argumen ke makro pembantu tersebut. Ini menggunakan trik standar untuk menghitung jumlah argumen ke makro.
Ini cukup keren, tapi menurut saya ini tidak akan berhasil jika saya melakukan PRINT_STRING. Dalam hal ini tidak akan ada cetakan default (dan itu sebenarnya kasus yang ingin saya gunakan). Masih +1 untuk sangat keren.
Cenoc
2
bekerja untuk saya di gcc (dan ini sangat pintar!) :-) tetapi tidak berhasil untuk saya di Visual Studio :-(
Tim Gradwell
3
@TimGradwell - ini karena bug dalam kompilator MSVC yang telah mereka akui tetapi belum diperbaiki dalam hampir satu dekade. Namun, solusi tersedia .
BeeOnRope
Cerdas, tetapi tidak berfungsi untuk argumen makro variadic opsional karena hal 'mendorong' yang Anda lakukan di `GET_4th_ARG '.
searchengine27
apakah itu PRINT_STRING_MACRO_CHOOSERdibutuhkan? Bisakah saya mengganti dengan tubuh bagian dalam secara langsung dan memanggil semua ini dengan (__VA_ARGS__)?
Herrgott
85
Dengan sangat hormat kepada Derek Ledbetter atas jawabannya - dan dengan permintaan maaf karena menghidupkan kembali pertanyaan lama.
Memperoleh pemahaman tentang apa yang dilakukannya dan mengambil di tempat lain tentang kemampuan untuk mendahului __VA_ARGS__dengan ##memungkinkan saya untuk menghasilkan variasi ...
// The multiple macros that you would need anyway [as per: Crazy Eddie]#define XXX_0()<code for no arguments>#define XXX_1(A)<code for one argument>#define XXX_2(A,B)<code for two arguments>#define XXX_3(A,B,C)<code for three arguments>#define XXX_4(A,B,C,D)<code for four arguments>// The interim macro that simply strips the excess and ends up with the required macro#define XXX_X(x,A,B,C,D,FUNC,...) FUNC
// The macro that the programmer uses #define XXX(...) XXX_X(,##__VA_ARGS__,\
XXX_4(__VA_ARGS__),\
XXX_3(__VA_ARGS__),\
XXX_2(__VA_ARGS__),\
XXX_1(__VA_ARGS__),\
XXX_0(__VA_ARGS__)\
)
Untuk non-ahli seperti saya yang menemukan jawabannya, tetapi tidak dapat melihat cara kerjanya, saya akan melangkah melalui pemrosesan yang sebenarnya, dimulai dengan kode berikut ...
XXX();
XXX(1);
XXX(1,2);
XXX(1,2,3);
XXX(1,2,3,4);
XXX(1,2,3,4,5);// Not actually valid, but included to show the process
PS: Hapus #define untuk XXX_0 untuk mendapatkan kesalahan kompilasi [yaitu: jika opsi tanpa argumen tidak diperbolehkan].
PPS: Akan menyenangkan jika situasi yang tidak valid (misalnya: 5) menjadi sesuatu yang memberikan kesalahan kompilasi yang lebih jelas kepada programmer!
PPPS: Saya bukan ahli, jadi saya sangat senang mendengar komentar (baik, buruk atau lainnya)!
Anda bisa mendapatkan kesalahan kompilasi yang jelas jika Anda mengubah argumen yang dipilih yang seharusnya menjadi nama MAKRO menjadi string menggunakan # (tanda pagar) dan membandingkan n karakter pertamanya dengan awalan yang diharapkan dan jika tidak ada kecocokan, mencetak informatif kesalahan.
AturSams
1
Wow, saya tidak tahu apakah ini berhasil, tetapi setidaknya sangat kreatif!
Penebusan Terbatas
4
mengapa argumen pertama selalu kosong? mengapa kita tidak bisa mengabaikannya: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman
2
Argumen kosong pertama (koma) penting. ## __ VA_ARGS__ jika diawali dengan koma – koma tersebut akan dihapus jika ## __ VA_ARGS__ meluas ke nol. Anda dapat melihatnya di contoh "Menjadi ..." karena baris pertama (tanpa argumen) hanya memiliki 6 parameter tetapi sisanya mendapatkan 7. Trik ini memastikan bahwa situasi tanpa argumen berfungsi
David Sorkovsky
@Eric - ini karena bug di kompiler microsoft, tetapi Anda dapat melihat pertanyaan ini untuk mengatasinya.
BeeOnRope
31
Makro C ++ tidak berubah dari C. Karena C tidak memiliki overloading dan argumen default untuk fungsi, tentu saja C tidak memilikinya untuk makro. Jadi untuk menjawab pertanyaan Anda: tidak, fitur tersebut tidak ada untuk makro. Satu-satunya pilihan Anda adalah menentukan beberapa makro dengan nama berbeda (atau tidak menggunakan makro sama sekali).
Sebagai catatan samping: Di C ++ umumnya dianggap praktik yang baik untuk menjauh dari makro sebanyak mungkin. Jika Anda membutuhkan fitur seperti ini, ada kemungkinan Anda menggunakan makro secara berlebihan.
Perhatikan bahwa alasan mengapa makro tidak mungkin "memuat berlebihan" adalah karena makro tidak memiliki jenis yang melekat. Makro hanya diperluas.
mk12
2
Meskipun saya menggunakan makro sesedikit mungkin, saya menemukan bahwa debugging melalui keluaran jejak menjadi sedikit lebih mudah dengan hal-hal seperti __FILE__dan __LINE__dan semacamnya ...
Kompilasi Bersyarat dan Debugging / Logging adalah domain tempat makro sangat berguna dan sah. Setiap programmer yang serius tahu itu. Praktik yang baik adalah menghindari penggunaan makro untuk menentukan konstanta dan melakukan beberapa hal pengkodean tingkat C yang gila untuk membuat template kontainer. Saya berharap C ++ akan menambahkan lebih banyak fitur ke makro juga. Mereka ortogonal terhadap template. Yang terbaik tentu saja adalah codelet yang memungkinkan saya menambahkan generator ke dalam kompiler untuk bahasa tertentu domain (aspek).
Lothar
1
Saya juga berpikir ini bukan jawaban yang baik, karena makro adalah sesuatu yang lengkap selain opsi bahasa C ++, karena itu akan ditangani SEBELUM kompiler. Jadi Anda dapat melakukan hal-hal lain, dan tidak ada compiler atau linker yang harus mengoptimalkan kodenya, karena mungkin tidak untuk mengoptimalkannya.
alabamajack
26
Dengan sangat hormat kepada Derek Ledbetter , David Sorkovsky , Syphorlate atas jawaban mereka, bersama dengan metode cerdik untuk mendeteksi argumen makro kosong oleh Jens Gustedt di
akhirnya saya keluar dengan sesuatu yang menggabungkan semua trik, jadi solusinya
Hanya menggunakan makro C99 standar untuk mencapai overloading fungsi, tidak ada ekstensi GCC / CLANG / MSVC yang terlibat (yaitu, menelan koma dengan ekspresi spesifik , ##__VA_ARGS__untuk GCC / CLANG, dan menelan implisit oleh ##__VA_ARGS__untuk MSVC). Jadi jangan ragu untuk meneruskan yang hilang --std=c99ke kompiler Anda jika Anda ingin =)
Berfungsi untuk argumen nol , serta argumen dalam jumlah tidak terbatas , jika Anda mengembangkannya lebih jauh untuk memenuhi kebutuhan Anda
Berfungsi cukup lintas platform , setidaknya diuji
GNU / Linux + GCC (GCC 4.9.2 di CentOS 7.0 x86_64)
GNU / Linux + CLANG / LLVM , (CLANG / LLVM 3.5.0 di CentOS 7.0 x86_64)
OS X + Xcode , (XCode 6.1.1 di OS X Yosemite 10.10.1)
Windows + Visual Studio , (Visual Studio 2013 Pembaruan 4 pada Windows 7 SP1 64 bit)
Untuk para pemalas, langsung saja ke bagian paling akhir dari posting ini untuk menyalin sumbernya. Di bawah ini adalah penjelasan detailnya, yang semoga membantu dan menginspirasi semua orang yang mencari __VA_ARGS__solusi umum seperti saya. =)
Begini caranya. Pertama menentukan dilihat pengguna kelebihan beban "fungsi", saya menamakannya create, dan definisi fungsi sebenarnya terkait realCreate, dan definisi makro dengan nomor yang berbeda argumen CREATE_2, CREATE_1, CREATE_0, seperti yang ditunjukkan di bawah ini:
Bagian MACRO_CHOOSER(__VA_ARGS__)akhirnya menyelesaikan nama definisi makro, dan bagian kedua (__VA_ARGS__)terdiri dari daftar parameter mereka. Jadi panggilan pengguna untuk create(10)memutuskan CREATE_1(10), CREATE_1bagian berasal dari MACRO_CHOOSER(__VA_ARGS__), dan (10)bagian berasal dari yang kedua (__VA_ARGS__).
The MACRO_CHOOSERmenggunakan trik yang, jika __VA_ARGS__kosong, ekspresi berikut concatenated menjadi panggilan makro valid oleh preprocessor:
NO_ARG_EXPANDER __VA_ARGS__ ()// simply shrinks to NO_ARG_EXPANDER()
Secara jenius, kita dapat mendefinisikan panggilan makro yang dihasilkan ini sebagai
#define NO_ARG_EXPANDER(),,CREATE_0
Perhatikan kedua koma tersebut, keduanya akan segera dijelaskan. Makro berguna berikutnya adalah
Seperti yang disarankan oleh nama makro, kita akan menghitung jumlah argumen nanti. Inilah trik lain: preprocessor hanya melakukan penggantian teks sederhana. Ini menyimpulkan jumlah argumen panggilan makro hanya dari jumlah koma yang dilihatnya di dalam tanda kurung. "Argumen" sebenarnya yang dipisahkan dengan koma tidak harus memiliki sintaks yang valid. Mereka bisa berupa teks apa saja. Artinya, dalam contoh di atas, NO_ARG_EXPANDER 10 ()dihitung sebagai 1 argumen untuk panggilan tengah. NO_ARG_EXPANDER 20dan 20 ()dihitung sebagai 2 argumen untuk panggilan terbawah.
Jika kita menggunakan makro pembantu berikut untuk mengembangkannya lebih jauh
Trailing ,setelahnya CREATE_1adalah solusi untuk GCC / CLANG, menekan kesalahan (positif palsu) yang mengatakan itu ISO C99 requires rest arguments to be usedsaat meneruskan -pedanticke kompilator Anda. Ini FUNC_RECOMPOSERadalah solusi untuk MSVC, atau tidak dapat menghitung jumlah argumen (yaitu, koma) di dalam tanda kurung panggilan makro dengan benar. Hasilnya diselesaikan lebih lanjut ke
Seperti yang mungkin telah Anda lihat, satu-satunya langkah terakhir yang kita butuhkan adalah menggunakan trik penghitungan argumen standar untuk akhirnya memilih nama versi makro yang diinginkan:
#define FUNC_CHOOSER(_f1, _f2, _f3,...) _f3
yang menyelesaikan hasil menjadi
CREATE_0();
CREATE_1(10);
CREATE_2(20,20);
dan tentu saja memberi kita panggilan fungsi aktual yang diinginkan:
Meskipun rumit, jelek, membebani pengembang API, ada solusi untuk membebani dan menyetel parameter opsional fungsi C / C ++ bagi kita orang gila. Penggunaan API kelebihan beban yang keluar menjadi sangat menyenangkan dan menyenangkan. =)
Jika ada kemungkinan penyederhanaan lebih lanjut dari pendekatan ini, harap beri tahu saya di
Untuk siapa pun yang dengan susah payah mencari solusi VA_NARGS yang bekerja dengan Visual C ++. Makro berikut bekerja untuk saya dengan sempurna (juga dengan parameter nol!) Di visual c ++ express 2010:
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)bool(#__VA_ARGS__)?(VA_NUM_ARGS_IMPL_((__VA_ARGS__,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))):0
Jika Anda menginginkan makro dengan parameter opsional, Anda dapat melakukan:
Saya mendapatkanunresolved external symbol _bool referenced in function _main
Avidan Borisov
ya itu bisa terjadi dalam beberapa kasus. Anda perlu menyadari bahwa bool (#__ VA_ARGS__)? berbeda dari makro lain karena sedang dievaluasi pada waktu proses. tergantung pada kasus Anda, Anda dapat menghilangkan bagian kode itu.
Syphorlate
2
Saya sebenarnya berakhir dengan pastebin.com/H3T75dcn yang berfungsi dengan sempurna (0 argumen juga).
Avidan Borisov
Terima kasih untuk tautannya, dan ya Anda dapat melakukannya menggunakan sizeof juga tetapi bagi saya itu tidak berfungsi dalam beberapa kasus tetapi prinsipnya sama (evaluasi boolean).
Syphorlate
Bisakah Anda memberikan beberapa contoh jika gagal?
Avidan Borisov
7
gcc/ g++mendukung makro varargs tetapi menurut saya ini tidak standar, jadi gunakan dengan risiko Anda sendiri.
Mereka standar di C99, dan ditambahkan ke C ++ 0x juga.
greyfade
5
#include<stdio.h>#define PP_NARG(...) \
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())#define PP_NARG_(...) \
PP_ARG_N(__VA_ARGS__)#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define PP_RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0#define PP_CONCAT(a,b) PP_CONCAT_(a,b)#define PP_CONCAT_(a,b) a ## b#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)#define THINK_0() THINK_1("sector zz9 plural z alpha")#define THINK_1(location) THINK_2(location,42)#define THINK_2(location,answer) THINK_3(location, answer,"deep thought")#define THINK_3(location,answer,computer) \
printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"" actually means will be build in %s\n",(answer),(computer),(location))int
main (int argc,char*argv[]){
THINK ();/* On compilers other than GCC you have to call with least one non-default argument */}
ada kesalahan dalam kode Anda. tolong lakukan :%s/MY_MACRO_/THINK_/g:)
João Portela
juga, ini tidak bekerja dengan nol argumen menggunakan g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela
1
Tidak ada argumen untuk makro variadiac, karena token kosong adalah placeholder yang valid.
Paul Fultz II
3
Bukan itu yang dirancang untuk preprocessor.
Yang mengatakan, jika Anda ingin memasuki area pemrograman makro yang sangat menantang dengan sedikit keterbacaan, Anda harus melihat pustaka preprocessor Boost . Lagi pula, itu tidak akan menjadi C ++ jika tidak ada tiga tingkat pemrograman yang sepenuhnya kompatibel dengan Turing (preprocessor, metaprogramming template, dan base level C ++)!
Sebagai penggemar berat monster makro yang mengerikan, saya ingin memperluas jawaban Jason Deng dan membuatnya benar-benar dapat digunakan. (Baik atau buruk.) Yang asli tidak terlalu bagus untuk digunakan karena Anda perlu memodifikasi sup alfabet besar setiap kali Anda ingin membuat makro baru dan bahkan lebih buruk jika Anda membutuhkan jumlah argumen yang berbeda.
Jadi saya membuat versi dengan fitur-fitur ini:
0 kasus argumen berhasil
1 hingga 16 argumen tanpa modifikasi apa pun pada bagian yang berantakan
Mudah untuk menulis lebih banyak fungsi makro
Diuji di gcc 10, clang 9, Visual Studio 2017
Saat ini saya baru membuat maksimum 16 argumen, tetapi jika Anda membutuhkan lebih banyak (sungguh sekarang? Anda semakin konyol ...) Anda dapat mengedit FUNC_CHOOSER dan CHOOSE_FROM_ARG_COUNT, lalu menambahkan beberapa koma ke NO_ARG_EXPANDER.
Silakan lihat jawaban luar biasa Jason Deng untuk detail lebih lanjut tentang penerapannya, tetapi saya hanya akan meletakkan kodenya di sini:
#include<stdio.h>void realCreate(int x,int y){
printf("(%d, %d)\n", x, y);}// This part you put in some library header:#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16,...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F,...) FUNC_RECOMPOSER((__VA_ARGS__, \
F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))#define NO_ARG_EXPANDER(FUNC),,,,,,,,,,,,,,,,FUNC ## _0#define MACRO_CHOOSER(FUNC,...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))#define MULTI_MACRO(FUNC,...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)// When you need to make a macro with default arguments, use this:#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)#define CREATE_0() CREATE_1(0)#define CREATE_1(x) CREATE_2(x,0)#define CREATE_2(x, y) \
do{ \
/* put whatever code you want in the last macro */ \
realCreate(x, y); \
}while(0)int main(){
create();
create(10);
create(20,20);//create(30, 30, 30); // Compilation errorreturn0;}
Bergantung pada apa yang Anda butuhkan, Anda dapat melakukannya dengan var args dengan makro. Sekarang, parameter opsional atau kelebihan makro, tidak ada hal seperti itu.
Tak satu pun dari contoh di atas (dari Derek Ledbetter, David Sorkovsky, dan Joe D) untuk menghitung argumen dengan makro bekerja untuk saya menggunakan Microsoft VCC 10. __VA_ARGS__Argumen selalu dianggap sebagai argumen tunggal (memberi tanda dengan ##atau tidak), jadi pergeseran argumen di mana contoh-contoh itu bergantung tidak berhasil.
Jadi, jawaban singkatnya, seperti yang dinyatakan oleh banyak orang di atas: tidak, Anda tidak dapat membebani makro atau menggunakan argumen opsional padanya.
Bisa, tetapi hanya di C99 atau C ++ 11 (karena memiliki __VA_ARGS__). VC2010 adalah C89 / C ++ 03 (dengan beberapa bit C ++ 11 mulai muncul, tetapi belum yang itu).
No you can't
Jawaban:
Inilah salah satu cara untuk melakukannya. Ini menggunakan daftar argumen dua kali, pertama untuk membentuk nama makro pembantu, dan kemudian meneruskan argumen ke makro pembantu tersebut. Ini menggunakan trik standar untuk menghitung jumlah argumen ke makro.
Ini mempermudah pemanggil makro, tetapi tidak untuk penulisnya.
sumber
PRINT_STRING_MACRO_CHOOSER
dibutuhkan? Bisakah saya mengganti dengan tubuh bagian dalam secara langsung dan memanggil semua ini dengan(__VA_ARGS__)
?Dengan sangat hormat kepada Derek Ledbetter atas jawabannya - dan dengan permintaan maaf karena menghidupkan kembali pertanyaan lama.
Memperoleh pemahaman tentang apa yang dilakukannya dan mengambil di tempat lain tentang kemampuan untuk mendahului
__VA_ARGS__
dengan##
memungkinkan saya untuk menghasilkan variasi ...Untuk non-ahli seperti saya yang menemukan jawabannya, tetapi tidak dapat melihat cara kerjanya, saya akan melangkah melalui pemrosesan yang sebenarnya, dimulai dengan kode berikut ...
Menjadi...
Yang menjadi argumen keenam ...
PS: Hapus #define untuk XXX_0 untuk mendapatkan kesalahan kompilasi [yaitu: jika opsi tanpa argumen tidak diperbolehkan].
PPS: Akan menyenangkan jika situasi yang tidak valid (misalnya: 5) menjadi sesuatu yang memberikan kesalahan kompilasi yang lebih jelas kepada programmer!
PPPS: Saya bukan ahli, jadi saya sangat senang mendengar komentar (baik, buruk atau lainnya)!
sumber
XXX_X(,##__VA_ARGS__,` ...
XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `Makro C ++ tidak berubah dari C. Karena C tidak memiliki overloading dan argumen default untuk fungsi, tentu saja C tidak memilikinya untuk makro. Jadi untuk menjawab pertanyaan Anda: tidak, fitur tersebut tidak ada untuk makro. Satu-satunya pilihan Anda adalah menentukan beberapa makro dengan nama berbeda (atau tidak menggunakan makro sama sekali).
Sebagai catatan samping: Di C ++ umumnya dianggap praktik yang baik untuk menjauh dari makro sebanyak mungkin. Jika Anda membutuhkan fitur seperti ini, ada kemungkinan Anda menggunakan makro secara berlebihan.
sumber
__FILE__
dan__LINE__
dan semacamnya ...Dengan sangat hormat kepada Derek Ledbetter , David Sorkovsky , Syphorlate atas jawaban mereka, bersama dengan metode cerdik untuk mendeteksi argumen makro kosong oleh Jens Gustedt di
https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
akhirnya saya keluar dengan sesuatu yang menggabungkan semua trik, jadi solusinya
, ##__VA_ARGS__
untuk GCC / CLANG, dan menelan implisit oleh##__VA_ARGS__
untuk MSVC). Jadi jangan ragu untuk meneruskan yang hilang--std=c99
ke kompiler Anda jika Anda ingin =)Berfungsi cukup lintas platform , setidaknya diuji
Untuk para pemalas, langsung saja ke bagian paling akhir dari posting ini untuk menyalin sumbernya. Di bawah ini adalah penjelasan detailnya, yang semoga membantu dan menginspirasi semua orang yang mencari
__VA_ARGS__
solusi umum seperti saya. =)Begini caranya. Pertama menentukan dilihat pengguna kelebihan beban "fungsi", saya menamakannya
create
, dan definisi fungsi sebenarnya terkaitrealCreate
, dan definisi makro dengan nomor yang berbeda argumenCREATE_2
,CREATE_1
,CREATE_0
, seperti yang ditunjukkan di bawah ini:Bagian
MACRO_CHOOSER(__VA_ARGS__)
akhirnya menyelesaikan nama definisi makro, dan bagian kedua(__VA_ARGS__)
terdiri dari daftar parameter mereka. Jadi panggilan pengguna untukcreate(10)
memutuskanCREATE_1(10)
,CREATE_1
bagian berasal dariMACRO_CHOOSER(__VA_ARGS__)
, dan(10)
bagian berasal dari yang kedua(__VA_ARGS__)
.The
MACRO_CHOOSER
menggunakan trik yang, jika__VA_ARGS__
kosong, ekspresi berikut concatenated menjadi panggilan makro valid oleh preprocessor:Secara jenius, kita dapat mendefinisikan panggilan makro yang dihasilkan ini sebagai
Perhatikan kedua koma tersebut, keduanya akan segera dijelaskan. Makro berguna berikutnya adalah
jadi panggilan dari
sebenarnya diperluas menjadi
Seperti yang disarankan oleh nama makro, kita akan menghitung jumlah argumen nanti. Inilah trik lain: preprocessor hanya melakukan penggantian teks sederhana. Ini menyimpulkan jumlah argumen panggilan makro hanya dari jumlah koma yang dilihatnya di dalam tanda kurung. "Argumen" sebenarnya yang dipisahkan dengan koma tidak harus memiliki sintaks yang valid. Mereka bisa berupa teks apa saja. Artinya, dalam contoh di atas,
NO_ARG_EXPANDER 10 ()
dihitung sebagai 1 argumen untuk panggilan tengah.NO_ARG_EXPANDER 20
dan20 ()
dihitung sebagai 2 argumen untuk panggilan terbawah.Jika kita menggunakan makro pembantu berikut untuk mengembangkannya lebih jauh
Trailing
,
setelahnyaCREATE_1
adalah solusi untuk GCC / CLANG, menekan kesalahan (positif palsu) yang mengatakan ituISO C99 requires rest arguments to be used
saat meneruskan-pedantic
ke kompilator Anda. IniFUNC_RECOMPOSER
adalah solusi untuk MSVC, atau tidak dapat menghitung jumlah argumen (yaitu, koma) di dalam tanda kurung panggilan makro dengan benar. Hasilnya diselesaikan lebih lanjut keSeperti yang mungkin telah Anda lihat, satu-satunya langkah terakhir yang kita butuhkan adalah menggunakan trik penghitungan argumen standar untuk akhirnya memilih nama versi makro yang diinginkan:
yang menyelesaikan hasil menjadi
dan tentu saja memberi kita panggilan fungsi aktual yang diinginkan:
Menyatukan semuanya, dengan beberapa penataan ulang pernyataan agar lebih mudah dibaca, seluruh sumber dari contoh 2-argumen ada di sini:
Meskipun rumit, jelek, membebani pengembang API, ada solusi untuk membebani dan menyetel parameter opsional fungsi C / C ++ bagi kita orang gila. Penggunaan API kelebihan beban yang keluar menjadi sangat menyenangkan dan menyenangkan. =)
Jika ada kemungkinan penyederhanaan lebih lanjut dari pendekatan ini, harap beri tahu saya di
https://github.com/jason-deng/C99FunctionOverload
Sekali lagi terima kasih khusus kepada semua orang brilian yang menginspirasi dan menuntun saya untuk mencapai karya ini! =)
sumber
Untuk siapa pun yang dengan susah payah mencari solusi VA_NARGS yang bekerja dengan Visual C ++. Makro berikut bekerja untuk saya dengan sempurna (juga dengan parameter nol!) Di visual c ++ express 2010:
Jika Anda menginginkan makro dengan parameter opsional, Anda dapat melakukan:
Itu berhasil untuk saya juga di vc. Tapi itu tidak berfungsi untuk parameter nol.
sumber
unresolved external symbol _bool referenced in function _main
gcc
/g++
mendukung makro varargs tetapi menurut saya ini tidak standar, jadi gunakan dengan risiko Anda sendiri.sumber
DISCLAIMER: Sebagian besar tidak berbahaya.
sumber
:%s/MY_MACRO_/THINK_/g
:)i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
Bukan itu yang dirancang untuk preprocessor.
Yang mengatakan, jika Anda ingin memasuki area pemrograman makro yang sangat menantang dengan sedikit keterbacaan, Anda harus melihat pustaka preprocessor Boost . Lagi pula, itu tidak akan menjadi C ++ jika tidak ada tiga tingkat pemrograman yang sepenuhnya kompatibel dengan Turing (preprocessor, metaprogramming template, dan base level C ++)!
sumber
Anda tahu berapa banyak argumen yang akan Anda sampaikan sehingga tidak perlu membebani secara berlebihan.
sumber
Versi yang lebih ringkas dari kode Derek Ledbetter:
sumber
Sebagai penggemar berat monster makro yang mengerikan, saya ingin memperluas jawaban Jason Deng dan membuatnya benar-benar dapat digunakan. (Baik atau buruk.) Yang asli tidak terlalu bagus untuk digunakan karena Anda perlu memodifikasi sup alfabet besar setiap kali Anda ingin membuat makro baru dan bahkan lebih buruk jika Anda membutuhkan jumlah argumen yang berbeda.
Jadi saya membuat versi dengan fitur-fitur ini:
Saat ini saya baru membuat maksimum 16 argumen, tetapi jika Anda membutuhkan lebih banyak (sungguh sekarang? Anda semakin konyol ...) Anda dapat mengedit FUNC_CHOOSER dan CHOOSE_FROM_ARG_COUNT, lalu menambahkan beberapa koma ke NO_ARG_EXPANDER.
Silakan lihat jawaban luar biasa Jason Deng untuk detail lebih lanjut tentang penerapannya, tetapi saya hanya akan meletakkan kodenya di sini:
sumber
Anda dapat menggunakan
BOOST_PP_OVERLOAD
dariboost
perpustakaan.Contoh dari dokumen peningkatan resmi :
sumber
Bergantung pada apa yang Anda butuhkan, Anda dapat melakukannya dengan var args dengan makro. Sekarang, parameter opsional atau kelebihan makro, tidak ada hal seperti itu.
sumber
Tak satu pun dari contoh di atas (dari Derek Ledbetter, David Sorkovsky, dan Joe D) untuk menghitung argumen dengan makro bekerja untuk saya menggunakan Microsoft VCC 10.
__VA_ARGS__
Argumen selalu dianggap sebagai argumen tunggal (memberi tanda dengan##
atau tidak), jadi pergeseran argumen di mana contoh-contoh itu bergantung tidak berhasil.Jadi, jawaban singkatnya, seperti yang dinyatakan oleh banyak orang di atas: tidak, Anda tidak dapat membebani makro atau menggunakan argumen opsional padanya.
sumber