Apakah ada cara untuk mendapatkan parameter opsional dengan C ++ Macro? Semacam kelebihan beban akan menyenangkan juga.
No you can't
Apakah ada cara untuk mendapatkan parameter opsional dengan C ++ Macro? Semacam kelebihan beban akan menyenangkan juga.
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.
enum
{
plain = 0,
bold = 1,
italic = 2
};
void PrintString(const char* message, int size, int style)
{
}
#define PRINT_STRING_1_ARGS(message) PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size) PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)
#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )
#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
int main(int argc, char * const argv[])
{
PRINT_STRING("Hello, World!");
PRINT_STRING("Hello, World!", 18);
PRINT_STRING("Hello, World!", 18, bold);
return 0;
}
Ini mempermudah pemanggil makro, tetapi tidak untuk penulisnya.
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 ...
// 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
Menjadi...
XXX_X(, XXX_4(), XXX_3(), XXX_2(), XXX_1(), XXX_0() );
XXX_X(, 1, XXX_4(1), XXX_3(1), XXX_2(1), XXX_1(1), XXX_0(1) );
XXX_X(, 1, 2, XXX_4(1,2), XXX_3(1,2), XXX_2(1,2), XXX_1(1,2), XXX_0(1,2) );
XXX_X(, 1, 2, 3, XXX_4(1,2,3), XXX_3(1,2,3), XXX_2(1,2,3), XXX_1(1,2,3), XXX_0(1,2,3) );
XXX_X(, 1, 2, 3, 4, XXX_4(1,2,3,4), XXX_3(1,2,3,4), XXX_2(1,2,3,4), XXX_1(1,2,3,4), XXX_0(1,2,3,4) );
XXX_X(, 1, 2, 3, 4, 5, XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );
Yang menjadi argumen keenam ...
XXX_0();
XXX_1(1);
XXX_2(1,2);
XXX_3(1,2,3);
XXX_4(1,2,3,4);
5;
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)!
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.
__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 terkait realCreate
, dan definisi makro dengan nomor yang berbeda argumen CREATE_2
, CREATE_1
, CREATE_0
, seperti yang ditunjukkan di bawah ini:
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}
#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)
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_1
bagian berasal dari MACRO_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:
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
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
jadi panggilan dari
create();
create(10);
create(20, 20);
sebenarnya diperluas menjadi
CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);
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
dan 20 ()
dihitung sebagai 2 argumen untuk panggilan terbawah.
Jika kita menggunakan makro pembantu berikut untuk mengembangkannya lebih jauh
##define CHOOSE_FROM_ARG_COUNT(...) \
FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
FUNC_CHOOSER argsWithParentheses
Trailing ,
setelahnya CREATE_1
adalah solusi untuk GCC / CLANG, menekan kesalahan (positif palsu) yang mengatakan itu ISO C99 requires rest arguments to be used
saat meneruskan -pedantic
ke kompilator Anda. Ini FUNC_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 ke
FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);
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:
realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);
Menyatukan semuanya, dengan beberapa penataan ulang pernyataan agar lebih mudah dibaca, seluruh sumber dari contoh 2-argumen ada di sini:
#include <stdio.h>
void realCreate(int x, int y)
{
printf("(%d, %d)\n", x, y);
}
#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
int main()
{
create();
create(10);
create(20, 20);
//create(30, 30, 30); // Compilation error
return 0;
}
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! =)
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:
//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__)))
Itu berhasil untuk saya juga di vc. Tapi itu tidak berfungsi untuk parameter nol.
int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
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.
#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 */
}
DISCLAIMER: Sebagian besar tidak berbahaya.
:%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 ++)!
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)
Anda tahu berapa banyak argumen yang akan Anda sampaikan sehingga tidak perlu membebani secara berlebihan.
Versi yang lebih ringkas dari kode Derek Ledbetter:
enum
{
plain = 0,
bold = 1,
italic = 2
};
void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}
#define PRINT_STRING(...) PrintString(__VA_ARGS__)
int main(int argc, char * const argv[])
{
PRINT_STRING("Hello, World!");
PRINT_STRING("Hello, World!", 18);
PRINT_STRING("Hello, World!", 18, bold);
return 0;
}
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:
#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 error
return 0;
}
Anda dapat menggunakan BOOST_PP_OVERLOAD
dari boost
perpustakaan.
Contoh dari dokumen peningkatan resmi :
#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>
#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)
#if !BOOST_PP_VARIADICS_MSVC
#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)
#else
// or for Visual C++
#define MACRO_ADD_NUMBERS(...) \
BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
#endif
MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9
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.