C & C ++ (Trả lời cập nhật)
Theo quan sát trong một bình luận, giải pháp ban đầu của tôi có hai vấn đề:
- Các tham số tùy chọn chỉ có sẵn trong C99 và các tiêu chuẩn mới hơn của họ ngôn ngữ.
- Dấu phẩy dấu phẩy trong định nghĩa enum cũng đặc trưng cho C99 trở lên.
Vì tôi muốn mã của mình càng chung chung càng tốt để hoạt động trên các nền tảng cũ hơn, tôi đã quyết định thực hiện một cú đâm khác vào nó. Nó dài hơn so với trước đây, nhưng nó hoạt động trên các trình biên dịch và tiền xử lý được đặt thành chế độ tương thích C89 / C90. Tất cả các macro được truyền một số lượng đối số thích hợp trong mã nguồn, mặc dù đôi khi các macro đó "mở rộng" thành không có gì.
Visual C ++ 2013 (còn gọi là phiên bản 12) phát ra các cảnh báo về các tham số bị thiếu, nhưng cả mcpp (bộ tiền xử lý nguồn mở yêu cầu tuân thủ cao với tiêu chuẩn) cũng như gcc 4.8.1 (với -std = iso9899: 1990 -pedantic-error) cảnh báo hoặc lỗi cho các yêu cầu vĩ mô đó với một danh sách đối số trống có hiệu quả.
Sau khi xem xét tiêu chuẩn có liên quan (ANSI / ISO 9899-1990, 6.8.3, Thay thế vĩ mô), tôi nghĩ có đủ sự mơ hồ rằng điều này không nên được coi là không chuẩn. "Số lượng đối số trong lệnh gọi macro giống như hàm sẽ đồng ý với số lượng tham số trong định nghĩa macro ...". Nó dường như không loại trừ một danh sách đối số trống miễn là các dấu ngoặc đơn cần thiết (và dấu phẩy trong trường hợp có nhiều tham số) được đặt ra để gọi macro
Đối với vấn đề dấu phẩy, được giải quyết bằng cách thêm một số nhận dạng bổ sung vào bảng liệt kê (trong trường hợp của tôi, MMMM có vẻ hợp lý như bất cứ điều gì để định danh tuân theo 3999 ngay cả khi nó không tuân theo các quy tắc được chấp nhận của trình tự chữ số La Mã chính xác).
Một giải pháp sạch hơn một chút sẽ liên quan đến việc di chuyển enum và các macro hỗ trợ sang một tệp tiêu đề riêng như được ngụ ý trong một nhận xét ở nơi khác và sử dụng undef của các tên macro ngay sau khi chúng được sử dụng để tránh gây ô nhiễm không gian tên. Tên macro tốt hơn chắc chắn nên được chọn là tốt, nhưng điều này là đủ cho nhiệm vụ trong tay.
Giải pháp cập nhật của tôi, theo sau là giải pháp ban đầu của tôi:
#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;
}
Câu trả lời ban đầu (đã nhận được sáu lần nâng cấp đầu tiên, vì vậy nếu không ai nhận được điều này một lần nữa, bạn không nên nghĩ rằng giải pháp cập nhật của tôi đã nhận được các upvote):
Với tinh thần tương tự như một câu trả lời trước đó, nhưng thực hiện theo một cách mà nên được cầm tay sử dụng hành vi chỉ định (mặc dù môi trường khác nhau không phải lúc nào đồng ý trên một số khía cạnh của tiền xử lý). Xử lý một số tham số là tùy chọn, bỏ qua các tham số khác, nó nên hoạt động trên các bộ tiền xử lý không hỗ trợ __VA_ARGS__
macro, bao gồm C ++, nó sử dụng macro gián tiếp để đảm bảo các tham số được mở rộng trước khi dán mã thông báo và cuối cùng nó ngắn hơn và tôi nghĩ dễ đọc hơn ( mặc dù nó vẫn còn khó và có thể không dễ đọc, chỉ dễ hơn):
#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 };