C & C ++ (คำตอบล่าสุด)
วิธีการแก้ปัญหาดั้งเดิมของฉันมีสองปัญหา:
- พารามิเตอร์ทางเลือกมีให้เฉพาะใน C99 และมาตรฐานที่ใหม่กว่าของตระกูลภาษา
- เครื่องหมายจุลภาคต่อท้ายในคำจำกัดความ enum ก็เฉพาะกับ C99 และใหม่กว่า
เนื่องจากฉันต้องการให้รหัสของฉันเป็นรหัสทั่วไปที่สุดเท่าที่จะเป็นไปได้ในการทำงานบนแพลตฟอร์มที่เก่ากว่าฉันจึงตัดสินใจแทงอีกครั้ง มันมีความยาวมากกว่าที่เคยเป็นมาก่อน แต่มันจะทำงานกับคอมไพเลอร์และตัวประมวลผลล่วงหน้าที่ตั้งค่าเป็นโหมดความเข้ากันได้ของ C89 / C90 มาโครทั้งหมดจะถูกส่งผ่านจำนวนอาร์กิวเมนต์ที่เหมาะสมในซอร์สโค้ดแม้ว่าบางครั้งแมโครเหล่านั้นจะ "ขยาย" เป็นอะไรก็ตาม
Visual C ++ 2013 (aka รุ่น 12) ส่งเสียงคำเตือนเกี่ยวกับพารามิเตอร์ที่หายไป แต่ mcpp (ตัวประมวลผลก่อนโอเพนซอร์สที่อ้างว่าปฏิบัติตามมาตรฐานสูง) หรือ gcc 4.8.1 (พร้อม -std = iso9899: 1990 -pedantic-errors สวิตช์) ปล่อย คำเตือนหรือข้อผิดพลาดสำหรับการเรียกใช้แมโครเหล่านั้นด้วยรายการอาร์กิวเมนต์ที่ว่างเปล่าอย่างมีประสิทธิภาพ
หลังจากตรวจสอบมาตรฐานที่เกี่ยวข้อง (ANSI / ISO 9899-1990, 6.8.3, การแทนที่มาโคร) ฉันคิดว่ามีความกำกวมเพียงพอที่จะไม่ถือว่าเป็นมาตรฐาน "จำนวนอาร์กิวเมนต์ในการภาวนาของมาโครคล้ายฟังก์ชันจะเห็นด้วยกับจำนวนพารามิเตอร์ในนิยามแมโคร ... " ดูเหมือนจะไม่ตัดทอนรายการอาร์กิวเมนต์ที่ว่างเปล่าตราบใดที่วงเล็บที่ต้องการ (และเครื่องหมายจุลภาคในกรณีของพารามิเตอร์หลายตัว) อยู่ในตำแหน่งเพื่อเรียกใช้แมโคร
สำหรับปัญหาเครื่องหมายจุลภาคต่อท้ายที่ได้รับการแก้ไขโดยการเพิ่มตัวระบุพิเศษลงในการแจงนับ (ในกรณีของฉัน MMMM ซึ่งดูเหมือนว่าสมเหตุสมผลตามที่ตัวบ่งชี้ทำตาม 3999 แม้ว่าจะไม่ปฏิบัติตามกฎที่ยอมรับของลำดับเลขโรมัน ตรง)
วิธีแก้ปัญหาที่สะอาดกว่าเล็กน้อยจะเกี่ยวข้องกับการย้าย enum และมาโครที่สนับสนุนไปยังไฟล์ส่วนหัวแยกตามที่ระบุไว้ในความคิดเห็นที่อื่นและใช้ undef ของชื่อแมโครทันทีหลังจากใช้เพื่อหลีกเลี่ยงการสร้างเนมสเปซ ชื่อมาโครที่ดีกว่าก็ไม่ควรสงสัยเช่นกัน แต่ก็เพียงพอแล้วสำหรับงานในมือ
โซลูชันที่อัปเดตแล้วตามด้วยโซลูชันดั้งเดิมของฉัน:
#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;
}
คำตอบเดิม (ซึ่งได้รับหก upvotes แรกดังนั้นหากไม่มีใคร upvotes นี้อีกครั้งคุณไม่ควรคิดว่าโซลูชันที่อัปเดตของฉันจะได้รับ upvotes):
ในจิตวิญญาณเดียวกันกับคำตอบก่อนหน้านี้ แต่ทำในลักษณะที่ควรพกพาโดยใช้พฤติกรรมที่กำหนดเท่านั้น (แม้ว่าสภาพแวดล้อมที่แตกต่างกันไม่เห็นด้วยเสมอในบางแง่มุมของตัวประมวลผลล่วงหน้า) ใช้พารามิเตอร์บางอย่างเป็นตัวเลือกละเว้นผู้อื่นควรทำงานกับตัวประมวลผลล่วงหน้าที่ไม่สนับสนุน__VA_ARGS__
แมโครรวมถึง C ++ ใช้มาโครทางอ้อมเพื่อให้แน่ใจว่าพารามิเตอร์จะถูกขยายก่อนที่จะวางโทเค็นและในที่สุดมันสั้นกว่าและฉันคิดว่าอ่านง่ายขึ้น แม้ว่ามันจะยังคงมีเล่ห์เหลี่ยมและอาจไม่ง่ายต่อการอ่านง่ายกว่า):
#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 };