C プリプロセッサ
C++へ戻る
※このページではC言語にも存在していたという意味で記事タイトルがC プリプロセッサになっていますが、
C++でも同様です。C++だけの機能がある場合は明記します。
プリプロセッサ
プリプロセスと呼ばれる機械語翻訳処理(コンパイルやリンク)の前処理と解釈すべき動作があります。英語のpre(プリ)…は日本語で…の前にというような意味ですから、pre-processで処理の前というような直訳にもなりますから、なんとなく前準備のような処理であると認識しておけばよいと思います。このプリプロセスのプログラムで表現する部分をプリプロセッサと呼んでいます。プリプロセッサには各処理系(OS毎にも異なるc言語あるいはC++言語の翻訳処理システムVisual C++やgcc)によって動作や記述が異なる部分が多く、標準化されているものよりもVisual C++におけるMicrosoft固有のプリプロセッサが圧倒的に多く用意されています。
プリプロセスについて触れましたが、機械語翻訳処理の本処理についても触れておきます。コンパイルという単語だけが翻訳という意味でよく飛び交いますが、実際はコンパイルだけでは実行ファイルは生成されません。コンパイルして生成されるのは、実行可能ファイル(.objとか.oという拡張子がよく使われます)です。実行可能であって、実行ファイルではないです。機械語にはなっているんだけど、まだコンピュータに処理をしてもらえる実行プログラムアプリケーションにはなっていないということです。そして、リンク(リンカー)と呼んでいる処理を実施して実行可能ファイルから実行ファイルを生成します。ナニソレ、めんどくせ。一気にやっちゃえばいいじゃん。と思った人もいるかもしれませんが(既に知ってる人からすれば、この流れはよく見る寸劇に思えるでしょう。)、小規模なプログラムを作成している場合にはコンパイルとリンクを同時にひとまとめに実施してしまって問題ないのですが、大規模になってくるとコンパイル処理だけでも時間がかかるようになってきます。大規模なプログラムの一部分だけを変更した場合のコンパイル処理時間を減らすために、中間的な実行可能ファイル(.objや.o …)を生成する仕組みを使って、変更の無いところは既存の実行可能ファイルを使い、変更した部分は新たにコンパイル処理を実行し、新しい実行可能ファイルを生成します。そして、必要となるすべての実行可能ファイルをひとまとめにするリンクと呼んでいる処理を実行します。
C言語でのコンパイル・リンク処理はコンパイルとだけ呼ぶような言い回しが流行していることが、先に書いた機械語翻訳の本処理の実体をわからなくしている一つの原因になったと思います。常に、コンパイル・リンク処理を意識するべく、コンパリンとかの略称で言いまわしておけば誤解がうまれる原因も少しはマシだったのかもしれないと思ったり、別にどうでもいいかと思ったりします。コンパリンとかはやんねぇし…。そのコンパリンの前処理みたいなのが、プリプロセス。
コンパリン…
実際はコンパリンはMakefileやMSBuild(後者がVisualStudioでは一般的)と呼んでいる作成しているプロジェクト(アプリケーションのためのソース一式)から実行ファイルを生成するためのまさにコンパリンをひとまとめにしたファイルによって一気に実行ファイルが生成できるように管理するのが通常です。コンパリン処理をひとまとめに記述したMakefileから新しく編集したプログラムのファイルの部分だけ実行することをVisual Studioではビルドと呼ぶようになりましたし、Makefileに書かれている内容を全部やりなおす処理をリビルドと呼ぶようになりました。ビルドすれば、そのあとはデバッグなしで実行したりデバッグしたりすることもできるようになります。デバッグするためのDebugモードのプログラムアプリとRelease用のアプリのMakefileを個別に準備して、より強固なデバッグシステムを用意し、実際に使うアプリケーションプログラムは余計なものを限りなくそぎ落としたものにする文化もVisual Studioと呼ばれるような開発環境で普通にやれるようになったのも素晴らしいことです。実際のコンパイル処理やリンク処理の内容は非常に複雑な説明を必要としますので、そのあたりはVisual Studioの記事側で触れたいと思います。コンパイルとリンクに関する記事はまだ未作成なので、しばらくはよそ様のWebSiteから知識を得ていただければと思います。
ここでは、Microsoft固有のものも含めて、なるべく多くのプリプロセッサを体系的にまとめたものについて記載したいと思います。
ん~それで、具体的にはプリプロセッサって何?って話になりますが、よく出てくるプリプロセッサのひとつが#includeのようなディレクティブと呼んでいる記述による命令で、これはC言語の最初の学習でも学ぶことの多いヘッダファイルの指定のディレクティブです。ディレクティブというのはテレビのディレクターのような英単語と似た意味の英語で指示という意味をもっています。プリプロセッサは#includeに代表されるような#で始まる命令で構成されるディレクティブによって指定されます。
Visual C++のような開発環境では、Windows用アプリケーション向けの機械語翻訳処理をすることもあって、もっとたくさんの事を本来はプリプロセッサで処理の指定をする必要が発生します。この問題を解決するためにVisual Studioではプロジェクトのプロパティという項目をインタフェースに追加して、プリプロセッサにおける指定を簡略化できる工夫がなされていると考えてよいでしょう。機械語翻訳処理と実行ファイルの生成処理までを含めてVisual Studioではビルドと呼んでいます。このビルド処理を実行した際には実際は非常に複雑なC++の機械語翻訳オプションを引数として指定しています。C++言語のプリプロセッサを知る上で、今ここでそのMicrosoft固有のプリプロセッサの詳細を知っておく必要はないかもしれませんが、プリプロセッサってのは機械語翻訳のために必要な前準備として必要な事柄を指定することだということを思い描ければよいかと思います。Visual Studioを使っている人なら新しいプロジェクトを作成して、プロジェクト作成ウィザードでC++言語の学習のためによく使うコマンドアプリを作ることをやってみて下さい。そしてプロジェクトのプロパティを見るとたくさんの設定項目があって、わけのわかるものから、わけのわからないものまで多岐にわたるオプションの指定がなされているのがわかると思います。その中の項目の構成とプロパティの中のC/C++の中にあるコマンドラインという項目を見ると、各項目のオプションで設定した結果
/Yu"stdafx.h" /GS /analyze- /W3 /Zc:wchar_t /ZI /Gm /Od /sdl /Fd"Debug\vc110.pdb" /fp:precise /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /Oy- /MDd /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\ConsoleApplication003.pch"
とこれくらいの長さのコンパイル時の引数があって、リンクのコマンドラインという項目を見ても
/OUT:"C:\Users\******\Documents\Visual Studio 20xx\Projects\CppRefSample\ConsoleApplication003\Debug\ConsoleApplication003.exe" /MANIFEST /NXCOMPAT /PDB:"C:\Users\******\Documents\Visual Studio 20xx\Projects\CppRefSample\ConsoleApplication003\Debug\ConsoleApplication003.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG /MACHINE:X86 /INCREMENTAL /PGD:"C:\Users\******\Documents\Visual Studio 20xx\Projects\CppRefSample\ConsoleApplication003\Debug\ConsoleApplication003.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"Debug\ConsoleApplication003.exe.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /TLBID:1
とこれくらいの長さの引数が設定されていることになっています。上記は自分がこのC++のリファレンスのサンプルを作っているときのコマンドラインのオプションの一例です。特に小難しい設定をしているわけではありませんが、ほぼ規定値でもこれくらいの引数が指定されています。こういった指定もプログラムの内部から指定できるようにするものがプリプロセッサの役割でもあります。こういった機能は主に#pragmaと呼ばれるキーワードのディレクティブで指定することが多いです。
他にもプリプロセッサの役割としては、機械語翻訳のオプション設定をするだけでなく、プログラムに便利な機能としてマクロと呼ばれるプロジェクト全体にかかわる大きな視点での処理を指定することにもあります。マクロという言葉はミクロという言葉と対になるような英語です。macroとmicroです。それぞれ大きなもの、小さなものという意味です。マクロはプロジェクト全体に関わる処理であることから、このように呼ばれています。マクロは主に変数をある言葉や数値に置き換えたりする処理であったり、プログラムの記述箇所の翻訳する方法や順番あるいはその前に指定したマクロ構文にしたがって逐次、翻訳方法を分岐したりとまるでプログラムを翻訳処理方法をプログラムするような役割があります。マクロに使われるディレクティブも多数存在しています。
プリプロセッサの概要は以上です。
プリプロセッサをいくつかの役割に分けて説明を記述しますので、詳細は個別の項目のページを参照して下さい。
ここで説明していることはMicrosoftのMSDNにあるC++言語リファレンスに記載されているプリプロセッサの説明と変わらないので正しく短い説明の記事はMicrosoftの記事を参考にして下さい。ここでは、自分なりに特別にしっておいた方がよさそうなことを独断と偏見で記載しています。好きな方の記事を見て下さい。自分の言葉からだけ得られるもの、そしてイメージを膨らませれた事柄。Microsoftのページで得られるものそれぞれの個性に少しでも差がでれば、自分のやっていることに意味が見いだせることになりますので、自分にとっては喜ばしい限りです。
C++へ戻る