#pragma twice

KAB-studio > プログラミング > #pragma twice > 107 Version 6.07 条件コンパイルの世界

#pragma twice 107 Version 6.07 条件コンパイルの世界

前のページへ 表紙・目次へ 次のページへ

 Version 6.07
条件コンパイルの世界

今回は前回の続き。前回 #if defined や #ifdef を紹介しました
 #define されていたら〜ってヤツね
 C 言語をする上で、これは死ぬほど付き合わなきゃいけない相手です。
そして結構問題のある相手です
問題があるぅ!?
そう。ま、便利な部分もあるけどね
はっ、そういえば水希ちゃんは反マクロ派だった……
じゃ、まず一番メジャーな使われ方から紹介しましょう。 Windef.h を見
てみて。その最初に

#ifndef _WINDEF_
#define _WINDEF_

うん、書いてある
で、最後に

#endif /* _WINDEF_ */

書いてある
これはほとんどのヘッダーファイルに書かれてるもので、火美ちゃんが自
分でヘッダーファイルを作るときには火美ちゃんも書いた方がいいもの
そんな大事なんだ、なんでだろ
考えてみて
えっと、 #ifndef は #if !defined と同じだから _WINDEF_ が #define
されていなかったら、だね
そう
いなかったら、そのあとから……最後の #endif まで、えっと……そのま
ま残るんだよね
そのとおり! よくできたねー、こんがらがるでしょ
こんがらがる〜。でも、そのまま残るって、なんかそのままだね
うん、そういうもの。逆の場合だと
えっと、 _WINDEF_ が #define されてたら、そのあと……空白。ってこ
とは、 Windef.h のほとんどが消えちゃうって事? それってやばそう
それが、こうしないとやばいんだよねー。 _WINDEF_ ってどこで #define
されてる?
どこ……あ、 #ifndef _WINDEF_ のすぐ下でしてるじゃん
ってことは、 Windef.h が最初に読み込まれたときに #define _WINDEF_ 
されるってこと。で、2度目に Windef.h が読み込まれたら?
あ! そっか、そのときは _WINDEF_ が #define されてるから、 #ifndef
で Windef.h のほとんどが読み込まれない!
つまり?
つまり、こうすればヘッダーファイルがたった1度しか読み込まれなくな
る!
そういうことでした
へー、面白い! こーゆー仕組みに使うことできるんだー
 Ver 6.04 ( No.104 ) で〈2重にインクルードしても問題ない〉って
言ったのはこれが理由
ってことは、ほとんどのヘッダーファイルがそうなってるんだ
そうだよ。たとえば Debug プロジェクトの各ヘッダーファイルも

#if !defined(AFX_STDAFX_H__D4FAC1E8_略_F492281A3E79__INCLUDED_)
#define AFX_STDAFX_H__D4FAC1E8_略_F492281A3E79__INCLUDED_

// 中身

#endif // !defined(AFX_STDAFX_H__D4FAC1E8_略_F492281A3E79__INCLUDED_)

む、なんか変な数字か文字かなんか
 VC が自動生成したのだからね。普通は _WINDEF_ みたくヘッダーファイ
ルの名前に近いのにするけど、逆に近いと他のヘッダーファイルと重なっ
ちゃったりするから
だからこんな無茶苦茶なの?
そゆこと。これは各ファイルで違うようになってるから
ふーん。あ、そうそう、質問!
はい、火美ちゃん
今回のふたつもそうだし、前回のこれ

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

もそうなんだけど、 #endif ってなんで最後に #if の条件がコメントさ
れてるの?
それは、ネストが判りにくいから。今回みたいな使い方だと、インデント
しようがないでしょ
ヘッダーファイル全体がインデントしちゃうね
だから、 #if と #endif の対応が判りにくいわけ
そこで対応する #if をコメントで書いとくんだ。でもなんか応急処置っ
ぽいね
ま、プリプロセッサそのものの分かりにくさってことかも。あとついで


#pragma once

そいやこれもプリプロセッサだね。どっかで見たような感じ……
 #pragma は〈プラグマ〉って読んで、色々便利なことができるプリプロ
セッサ。そのひとつが #pragma once で、こうするとこのファイルを1回し
か読み込まないようにしてくれます
へ? じゃー #if - #endif って要らないじゃん
そうもいかないんだよね。 #pragma って最近の開発環境じゃないと使え
ないから
それってもしかして、 #if _MSC_VER > 1000 と関係してる?
関係してます! まさにそのためのプリプロセッサ
ほ〜
この例みたいに、プリプロセッサには〈場合によってプログラムを変える
機能〉もあります
つまりこの例だと、 _MSC_VER って組み込みマクロが、 1000 以上に置き
換わるんなら、 #pragma once をプログラムに置きましょう、ってことだよ

そういうこと。この例では組み込みマクロをフラグにしてるけど、それ以
外の #define されたものをフラグにすることも多いです
たとえば?
自分で #define するものとか。ライブラリの中には #if がいっぱい入っ
てて、自分で #define をすることでライブラリを書き換えることができま

