C++
macro
cocos2d-x

マクロの扱いには気を付けろ! ver.1

More than 3 years have passed since last update.

元ブログ、マクロの扱いには気を付けろ! ver.1 - 技術は熱いうちに打て!

マクロはとても便利だし、使えばソースコードが綺麗にまとまることは多いにあります。

ですが、このマクロに関して二つ気をつけなければいけないことがあるので、

自戒の意味も込めて書きたいと思いました。



・マクロはテキスト置換である

・マクロにはスコープと言う概念がない

二項目は明日の記事で説明します。


マクロはテキスト置換である

このエントリを書こうと思ったのもこれがきっかけです。

使う際、自分で作る際、それぞれ気をつけてほしい事があります。


使う際に気をつけること

何らかの値を変化させるような要素を与えてはいけない

どういうことでしょうか。

以下のコードを見てください。

※MAXはcocos2d-xのCCStdC-ios.hから引っ張ってきました。


testmacro.cpp

#include <stdio.h>


#define MAX(x,y) (((x) < (y)) ? (y) : (x))

int main(void) {
int a = 1;
int b = 2;
int c = MAX(++a, b); // cに大きい方の値を入れる
printf("a is %d, b is %d", a, b);

return 0;
}


これを実行してみると結果はどうなるでしょうか?



a is 3, b is 2



なんとaの値が変わってしまっています。

これはMAXを使ったプログラマが意図した動作ではないはずです。


何が起こっているのか

マクロはテキスト置換です。

つまり、MAXは以下の様に置換されます。

MAX(x,y) (((x) < (y)) ? (y) : (x))

-> MAX(x,y) (((++a) < (b)) ? (b) : (++a))

-> 左辺((++a) < (b))の評価

-> (2 < 2)の判定

-> falseである

-> (++a)が選択され、また++aが評価される

-> 3


そんなアホな事するかよ...

この例だったら簡単なのでそう思うかもしれません。

本当にそう言い切れるでしょうか?

このマクロに渡す要素がメソッドだった場合を考えてみてください。

int c = MAX(this->hoge(), 0);

hoge内でメンバ変数を変えていた場合。

2度以上評価される事により必要以上にメンバ変数が変更されてしまうと言うこともあるのです。


作る際に気をつけること

各引数は( )で括る

先ほど例に挙げたMAX。

異常に( )が多くなかったですか?

なぜでしょう。

次のマクロを見てみましょう。


#define DIMENSION(width, height) width * height

これはかなり危ないマクロです。

なぜ危ないか分かるでしょうか。

何度も言いますがマクロはテキスト置換です。

int dim = DIMENSION(1 + 2, 2 + 3);

これは以下の様に置換されます。

DIMENSION(1 + 2, 2 + 3)

-> 1 + 2 * 2 + 3

もうお分かりですね。

本当は3 * 5 = 15が出力されてほしいのに1 + 4 + 3の8が出力されてしまいます。

なので正しく定義するなら以下です。

#define DIMENSION(width,height) ((width)*(height))

これで

DIMENSION(1 + 2, 2 + 3)

-> (1+2)*(2+3)

となるので予想通りの結果になるでしょう。

思ってるのと違う動作をするのでこのデバッグはとても大変になるでしょう。

ある意味この記事に辿り着けている時点で答えは見えてるって事ですね笑

誰かのお役に立てば。