Parameter Opsional dengan Makro C ++


105

Apakah ada cara untuk mendapatkan parameter opsional dengan C ++ Macro? Semacam kelebihan beban akan menyenangkan juga.



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.

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.


1
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 

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)!


3
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.


4
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 ...
Christian Severin

bukan jawaban yang bagus. ini adalah jawaban yang bagus: stackoverflow.com/q/27049491/893406
v.oddou

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

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

akhirnya saya keluar dengan sesuatu yang menggabungkan semua trik, jadi solusinya

  1. 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 =)
  2. Berfungsi untuk argumen nol , serta argumen dalam jumlah tidak terbatas , jika Anda mengembangkannya lebih jauh untuk memenuhi kebutuhan Anda
  3. 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:

#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_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

#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 20dan 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_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

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! =)


3
Bagaimana cara mengembangkan ini menjadi 3 atau 4 fungsi?
Phylliida

@Phylliida ideone.com/jD0Hm5 - nol sampai lima argumen didukung.
xx

9

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

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.


4
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 */
}

DISCLAIMER: Sebagian besar tidak berbahaya.


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 ++)!


3
#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.


2
Saya sebenarnya bertanya tentang keberadaan fitur tersebut.
Cenoc

3

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

3

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 error
    return 0;
}

2

Anda dapat menggunakan BOOST_PP_OVERLOADdari boostperpustakaan.

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

0

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.


-1

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.


1
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).
puetzk
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.