C&C ++(更新された回答)
コメントで見たように、私の元のソリューションには2つの問題がありました。
- オプションのパラメータは、C99以降の言語ファミリの標準でのみ利用可能です。
- enum定義の末尾のコンマもC99以降に固有です。
古いプラットフォームで動作するようにコードをできるだけ汎用的にしたかったので、私はそれをもう一度突き止めることにしました。以前よりも長くなりますが、C89 / C90互換モードに設定されたコンパイラーおよびプリプロセッサーで動作します。すべてのマクロには、ソースコードで適切な数の引数が渡されますが、これらのマクロは「展開」されて何もないことがあります。
Visual C ++ 2013(バージョン12)は、パラメーターの欠落に関する警告を発しますが、mcpp(標準への高い準拠を主張するオープンソースプリプロセッサー)もgcc 4.8.1(-std = iso9899:1990 -pedantic-errorsスイッチ付き)も発しません事実上空の引数リストを持つマクロ呼び出しの警告またはエラー。
関連する標準(ANSI / ISO 9899-1990、6.8.3、マクロ置換)を確認した後、これが非標準と見なされるべきではないという十分な曖昧性があると思います。「関数のようなマクロの呼び出しにおける引数の数は、マクロ定義のパラメーターの数と一致しなければならない...」。マクロを呼び出すために必要な括弧(および複数のパラメーターの場合はコンマ)が配置されている限り、空の引数リストを除外するようには見えません。
末尾のコンマの問題については、列挙に余分な識別子を追加することで解決されます(私の場合、MMMMは、ローマ数字の順序付けの受け入れられた規則に従わなくても、識別子が3999に従うのと同じくらい合理的だと思われます正確に)。
少し簡潔なソリューションには、列挙を移動し、マクロを別のヘッダーファイルにある別のヘッダーファイルにサポートすることと、名前空間の汚染を避けるために使用直後のマクロ名の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 };