C プラグマ 新しいページはコチラ

提供: yonewiki
移動: 案内, 検索
(#pragma comment(compiler))
(#pragma once)
 
1行: 1行:
 +
[[C PlusPlus#Cにもあった技術|C++]]へ戻る
 +
 +
 
※このページではC言語にも存在していたという意味で記事タイトルがC プラグマになっていますが、<br />
 
※このページではC言語にも存在していたという意味で記事タイトルがC プラグマになっていますが、<br />
 
[[C PlusPlus|C++]]でも同様です。[[C PlusPlus|C++]]だけの機能がある場合は明記します。<br />
 
[[C PlusPlus|C++]]でも同様です。[[C PlusPlus|C++]]だけの機能がある場合は明記します。<br />
18行: 21行:
  
 
=='''#pragma'''==
 
=='''#pragma'''==
 +
 
<nowiki>#</nowiki>pragmaディレクティブは半角スペースに続けてもう一つのワードと引数パラメータを伴って定義する形式のものがほとんどです。また、マイクロソフト固有の仕様ですが、<nowiki>#</nowiki>pragmaの代わりに__pragmaでも同様の機能として動作するようになっています。これはディレクティブとして先頭に使われる#以降の#はプリプロセッサ演算子の文字列化演算子として扱われることを回避するために用意されています。
 
<nowiki>#</nowiki>pragmaディレクティブは半角スペースに続けてもう一つのワードと引数パラメータを伴って定義する形式のものがほとんどです。また、マイクロソフト固有の仕様ですが、<nowiki>#</nowiki>pragmaの代わりに__pragmaでも同様の機能として動作するようになっています。これはディレクティブとして先頭に使われる#以降の#はプリプロセッサ演算子の文字列化演算子として扱われることを回避するために用意されています。
  
30行: 34行:
  
  
として利用します。ライブラリの検索と読み込みについての指示を実行ファイル(.objや.oの拡張子がよく使われる)に記述するための指示です。リンク処理時に実行ファイルに記述されたライブラリ検索と読み込みを実行させることができるため、結果的に予め準備していたタイプライブラリの関数を利用することができるようになります。このような指示はVisualStudioではプロジェクトのプロパティでも同様に指定する手段が設けられています。
+
として利用します。ライブラリの検索と読み込みについての指示を実行ファイル(.objや.oの拡張子がよく使われる)に記述するための指示です。実行ファイルの先頭付近に実行ファイルが必要とするライブラリに関する記述が加えられるようになっています。ASCIIコードで挿入されるためバイナリーファイルを確認すれば、必要としているライブラリが分かります。「/DEFAULTLIB:ライブラリ名.lib」のように記載されます。リンク処理時に実行ファイルに記述されたライブラリ検索と読み込みを実行させることができるため、結果的に予め準備していたタイプライブラリの関数を利用することができるようになります。このような指示はVisualStudioではプロジェクトのプロパティでも同様に指定する手段が設けられています。
  
  
96行: 100行:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
このようにプラグマと#ifディレクティブのようなプリプロセッサ構文制御やマクロ定義といったディレクティブを併用して、適切なライブラリがロードされるようにプログラムソースで記述することができます。_DEBUGというマクロ定義はVisualStudioではプロジェクトの中でもデバッグモードというプログラム作成時の不具合箇所発見をするための方式を利用している作業のときだけ定義されるマクロで、要するにモード毎に異なるライブラリを使っています。ライブラリにもデバッグ向けのライブラリとリリース向けのライブラリが分かれている場合があり、デバッグ向けのライブラリを使っているとデバッグをしやすいように例外処理による警告が発動するように作られていて、ライブラリ利用中に発生した問題でも、どのような問題が起こったのかをわかりやすく支援してくれる形式になっています。
+
20行目から37行目のようにプラグマと#ifディレクティブのようなプリプロセッサ構文制御やマクロ定義といったディレクティブを併用して、適切なライブラリがロードされるようにプログラムソースで記述することができます。_DEBUGというマクロ定義はVisualStudioではプロジェクトの中でもデバッグモードというプログラム作成時の不具合箇所発見をするための方式を利用している作業のときだけ定義されるマクロで、要するにモード毎に異なるライブラリを使っています。ライブラリにもデバッグ向けのライブラリとリリース向けのライブラリが分かれている場合があり、デバッグ向けのライブラリを使っているとデバッグをしやすいように例外処理による警告が発動するように作られていて、ライブラリ利用中に発生した問題でも、どのような問題が起こったのかをわかりやすく支援してくれる形式になっています。
  
  
110行: 114行:
  
 
があります。それぞれについても以下に説明します。
 
があります。それぞれについても以下に説明します。
 +
 +
 +
 
 +
 
====#pragma comment(compiler)====
 
====#pragma comment(compiler)====
 
使い方は、決まっていて、以下の通りで、第二パラメータはありません。
 
使い方は、決まっていて、以下の通りで、第二パラメータはありません。
117行: 125行:
  
  
実行ファイル(.objファイル)にコンパイラのバージョン情報が記述されます。
+
実行ファイル(.objファイル)にコンパイラのバージョン情報が記述されます。#pragma comment(user,[" "])と#pragma comment(comiler,[" "])は末尾にコンパイル処理ファイル順、記述行での登場順で記述されます。
  
  
Visual Studioのリンク処理では実行ファイル(.obj)に記述されているコンパイラのバージョンに不一致があればエラーとなります。したがって、2つ以上のヘッダファイルがあるプロジェクトで、一度ビルドしたあと、このプラグマを一つのヘッダファイルだけに記述してビルドした場合、objファイルの中のコンパイラバージョンの記述に不一致が発生し、エラーが発生します。リビルドするとこの問題は解決します。逆にある一つのヘッダファイルだけにこのプラグマがある状態で、一度ビルドしたプロジェクトから、このプラグマを消すと同じようにエラーが発生します。この場合もリビルドすることによってコンパイラバージョン情報に関する整合性エラーが解決します。
+
Visual Studioのリンク処理では実行ファイル(.obj)に記述されているコンパイラのバージョンに不一致があればエラーとなります。したがって、2つ以上のヘッダファイルがあるプロジェクトで、一度ビルドしたあと、このプラグマを一つのヘッダファイルだけに記述して、従来利用していたヘッダファイルをコメント化するなどして無効化した状態でビルドした場合、objファイルの中のコンパイラバージョンの記述に不一致が発生し、エラーが発生します。リビルドするとこの問題は解決します。逆にある一つのヘッダファイルだけにこのプラグマがある状態で、一度ビルドしたプロジェクトから、このプラグマを消すと同じようにエラーが発生するケースもあります。この場合もリビルドすることによってコンパイラバージョン情報に関する整合性エラーが解決します。
  
  
自分は現在も使っている開発環境としてVisual Studio Proffesionalの2010~2013までの各バージョンのライセンスを持っていますが、VisualStudio2012をメインに使っています。試しにこのプラグマを使ってみたらobjファイルの末尾に
+
自分は現在も使っている開発環境としてVisual Studio Proffesionalの2010~2013までの各バージョンのライセンスを持っていますが、VisualStudio2012をメインに使っています。現在は使っていない古いバージョンでは、何を血迷ったか個人的に18万円ほど支払ってEnterprise Editionを使っていた無知な時期もあります。何か更新がある耽美にマイクロソフトから大量にCDが送られてきて、なんじゃこりゃ!的な状態に陥っていました。全部のWindows OSのバージョンのCDとか、MSDN(英語があまり読めないのにもちろん英語版)の変更があるだけでもCDが来るし…。何が何であれ、個人的にEnterprise Editionとか買うとかってのは間違っているなと思いました。なんでもいっちゃんええのが勉強になるって思ってましたが、どんなものにも身分相応ってのがあるんだということをこのときはじめて理解しました。話は戻りますが、試しにこのプラグマを使ってみたらobjファイルの末尾に
  
  
Compiler Version 17.00.61030.0
+
Microsoft (R) Optimizing Compiler Version 17.00.61030.0
  
  
 
となるような、ASCIIコードが挿入されました。.objファイルはバイナリーファイルですので、ASCIIコードとして読み取れる部分とそうでない部分とが混在するファイルになっています。
 
となるような、ASCIIコードが挿入されました。.objファイルはバイナリーファイルですので、ASCIIコードとして読み取れる部分とそうでない部分とが混在するファイルになっています。
 +
 +
 +
 
  
 
====#pragma comment(linker, [” ”])====
 
====#pragma comment(linker, [” ”])====
 +
2番目の引数の二重引用符内の先頭に決められたオプション文字列を記述することで、リンクオプションを追加できる指示になります。
 +
 +
 +
*<nowiki>#</nowiki>pragma comment(linker, ”/INCLUDE:[]”)
 +
:シンボル参照の強制と同じ動作で、シンボル(関数名)を記述するとシンボルテーブルに追加されます。これはプログラム中で使われないような関数をリンカが無視するのを防いで、実行可能ファイルからとりこむような処理です。
 +
 +
 +
*<nowiki>#</nowiki>pragma comment(linker, ”/DEFAULTLIB:[]”)
 +
:リンク処理時のライブラリの参照を指定します。優先度はコマンドライン→/DEFAULTLIB→実行可能ファイル(.obj)内に記述されたライブラリ参照の順で参照され、/NODEFAULTLIBによるライブラリ参照の無視に関する指示が優先されます。/NODEFAULTLIB関連の指示が優先されることから、これらのライブラリ参照の指示によらず、外部に依存していないリンク処理になっていることを確実にすることができます。
 +
 +
 +
*<nowiki>#</nowiki>pragma comment(linker, ”/EXPORT:[]”)
 +
:プログラムから関数を出力して、他のファイルから関数を利用できるようにするものです。
 +
:実際には
 +
:<nowiki>#</nowiki>pragma comment(linker, ”/EXPORT:関数名(@1~65536の値(省略可),NONAME(省略可),DATA(省略可))”)
 +
:という具合に利用します。1~65536の値は関数に割り振られる番号で省略すると、リンク処理時に自動で割り付けられます。NONAMEと指定すると関数名を省略した1~65535の番号によって呼び出す関数となります。DATAを使うとデータとして取り扱われます。
 +
:関数の出力はプログラムソースに__declspec(dllexport)キーワードを付けて出力する方法や.defファイルにEXPORT命令を使う方法もあります。なんだか意味不明な説明に感じると思いますが、ここではDLLのような外部から呼び出される関数やデータを作成する場合に使うプラグマがあると覚えておけばよいと思います。詳細は別の項目で記述するつもりです。
 +
 +
 +
*<nowiki>#</nowiki>pragma comment(linker, ”\"/MANIFESTDEPENDENCY:[]\"”)
 +
:マニフェストの依存関係についての記述をします。マニフェストとは声明文のような意味を持つイタリア語ですが、マイクロソフトではこのまにゅフェストと呼ばれる仕組みでClickOnceアプリケーションと呼ばれる自己更新型のアプリケーションを作成するための技術として用意されているものです。マニフェストはXMLによる構文でいくつかの要素を記述することになっていますが、このMANIFESTDEPENDENCYという要素にはアプリケーションのバージョンに関する情報を記述することになっています。詳細な情報については、ここで簡単に説明できるようなものではないので、これも別の場所で説明したいと思います。
 +
 +
 +
*<nowiki>#</nowiki>pragma comment(linker, ”/SECTION:[]”)
 +
:リンク処理ではセクションと呼ばれる単位で関数が配置されます。VxD(Virtual x device Driver:仮想デバイスドライバ OSと同じレベルの権限をもつ制御プログラム)の開発では、このようなセクションで分割される実行ファイルの配置を制御してメモリの使い方を厳密に指定する必要がある場合があります。SECTIONでは以下のよう定義することで、セクションの属性を変更することができます。
 +
 +
:<nowiki>#</nowiki>pragma comment(linker, ”/SECTION:セクション名,(!)セクション属性記号,ALIGN=#2の累乗で表現される値(省略可)”)
 +
 +
:このような動作を理解するにはリンク処理がどのようなものなのかを理解する必要がありますので、理解を深めるにはリンク処理を何度も実行して、セクションの生成のされかたや、デフォルトで定義されるセクションとROM配置やRAM配置の法則を知っていく必要があります。もしく同様の解説をしてくれる文献やサイトに触れる必要があります。ここでは説明しきれませんし、いまのところ自分もそれほど詳しくはありません。ROMやRAMの配置まで意識する必要があるときに使うものだと知っていれば十分だと思います。自分自身も理解を進めた場合には追加の記事を作成したいと思います。
 +
 +
 +
*<nowiki>#</nowiki>pragma comment(linker, ”/MERGE:[]”)
 +
:上記のセクションに関する指示と同様にリンク処理よって生成されるセクションを統合させる処理です。例えば、以下のように使います。
 +
 +
:<nowiki>#</nowiki>pragma comment(linker, ”/MERGE:統合するセクション名1=統合するセクション名2(統合後のセクション名でもある)”)
 +
 +
:とします。
 +
 +
 +
リンク処理の解析は機械語を読み解くのと同様に複雑ですが、動作自体は簡単な処理だと言われています。なんだかよくわからない処理がなされているように思われがちなリンク処理ですが、法則を理解出来れば、もっと理解が深まるということだと思います。
 +
 +
 
  
 
====#pragma comment(user, [” ”])====
 
====#pragma comment(user, [” ”])====
 +
実行ファイル(.obj)に、実行可能ファイル生成に影響を及ぼさないコメントを記述することができます。objファイルはバイナリーファイルなので、ASCIIコードの範囲にあるような1バイトの英文字を使うことをお勧めします。#pragma comment(user,[" "])と#pragma comment(comiler,[" "])は末尾にコンパイル処理ファイル順、記述行での登場順で記述されます。
 +
 +
 +
以下のように記述して利用します。
 +
 +
 +
'''<nowiki>#</nowiki>pragma comment(user, ”コメント(ASCIIコード範囲の文字利用を推奨)”)'''
 +
 +
 +
 +
 
  
 
===#pragma once===
 
===#pragma once===
 +
<nowiki>#</nowiki>includeのようなソース読み込み指示によって、ソースファイルが重複してオープンされないように制御するためのプラグマです。プロジェクトに使われるヘッダファイルとメインソースコードファイルの全てに追加しておくべきプラグマです。以下のマクロと同等だと考えてよいです。
 +
 +
<syntaxhighlight lang="cpp">
 +
FILE sample.h
 +
#ifndef __SAMPLE.H__
 +
#define __SAMPLE.H__
 +
 +
ヘッダファイルプロトタイプ宣言
 +
 +
#endif
 +
</syntaxhighlight>
 +
上記のようなマクロでは、インクルードガードと呼ばれる手法を使っていると呼ばれていて、一度ヘッダファイルを読み込むとファイル名ごとに決められたマクロ名が定義され、2回目にヘッダファイルが呼び出された時にはマクロが定義されているため、#ifndefの条件が成立せず、重複して読み込まれないようになります。
 +
 +
 +
上記のようなインクルードガードを簡潔にするのが、#pragma onceです。処理系によってこのプラグマは使えないものもありますが、最近のコンパイラでは広く適用できるプラグマになっているので、汎用性が高まると思います。
 +
 +
 +
上記マクロを以下のようにプラグマを使って、すっきりと記述できます。
 +
<syntaxhighlight lang="cpp">
 +
FILE sample.h
 +
#pragma once
 +
ヘッダファイルプロトタイプ宣言
 +
 +
</syntaxhighlight>
 +
 +
 +
 但し、この方法は標準化されたものではないため、Microsoft Visual Studio C++ 4.0以上でしか動作しないと考えて扱っても良いのかもしれません。他のコンパイラでも動作するものもありますが、自分の環境に合わせてしまっても良いと思います。他の人に配るなら、
 +
 +
<syntaxhighlight lang="cpp">
 +
FILE sample.h
 +
#ifndef __SAMPLE.H__
 +
#define __SAMPLE.H__
 +
 +
#if _MSC_VER > 1000
 +
#pragma once
 +
#endif
 +
ヘッダファイルプロトタイプ宣言
 +
 +
#endif
 +
</syntaxhighlight>
 +
 +
 +
のように記述すると良いと思います。インクルードガードによる手法をサポートしつつも、#pragma onceの機能も有効利用できます。コンパイル速度が#pragma onceの方が早いので、おすすめです。他のコンパイラでも速度が速くなるようにインクルードガードを実現するには、冗長インクルードガードと呼ばれる手法を使います
 +
 +
<syntaxhighlight lang="cpp">
 +
#ifndef __SAMPLE.H__
 +
#include "sample.h"
 +
#endif
 +
</syntaxhighlight>
 +
 +
のようにしてインクルードガード下上でさらにインクルード分のところでも読み込むかどうかを判断する処理を加えます。インクルード部がかなり煩雑になりますが、2重に読み込むことをより早い段階で避けることが出来るので、コンパイル速度の向上が見込めます。
 +
 +
 +
[[C PlusPlus#Cにもあった技術|C++]]へ戻る

2020年7月12日 (日) 00:00時点における最新版



個人用ツール
名前空間

変種
操作
案内
ツールボックス