- ベストアンサー
ifマクロは実現可能?
C言語の仕様だけで制御構文ifと同等の機能を持ったマクロを作ることは可能なんでしょうか? 可能なら実現方法を教えてください。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
#7を投稿した後で気付きましたが、繰返し文の中で使ったときにbreakが使えなかったり、関数の外にreturnできなかったり、gotoで抜けられなかったりと、色々課題山積ですね。 やはり、ifマクロの実装は難しそうです。
その他の回答 (7)
- jacta
- ベストアンサー率26% (845/3158)
ちなみに、C++ならこんな風に実現できるかと思います。 #include <iostream> struct if_base_ { void else_clause() {} }; #define join(a, b) join_(a, b) #define join_(a, b) a ## b #define IF(expr) \ struct join(if_, __LINE__) : if_base_ { \ join(if_, __LINE__)() { \ (expr) ? then_clause() : else_clause(); \ } \ void then_clause() { #define ELSE \ } \ void else_clause() { #define ENDIF \ } \ } auto _; int main() { IF(1) std::cout << 'A' << std::endl; IF(1) std::cout << 'B' << std::endl; ENDIF ELSE std::cout << 'C' << std::endl; ENDIF return 0; }
- xcrOSgS2wY
- ベストアンサー率50% (1006/1985)
jactaさんの回答をヒントに、こんなのを作ってみました。 #include <stdio.h> #define LABEL_THEN_CLAUSE LABEL_THEN_CLAUSE #define LABEL_ELSE_CLAUSE LABEL_ELSE_CLAUSE #define LABEL_END_IF LABEL_END_IF #define cat(x,y) x ## y #define xcat(x,y) cat(x,y) #define label(x) xcat(x, __LINE__) #define IF(cond,then_clause,else_clause) \ { \ switch (cond) { \ default: \ goto label(LABEL_THEN_CLAUSE); \ case 0: \ goto label(LABEL_ELSE_CLAUSE); \ } \ label(LABEL_THEN_CLAUSE): \ then_clause \ goto label(LABEL_END_IF); \ label(LABEL_ELSE_CLAUSE): \ else_clause \ label(LABEL_END_IF): \ ; \ } int main(int argc, char** argv) { IF( 0, { printf("a1\n"); printf("a2\n"); }, { printf("b1\n"); printf("b2\n"); } ); IF( 1, { printf("a1\n"); printf("a2\n"); }, { printf("b1\n"); printf("b2\n"); } ); return 0; } 実行結果: b1 b2 a1 a2 だいたい、いい感じ・・・なのですが、実はIF()をネストするとダメです(^^; プリプロセッサマクロの__LINE__は、いちばん外側のプリプロセッサ展開が完了するまで同じ値(定義)を取り続けるので、IF()をネストさせるとそのすべてで同じラベルが使われてしまいます。
お礼
ご回答ありがとうございます。 やはりネスト処理が問題なんですね。 深さに応じてラベル名がユニークであることが保障されるには。。 マクロ処理を始める前にあらかじめネストの深さを知り、それに応じたラベル名を動的に作り、それからマクロ処理を開始する。 なんて可能でしょうか?
- jacta
- ベストアンサー率26% (845/3158)
> マクロで使うラベルがユニークでも、そのラベルがプログラム内で使えないという制約が付いてしまう。 > その制約があるので制御構文ifとは同等ではない。 > > という意味だったんですね。 > この解釈で正しいでしょうか? 内部的に使用したラベルを使えなくなる問題は、予約済み識別子に該当する名前を使わせてもらえるなら回避可能です。例えば、_Lで始まる名前は予約済みですので、ユーザーが使うことはできません。 それより、やはりユニークなラベルを付けることが難しいのです。 例えば、IF, ELSE, ENDIFというマクロを作るとして、この3つのマクロが、ネストのレベルに応じて、その時点でユニークなラベルを共有しなければなりません。 gotoを使わない方法だと、繰返し文かswitch文を使うことになりますが、これだとbreakの振る舞いがおかしくなってしまいます。 また、三項演算子を使った場合には、副文ではなく式しか使うことができません。
お礼
回答ありがとうございます。 ネスト処理が問題ですか。 入れ子になっている構文で再帰的にネストしてそれぞれのネストの深さにおおじたELSE, ELSE IF に飛ばせるには、どうしてもラベル名がユニークでないといけないんですね。 想像以上に難しい問題があるんですね。 でも大変ヒントになりました。 ありがとうございます。
- jacta
- ベストアンサー率26% (845/3158)
> > 問題 > というのは、マクロ内でどうしても定義しないといけない変数名がすでにスコープ内にあったとき。 > と解釈しましたけどよかったですか? 変数であれば、ブロックをネストすればどうにかなりますが、gotoの飛び先のラベル名が重複してしまうのです。
お礼
回答ありがとうございます。 なるほど~。 それでは、 逆に言うと、マクロで使うラベル名がユニークであれば実現可能なんですね? それが保障される方法ってないものでしょうか。 コンパイルオプションでマクロを指定しておくとか。
補足
訂正します。 マクロで使うラベルがユニークでも、そのラベルがプログラム内で使えないという制約が付いてしまう。 その制約があるので制御構文ifとは同等ではない。 という意味だったんですね。 この解釈で正しいでしょうか?
- jacta
- ベストアンサー率26% (845/3158)
> > elseをつなげたり、 > else を単発で使用することって可能なんですか? 無理です。 ですから、if(条件)副文の後につなげたり、if~else if~elseのようにつなげると問題が出たりするということです。
お礼
回答ありがとうございます。 > if~else if~elseのようにつなげると問題が出たりするということです。 なんと、そこまで考えてらっしゃってたんですね。 > 問題 というのは、マクロ内でどうしても定義しないといけない変数名がすでにスコープ内にあったとき。 と解釈しましたけどよかったですか?
- jacta
- ベストアンサー率26% (845/3158)
> C言語の仕様だけで制御構文ifと同等の機能を持ったマクロを作ることは可能なんでしょうか? ちょっと考えてみましたが、完全に同等のものはできそうにありません。 副文の中でbreakを使ったり、elseをつなげたり、内部的に使っている識別子と同名の識別子をユーザーが定義した場合など、どうしても本来のifとは同等になりません。
お礼
考えていだだきありがとうございます。 そうですか。やっぱり無理なんでしょうか。 私も考えてみたのですが、完全以前のものも思いつかなかったです。 いただいた回答の中で気になったところがあります。 > elseをつなげたり、 else を単発で使用することって可能なんですか?
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
すいません、回答とは違うのですが、 それのどういう意義があるのでしょう?
補足
ただ気になっただけです。 「できる」と聞いたことがあるので。(聞いただけ)
お礼
ご回答ありがとうございます。 実現例も拝見しました。 行き詰ってしまわれたようなので、締め切らせていただきます。 今回の質問の結論は、 同等の機能を持つマクロの実現は困難。 しかし、制限付きで似たような機能のものを実現することはできる。 ということにさせていいただきます。 ここまで検討していただいてほんとにありがとうございました。