C & C ++ (Jawaban Diperbarui)
Seperti yang diamati dalam komentar, solusi asli saya memiliki dua masalah:
- Parameter opsional hanya tersedia dalam C99 dan standar selanjutnya dari rumpun bahasa.
- Trailing koma dalam definisi enum juga khusus untuk C99 dan yang lebih baru.
Karena saya ingin kode saya menjadi generik mungkin untuk bekerja pada platform lama, saya memutuskan untuk mengambil bacokan lain. Ini lebih lama dari sebelumnya, tetapi bekerja pada kompiler dan preprosesor yang diatur ke mode kompatibilitas C89 / C90. Semua makro diberikan sejumlah argumen yang sesuai dalam kode sumber, meskipun terkadang makro itu "tidak berkembang".
Visual C ++ 2013 (alias versi 12) memancarkan peringatan tentang parameter yang hilang, tetapi tidak mcpp (preprocessor open source yang mengklaim kepatuhan tinggi dengan standar) atau gcc 4.8.1 (dengan -std = iso9899: 1990 -pedantic-errors switches) memancarkan peringatan atau kesalahan untuk doa makro dengan daftar argumen kosong yang efektif.
Setelah meninjau standar yang relevan (ANSI / ISO 9899-1990, 6.8.3, Penggantian Makro), saya pikir ada ambiguitas yang cukup bahwa ini tidak boleh dianggap non-standar. "Jumlah argumen dalam doa makro seperti fungsi harus setuju dengan jumlah parameter dalam definisi makro ...". Tampaknya tidak menghalangi daftar argumen kosong selama tanda kurung yang diperlukan (dan koma dalam kasus beberapa parameter) tersedia untuk menjalankan makro
Adapun masalah trailing koma, yang diselesaikan dengan menambahkan pengenal ekstra untuk enumerasi (dalam kasus saya, MMMM yang tampaknya masuk akal seperti apa pun untuk pengidentifikasi untuk mengikuti 3999 bahkan jika itu tidak mematuhi aturan yang diterima dari pengurutan angka Romawi. persis).
Solusi yang sedikit lebih bersih akan melibatkan memindahkan enum dan mendukung makro ke file header terpisah seperti yang tersirat dalam komentar di tempat lain, dan menggunakan undef dari nama makro segera setelah mereka digunakan untuk menghindari polusi namespace. Nama-nama makro yang lebih baik tidak diragukan lagi harus dipilih juga, tetapi ini cukup untuk tugas yang ada.
Solusi saya yang diperbarui, diikuti oleh solusi asli saya:
#define _0(i,v,x)
#define _1(i,v,x) i
#define _2(i,v,x) i##i
#define _3(i,v,x) i##i##i
#define _4(i,v,x) i##v
#define _5(i,v,x) v
#define _6(i,v,x) v##i
#define _7(i,v,x) v##i##i
#define _8(i,v,x) v##i##i##i
#define _9(i,v,x) i##x
#define k(p,s) p##s,
#define j(p,s) k(p,s)
#define i(p) j(p,_0(I,V,X)) j(p,_1(I,V,X)) j(p,_2(I,V,X)) j(p,_3(I,V,X)) j(p,_4(I,V,X)) j(p,_5(I,V,X)) j(p,_6(I,V,X)) j(p,_7(I,V,X)) j(p,_8(I,V,X)) j(p,_9(I,V,X))
#define h(p,s) i(p##s)
#define g(p,s) h(p,s)
#define f(p) g(p,_0(X,L,C)) g(p,_1(X,L,C)) g(p,_2(X,L,C)) g(p,_3(X,L,C)) g(p,_4(X,L,C)) g(p,_5(X,L,C)) g(p,_6(X,L,C)) g(p,_7(X,L,C)) g(p,_8(X,L,C)) g(p,_9(X,L,C))
#define e(p,s) f(p##s)
#define d(p,s) e(p,s)
#define c(p) d(p,_0(C,D,M)) d(p,_1(C,D,M)) d(p,_2(C,D,M)) d(p,_3(C,D,M)) d(p,_4(C,D,M)) d(p,_5(C,D,M)) d(p,_6(C,D,M)) d(p,_7(C,D,M)) d(p,_8(C,D,M)) d(p,_9(C,D,M))
#define b(p) c(p)
#define a() b(_0(M,N,O)) b(_1(M,N,O)) b(_2(M,N,O)) b(_3(M,N,O))
enum { _ a() MMMM };
#include <stdio.h>
int main(int argc, char** argv)
{
printf("%d", MMMCMXCIX * MMMCMXCIX);
return 0;
}
Jawaban asli (yang menerima enam upvotes pertama, jadi jika tidak ada yang pernah memperbaiki ini lagi, Anda seharusnya tidak berpikir solusi saya yang diperbarui mendapat upvotes):
Dengan semangat yang sama dengan jawaban sebelumnya, tetapi dilakukan dengan cara yang seharusnya portabel hanya menggunakan perilaku yang didefinisikan (meskipun lingkungan yang berbeda tidak selalu setuju pada beberapa aspek preprosesor). Memperlakukan beberapa parameter sebagai opsional, mengabaikan yang lain, itu harus bekerja pada preprosesor yang tidak mendukung __VA_ARGS__
makro, termasuk C ++, menggunakan makro tidak langsung untuk memastikan parameter diperluas sebelum menempelkan token, dan akhirnya lebih pendek dan saya pikir lebih mudah dibaca ( meskipun masih sulit dan mungkin tidak mudah dibaca, hanya saja lebih mudah):
#define g(_,__) _, _##I, _##II, _##III, _##IV, _##V, _##VI, _##VII, _##VIII, _##IX,
#define f(_,__) g(_,)
#define e(_,__) f(_,) f(_##X,) f(_##XX,) f(_##XXX,) f(_##XL,) f(_##L,) f(_##LX,) f(_##LXX,) f(_##LXXX,) f(_##XC,)
#define d(_,__) e(_,)
#define c(_,__) d(_,) d(_##C,) d(_##CC,) d(_##CCC,) d(_##CD,) d(_##D,) d(_##DC,) d(_##DCC,) d(_##DCCC,) d(_##CM,)
#define b(_,__) c(_,)
#define a b(,) b(M,) b(MM,) b(MMM,)
enum { _ a };