つまりスイッチになるわけだ
他に #define 以外でも #define と同じようなことができます。【プロ
ジェクト】−【設定】ダイアログ開いて
ほい、いつものね
【プロジェクトの設定】ダイアログの【C++】ページ、カテゴリ【一般】
の【プリプロセッサの定義】の項目を見て

WIN32,_DEBUG,_WINDOWS,_AFXDLL,_MBCS

ここに書き込んだのはすべて #define されたものとして判断されます
つまり #define WIN32 ってなってるんだ。でもわざわざここでする必要
ある?
あるよ。この〈プロジェクトの設定〉っていろんな種類作れるから、それ
ごとに #define を変えることができるからね。左上の【設定の対象】を
Win32 Release に変えると……

WIN32,NDEBUG,_WINDOWS,_AFXDLL,_MBCS

を、かなり変わった!
これで #define がかなり変わるから、ライブラリの中身もかなり変わる
わけ
へー
こういうのを〈条件コンパイル〉っていいます
場合分けしてコンパイルするわけねー
ただ……
あ、水希ちゃん、ダークモードだ
確かに、この条件コンパイルはばしばし使われてます。たとえば、
Ver 6.03 ( No.103 ) で紹介した near と far 
普通は空白、ウィンドウズ 3.1 だったら……そっか! その〈 3.1 だっ
たら〉が条件になるんだ!
そゆこと。 MFC を含めて、ライブラリはこの条件コンパイルを使って、
いろんな環境で使えるようになってます。でも!
……でも?
最初はこういうのはやめた方がいいよ。プリプロセッサは、実際にどの
コードが使われてるかって分かりにくいからね
確かに、 #ifdef の分岐って〈それが #define されてるか〉だから、普
通の if とかよりも分かりにくいね
だから、当分はこういうのは使わない方がいいかも
当分っていつまで?
ま、使うなって言ってもマクロに凝ったりする時期って必ずあるから、そ
れで痛い目に遭ってから数年後ってとこかな
なんかそれって危ない青春時代みたいな変な表現……
っつーか実際、プリプロセッサやマクロは便利だから、使い始めるとどん
どん使う傾向にあるんだよね
たとえば?
たとえば、さっきのウィンドウズ 3.1 のとか。これは使わないけど、も
少し話を進めて、ウィンドウズとマックと両方で使えるプログラムとか
そいやマックでもアプリ作れるわけだからねぇ
で、自分で作ったのが、どっちの OS でも使える!
それってむっちゃかっこよくない!?
でしょう。そういうのがプリプロセッサを使うとできるわけ
そっかー、それだとはまっちゃいそうだね
他にも、プリプロセッサならではの機能とかあったり。たとえば __LINE__ 
ってマクロ
なんか妙なのだね
これは、その行の行番号に置き換えられるんです

    TRACE( "%d\n", __LINE__ );

ほー、 TRACE() した行の行番号が出た!
 _MSC_VER なんかと同じ組み込みマクロのひとつで、自動的にその行の行
番号に置き換えてくれます
これは便利だね。エラーが出たときとかこれで報せられるもんね
さらに、たとえばこれを使うマクロ

#define TRACE_LINE TRACE( "%d\n", __LINE__ )

 TRACE_LINE って書けば上のと同じのができるわけだ
これは関数じゃダメでしょ
そっか、関数だとその関数の中の行番号が出ちゃうんだ!
これはマクロの〈置き換える〉って機能を活用した例。他にも、今度解説
するけど TRACE() もマクロ
そいやそんなこと言ってたね
上の【プリプロセッサの定義】に _DEBUG ってあるでしょ。実はこれが 
#define されてなかったら、 TRACE() は空白に置き換えられちゃうんです
つまり消えちゃうってこと?
そゆこと。 TRACE() はデバッグ用だから、リリース用のからは削除する
んでこういう機能が付いてるんです
ほー。要するに便利ってことね
う”……あくまで、便利な部分もある! って話
はいはい
〈2度読み込み〉は #pragma once で、 #define 定数は const int で、
まだ教えてないけど関数似のマクロは inline 関数やテンプレートで、って
感じに置き換えられるんです
つまり、実際にはプリプロセッサは使う必要なし?
少なくとも自分で書く分にはね。プリプロセッサよりも安全なものを使っ
ていくことにしましょう!
はいはい

/*
    Preview Next Story!
*/
でも結局、そのテンプレートとかって知らないとダメってことよね
プリプロセッサは自由度高くて、少し知ってるだけで応用できるからね
じゃープリプロセッサの方がいいんじゃん
そうじゃないんだってばー
というわけで次回
< Version 6.08 バグも色々 >
につづく!
外科医がメスをペーパーナイフ代わりに使っちゃまずいでしょ?
出た水希ちゃん必殺の医療系たとえ!
ちゃかさないでーっ!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。