マクロにしかできないこと 〜C++でマクロを使うべきな場面〜
C++では, #define で定数を定義するな, const TYPE によるグローバル変数(もしくは適当な名前空間に内包されたグローバル変数)を使え, #define でマクロ関数を定義するな,代わりに inline 関数を使え,みたいなことが言われる.これは確かにその通りだ.
1項 #define ではなく, const と inline を使おう
Scott Meyers (スコット・メイヤーズ): Effective C++ (吉川訳, アスキー出版局, 1998)
に書いてあるように, プリプロセッサよりコンパイラに仕事させるべき だ.でもマクロにしかできないことも多々あるわけで.
case 1: 配列のサイズを取得
#define SIZE_OF_ARRAY(array) (sizeof(array)/sizeof((array)[0]))
SIZE_OF_ARRAY(配列) とすれば,配列の要素数に置き換えられる.これは, inline 関数ではできない.関数の引数に配列を使うと,自動的にポインタとして扱われてしまうからだ.
case 2: The # operator (マクロ引数の文字列化)
例えば変数名を文字列として取得したい場合, inline 関数では逆立ちしてもできない.しかし,マクロの # を使えばできる.
#define print(var) std::cout<<#var"= "<<var<<std::endl
これは, print(x) のように使うと
std::cout<<"x""= "<<x<<std::endl
のように置き換えられるマクロだ*1.マクロ引数 (var) に # をつけると (#var),文字列として置換される.文字列化 # が必要な理由は,
#define print(var) std::cout<<"var= "<<var<<std::endl
とマクロを定義すると, print(x) を
std::cout<<"var= "<<x<<std::endl
のように置き換えるだけだからだ.
case 3: The ## operator (連結)
例えば x が代入されているマクロ引数 HOGE と _0 をくっつけて x_0 のような識別子を生成した場合に使う.こういう連結による識別子の生成も, inline 関数では絶対できない.
#include <iostream> #include <cmath> using namespace std; #define print(var) std::cout<<#var"= "<<var<<std::endl // case 2 の print の使用例 int main(int argc, char**argv) { const double angle[]= {0.5*M_PI, 0.25*M_PI, M_PI/3.0}; #define defc(_j) c##_j= std::cos(angle[_j]) const double defc(0),defc(1),defc(2); print(c0);print(c1);print(c2); #undef defc // ここでしか使わないマクロは undef しておく方がいい return 0; }
この例では defc(0)... の部分は
const double c0= std::cos(angle[0]), c1= std::cos(angle[1]), c2= std::cos(angle[2]);
のように置換される.タイプミスなどで c の後の番号と配列のインデクスを異なったものにしてしまうと,発見しにくいバグになるから,結構便利なことがわかる.つまり,似たような変数を生成するときに,ミスを減らせる.
例えば Octave のソース mx-op-defs.h では,キャストなどの演算子を生成するためのマクロを定義して,コードを共通化しているが,ここでも連結 ## の使用例がみられる.
おまけ: その他のプリプロセッサ・ディレクティブ
#define 以外のプリプロセッサ・ディレクティブについても,適当に紹介しておく.
#ifdef/#ifndef 〜 #endif によるコードの切替え
例えば C と C++ の両方から include するヘッダで,
#ifdef __cplusplus extern "C" { #endif
こういうプリプロセッサ・ディレクティブを使うことはよくある.
ライブラリのヘッダを2重に読まないようにするために,
#ifndef hogehoge_h #define hogehoge_h ... #endif
みたいなコードを書くことも.
参考文献
- ISO/IEC 14882:2003 (E): "16 Preprocessing directives" (C++ の標準規格)