純規の暇人趣味ブログ

首を突っ込んで足を洗う

[VC++]#ifdef _DEBUGが効かない時はランタイムライブラリを疑え

      2016/08/24    HimaJyun

最近はDirectXに手出しして(、Minecraftサーバは放置して)いる、DXライブラリは使わずに直接DirectX(9)を扱うと言うマゾプレイ

例に依ってMicrosoftの謎い仕様のせいで「#ifdef _DEBUG」が使えないと言う謎い挙動に悩まされたので対処方法を(未来の自分用に)書いておく。

一応書いておくと、VisualStudio 2015 Community

#ifdef _DEBUGが効かないゾ

C++には「メモリリーク」とか言う厄介な物がある、手元の本にはそれを検出するための便利な便利な「crtdbg.h」とかがある事が書かれてあった

要はこんな感じ

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>

#ifdef _DEBUG // メモリリーク発見君
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <stdlib.h>
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif // _DEBUG

//=======================
// よくある普通のWinMain
//=======================
int WINAPI WinMain(_In_ HINSTANCE hInstance,
				   _In_opt_ HINSTANCE hPrevInstance,
				   _In_ LPSTR lpCmdLine,
				   _In_ int nCmdShow) {

	#ifdef _DEBUG
	// メモリリーク検出君呼び出し
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
	#endif

	return(S_OK);
}

これでメモリリークが発生していればファイル名と行番号を教えてくれる、流石!!(new int;とかして放置してみれば分かる)

「#ifdef _DEBUG」はデバッグモードでビルドした場合にのみ有効なコードになる。

詳細には、デバッグモードでビルドすると「_DEBUG」定数が定義されるので、それを見てプリプロセッサで分岐する至って普通の手法

僕はなぜか勘が強い方みたいで、こう、ふと思う事がある「そう言えばこれリリースモードできちんと無視されるのか?」

と言う訳で試す。

#ifdef _DEBUG
// メモリリーク検出君呼び出し
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
MessageBox(NULL, _T("デバッグモードですよん(はぁと"), _T("デバッグ中なのねん。"), MB_OK | MB_ICONHAND);
#endif

ビルドして実行してみる、とりあえずデバッグビルドしてみてメッセージボックスが表示される事を確認
cpp-ifdef-runtime-library-001

次にリリースモードに切り替えてビルド、実行してみる。
cpp-ifdef-runtime-library-001

何が「(はぁと」じゃこの野郎!!誰だこんなコ汚ド書いた奴!!(自分、ってな具合

この事に気付かずにリリースしてしまうと「夢想封印利用回数無限」とか「マスタースパーク威力9999倍」とかのメニューが入ったままリリースしてしまって、慌てて取り下げる事になる。(まぁ流石にリリース前に成果物の確認くらいはするだろうし気付くだろうが……)

可能性1(そもそも_DEBUGが定義されてない)

確率はかなり低いですが、この逆で「デバッグモードでビルドしてもデバッグコードが有効にならない」パターンが考えられるので一応書いておきます。

「ソリューションエクスプローラからプロジェクトを右クリック->プロパティ」で構成プロパティを出す、すなわち、この画面を出す。
cpp-ifdef-runtime-library-002

構成を「Debug」に切り替える。
cpp-ifdef-runtime-library-003

「C/C++->プリプロセッサ」に進み「プリプロセッサの定義」に「_DEBUG」が含まれているか確認する。
cpp-ifdef-runtime-library-004

含まれていなければ「_DEBUG」を追加する。

ここに指定した値がビルド時に自動的に#defineした物として扱われる、なので、ここに「_DEBUG」がなければ「#define _DEBUG」されていない扱いとなり、そもそものコードが有効にならない

可能性2(ランタイムライブラリがデバッグ系になっている)

リリースモードなのに(何かの拍子で)ランタイムライブラリが「デバッグ」系になっていると、「_DEBUG」が定義されていなくとも自動的に定義されている扱いになるみたい

まずは構成プロパティを開く
cpp-ifdef-runtime-library-002

構成を「Release」に変更する。
cpp-ifdef-runtime-library-005

「C/C++->コード生成」に進み「ランタイムライブラリ」が「マルチスレッド」か「マルチスレッドDLL」、要は「デバッグ」の名が付いていない物に変更する。
cpp-ifdef-runtime-library-006

僕の場合はこれで解決した、本当に謎い挙動、確かにデバッグ版なので「_DEBUG」を定義してくれる親切さは理解出来るが……余計なお世話ね。

オマケ:DLLの有無による違い

ちなみに、末尾「DLL」の有無の違い、必要ないだろうけど説明しておくと

DLLの方を選ぶと実行するPC(すなわちユーザのPC)にもランタイムライブラリのインストールが必要になる。

そう、例の「再頒布可能パッケージ」とかって奴、あれをインストールする必要がある。

DLLの無い方を選ぶと、必要なDLLがビルド時に組み込まれる。

再頒布可能パッケージをインストールする必要がなくなるので、「なんかエラー出て動きません」とか言うエラーメッセージすら添えられていないクソコメントを減らす事が出来る。

その代わり、実行ファイルがデカくなる。

どちらが適しているかはプロジェクトの規模に合わせて選べば良いかと思う。

無料で公開、配布するようなプログラムであれば(配布するサーバのストレージ容量、ネットワーク帯域の節約のために)DLLの方を選んで再頒布可能パッケージを別途インストールさせれば良い。

同人とかで販売する(お金を受け取る)様な物ならDLLの無い方を選んで再頒布可能パッケージをインストールする必要が無いようにした方が良い

買って来たゲームが動かない(動かすのに余分な物をインストールしなければならない)ユーザの目線で考えると腹が立つ以外の何物でもないので……

 - プログラミング ,