C & C ++ (업데이트 된 답변)
의견에서 볼 수 있듯이 원래 솔루션에는 두 가지 문제가 있습니다.
- 선택적 매개 변수는 C99 이상의 언어 제품군 표준에서만 사용할 수 있습니다.
- 열거 형 정의의 후행 쉼표는 C99 이상에만 해당됩니다.
이전 플랫폼에서 작동하도록 코드를 가능한 한 일반적으로 만들고 싶었으므로 다른 코드를 사용하기로 결정했습니다. 이전보다 오래되었지만 C89 / C90 호환 모드로 설정된 컴파일러 및 전 처리기에서 작동합니다. 모든 매크로는 소스 코드에서 적절한 수의 인수로 전달되지만 때로는 해당 매크로가 아무 것도 확장되지 않습니다.
Visual C ++ 2013 (일명 버전 12)은 누락 된 매개 변수에 대한 경고를 표시하지만 mcpp (표준을 준수한다고 주장하는 오픈 소스 전 처리기) 또는 gcc 4.8.1 (-std = iso9899 : 1990 -pedantic-errors 스위치 사용)을 방출하지 않습니다. 효과적으로 빈 인수 목록이있는 매크로 호출에 대한 경고 또는 오류
관련 표준 (ANSI / ISO 9899-1990, 6.8.3, 매크로 대체)을 검토 한 후 이것이 비표준으로 간주되어서는 안될 모호성이 충분하다고 생각합니다. "함수 유사 매크로를 호출 할 때의 인수 수는 매크로 정의의 매개 변수 수와 일치해야합니다 ..." 매크로를 호출하는 데 필요한 괄호 (및 여러 매개 변수의 경우 쉼표)가있는 한 빈 인수 목록을 배제하지 않는 것 같습니다
후행 쉼표 문제는 열거에 추가 식별자를 추가하여 해결됩니다 (제 경우에는 로마 숫자 시퀀싱의 허용 규칙을 준수하지 않더라도 식별자가 3999를 따르는 것만 큼 합리적인 것처럼 보이는 MMMM) 정확하게).
약간 더 깨끗한 해결책은 다른 곳의 주석에 암시 된 것처럼 열거 형과 지원 매크로를 별도의 헤더 파일로 옮기고 네임 스페이스를 오염시키지 않도록 매크로 이름을 사용한 직후 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;
}
원래의 대답 (처음 6 개의 투표를 받았으므로 아무도 이것을 다시 투표하지 않으면 업데이트 된 솔루션이 투표를했다고 생각해서는 안됩니다) :
이전 답변과 같은 정신이지만 정의 된 동작 만 사용하여 이식 가능 해야하는 방식으로 수행됩니다 (다양한 환경이 전 처리기의 일부 측면에 항상 동의하지는 않지만). 일부 매개 변수는 선택 사항으로 취급하고 다른 매개 변수는 무시하고 __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 };