- ベストアンサー
置換マクロ(引数あり)の作法について。
やりたい事は、双方向リストの追加、削除等々……ですが、双方向リストがプログラム中に2つ以上あって、追加、削除のプログラムを2つ以上書くのは嫌なので、置換マクロを使って解決したいと思いました。 そこで、置換マクロ(引数あり)で、以下のコードを書きましたが、呼び出し側でコンパイラに怒られてしまいます。 if文が無ければ怒られないのですが、何が悪いのか分かりません。 /** リストを実現する構造体 **/ typedef struct _lst { struct _lst *next; struct _lst *back; } LIST; /** l の直後に p を挿入 **/ #define append_list( l, p, listtail ) \ ( \ if( ((LIST *)(l)) == (listtail) ) \ { \ (listtail) = ((LIST *)(p)) \ } \ ((LIST *)(p))->next = ((LIST *)(l)), \ ((LIST *)(p))->back = ((LIST *)(l))->back, \ ((LIST *)(l))->back->next = ((LIST *)(l))->back = ((LIST *)(p)) \ ) 呼び出し側にエラーが無いので、この部分のif文が悪いと思います。 どうか、教えてください。 また、置換マクロ(引数あり)で、オススメするテクニックがありましたら、教えてください。
- みんなの回答 (8)
- 専門家の回答
質問者が選んだベストアンサー
> (*(type)) add_node, \ この部分は変数宣言のつもりですよね。 Cで変数宣言を式にするのは無理ですけど。 この例は型名をパラメータに含むので確かに関数にはできませんね。こういう場合はきれいに書きたかったらgcc拡張を使わないと無理です。 使う場での美しさを気にしなければ、返値を保存する変数もパラメータにしてしまってマクロ内で代入すれば返値なしのマクロになるのでdo{}while(0)で書けてしまいますけど。
その他の回答 (7)
- jacta
- ベストアンサー率26% (845/3158)
型をパラメータにしたいような場合、一番スマートな解放はC++でテンプレートを使うことです。 どうしてもCでなければならないのであれば、関数を定義するためのマクロを定義して、型ごとに関数を作るのが一番かと思います。 こんな感じです。 #define DEFINE_MAKE_NODE(type, node, listhead, listtail) \ type* MAKE_NODE(type *node, type *listhead, type *listtail) \ { \ ... \ } DEFINE_MAKE_NODE(HOGE) DEFINE_MAKE_NODE(BAR)
お礼
回答、有難う御座います。 回答内容が、ハイレベルなので、ちょっと分からないですが、理解できるようにしたいと思います。
- rinkun
- ベストアンサー率44% (706/1571)
返り値がある場合はマクロで複文を含める一般的な方法はありません。gcc拡張で良ければNo.3で書かれている方法が使えますけど。 一般的にはNo.5で書かれているように関数にしてしまうのが正解です。パラメータに型名が含まれるなど関数化不能なものは仕方ないですけど、それもなるべく使わない方が良いですね。
補足
皆様、回答ありがとうございます。 色々とアイデアを貰い、ソースを組んでみた結果、以下のようなところまでやってきました。 ソース上では、やりたいことが実現していますが、上手くコンパイル出来ません。呼び出し側に問題があるのか、もしくは、マクロ文に問題が有るのか、それさえ分かりませんが、呼び出し側に問題は無いと思います。 マクロ側------------------------------------------ #define MAKE_NODE( type, node, listhead, listtail ) \ ( \ (*(type)) add_node, \ add_node = (*(type))MyMalloc( sizeof( (type) )), \ ( (node) != NULL \ ? \ ( (node) == (listhead) ? (listhead) = add_node : 0 ), \ add_node->prev = (node)->prev, \ add_node->next = (node), \ ( (node)->prev ? (node)->prev->next = add_node : 0 ), \ (node)->prev = add_node \ : \ ( (listtail) == NULL \ ? \ add_node->next = NULL, \ add_node->prev = (listtail), \ (listtail)->next = add_node, \ (listtail) = add_node \ : \ add_node->next = NULL, \ add_node->prev = NULL, \ (listhead) = add_node, \ (listtail) = add_node \ ) \ ), \ add_node \ ) 呼び出し側---------------------------------------- pHOGE = MAKE_NODE( HOGE, NULL, pH_ListHead, pH_ListTail ); エラー内容---------------------------------------- 式の構文エラー(呼び出し側の行番号で) >NO.5さん 上記の元となっている関数が、 pHOGE Make_Node( pHOGE target ); と、なっているのですが、もしかしたら置換マクロでどうにかなる問題……なのでしょうか? 元のソースは上記ソースと非常に酷似しています; HOGEについては、このようになっていて、 *nextと*prevは、どの構造体にも同じ名前で存在しているものとします。 typedef struct _HOGE { .... .... struct _HOGE *next; struct _HOGE *prev; } HOGE, pHOGE;
- ChateauAres
- ベストアンサー率43% (64/148)
「追加、削除のプログラムを2つ以上書くのは嫌なので・・」といった場合、素直に関数にした方が見やすいですし、デバッグもしやすいですよ。 複雑な置換マクロを使用した場合、呼び出し側の問題なのか置換マクロ側の問題なのかを判断するために、置換後のプログラムを調べるなど手間がかかることがあります。 ですから、複雑な置換マクロは使用しない事をオススメします。
お礼
回答有難う御座います。 複雑な置換マクロを定義して、呼び出し側を作って、 「さぁ、動け!」 って時に、エラーが出てしまって、置換マクロ側のエラーってのは確実なのですが、実際、どこがエラーなのか分からない経験をしました。 非常にデバックしずらく、一行一行コメントアウトしていって調べました。 複雑な置換マクロ、使用しないことが正解ですね;
- tatsu99
- ベストアンサー率52% (391/751)
ちょっと強引ですが、以下のようにしてはいかがですか。 ----------------------------------- #define append_list( l, p, listtail ) \ { \ if( ((LIST *)(l)) == (listtail) ) \ { \ (listtail) = ((LIST *)(p));\ } \ ((LIST *)(p))->next = ((LIST *)(l));\ ((LIST *)(p))->back = ((LIST *)(l))->back;\ ((LIST *)(l))->back->next = ((LIST *)(l))->back = ((LIST *)(p));\ } -----------------------------------
- jacta
- ベストアンサー率26% (845/3158)
#1の回答にあるように、do {} while(0) を使ってもよいのですが、文ではなく式にしておいた方が関数と同じように使えて便利ですね。 そんな場合は三項演算子を使います。 ifのところを ( (LIST *)(l) == (listtail) ? (listtail) = (LIST *)(p) : (LIST*)0 ), のようにすれば、エラーにならないと思います。 もし、使っているコンパイラがGCCであれば、 ({ ... }) のように丸括弧の中に波括弧を入れれば、中で普通に文が記述でき、全体としては式になるようなマクロ定義が可能です。
- xcrOSgS2wY
- ベストアンサー率50% (1006/1985)
そのマクロを展開するとどうなるか、考えてみてください。 ( if (condition) { expression; } some_more_expression; ) のように展開されるわけですよね。 では、そのような書き方で簡単なテストプログラムを作ってみてください。 もちろんエラーになるはずです。 エラー行を見れば、どこでエラーになったかは分かりますよね。 それを見て、なぜエラーになったのかを考えてみてください。
- rinkun
- ベストアンサー率44% (706/1571)
質問の書き方だとマクロは式として記述する積もりのようですが、if文があるので合いませんね。 マクロで文を使うときは、返値が必要なければ > do { \ > .../*ここに文*/ \ > } while(0) のように書くのがセオリーになってますよ。
補足
すいません。 補足です。 返却値が有る場合が有ります。 その様な場合、どうすれば良いでしょうか?
お礼
回答有難う御座います。 gcc拡張をネットで調べてみたら、普通じゃ有り得ない事が出来るのですね; 自分はgcc拡張を使うのは嫌なので、戻り値をパラメータにする方法を選びました。 本当に有難う御座いました。とても参考になりました。