yohhoyの日記

技術的メモをしていきたい日記

windows.hのmin/maxマクロ回避策4パターン

Microsoft Windowsプラットフォームのヘッダファイル windows.h は悪名高いmin, maxマクロを定義するため、プリプロセス時に意図せずminmaxが置換されてしまい、妙なコンパイルエラーを引き起こす場合がある。一例としてMSVC10で下記コードをコンパイルすると、それぞれエラーメッセージが出力される。

#include <windows.h>
#include <numeric>

//-------- Case 1
int a = 1, b = 2;
int x = std::min(a, b);
//  error C2589: '(' : スコープ解決演算子 (::) の右側にあるトークンは使えません。
//  error C2059: 構文エラー : '::'

//-------- Case 2
double maxvalue = std::numeric_limits<double>::max();
//  warning C4003: マクロ 'max' に指定された実引数の数が少なすぎます。
//  error C2589: '(' : スコープ解決演算子 (::) の右側にあるトークンは使えません。
//  error C2059: 構文エラー : '::'

回避策1:マクロ定義を行なわせない

ヘッダファイル windows.h の include より前に、マクロNOMINMAXを定義しておく。Microsoft KBで公開されている正攻法。

#define NOMINMAX
#include <windows.h>

回避策2:マクロ定義を無効化する

ユーザが明示的にマクロの undef を行う。

#include <windows.h>
#undef min
#undef max

回避策3:プリプロセッサによるマクロ展開を抑止する(1)

minmaxを含む式を括弧でくくり*1、関数形式マクロ(function-like macro)として扱われないようにする。

//-------- Case 1
int x = (std::min)(a, b);  // OK
//-------- Case 2
double maxvalue = (std::numeric_limits<double>::max)();  // OK

回避策4:プリプロセッサによるマクロ展開を抑止する(2)

Boost.Config提供のマクロBOOST_PREVENT_MACRO_SUBSTITUTIONminmaxの直後に配置し、関数形式マクロとして扱われないようにする。

#include <boost/config.hpp>

//-------- Case 1
int x = std::min BOOST_PREVENT_MACRO_SUBSTITUTION (a, b);  // OK
//-------- Case 2
double maxvalue = std::numeric_limits<double>::max BOOST_PREVENT_MACRO_SUBSTITUTION ();  // OK

Boost Library Requirements and Guidelines によると、同マクロは (1) min/max名前空間を指定せずADL経由で呼び出したいとき、または (2) クラスメンバ関数名としてmin/maxを宣言定義するときに用いる。

*1:より正確には識別子 min/max に続くトークンとして、左括弧(lparen) ( が出現しないようにする。