この付録では、プラグマについて説明します。プラグマとは、プログラマがコンパイラに特定の情報を渡すために使用するコンパイラ指令です。プラグマを使用すると、コンパイル内容を詳細に渡って制御できます。たとえば、pack プラグマを使用すると、構造体の中のデータの配置を変えることができます。プラグマはディレクティブとも呼ばれます。
プリプロセッサキーワード pragma は C++ 標準の一部ですが、書式、内容、および意味はコンパイラごとに異なります。プラグマは C++ 標準には定義されていません。
次に、C++ コンパイラのプラグマのさまざまな書式を示します。
#pragma keyword #pragma keyword ( a [ , a ] ...) [ , keyword ( a [ , a ] ...) ] ,... #pragma sun keyword
変数 keyword は特定の指令を示し、a は引数を示します。
ここで示すいくつかのプラグマは、引数として関数名をとります。その関数が多重定義されている場合、プラグマは、その引数として、その直前の関数宣言を使用します。次の例を考えてみましょう。
int bar(int); int foo(int); int foo(double); #pragma does_not_read_global_data(foo, bar)
この例の foo は、プラグマの直前の foo の宣言である foo(double) を意味し、bar は、単に宣言されている bar である bar(int) を意味します。ここで、foo が再び多重定義されている次の例を考えてみます。
int foo(int); int foo(double); int bar(int); #pragma does_not_read_global_data(foo, bar)
この例の bar は、単に宣言されている bar である bar(int) を意味します。しかし、プラグマは、どのバージョンの foo を使用すべきか分かりません。この問題を解決するには、プラグマが使用すべき foo の定義の直後にプログラムを置く必要があります。
次のプラグマは、このセクションで説明した方法で選択を行います。
does_not_read_global_data
does_not_return
does_not_write_global_data
no_side_effect
opt
rarely_called
returns_new_memory
このセクションでは、C++ コンパイラにより認識されるプラグマキーワードについて説明します。
#pragma align integer(variable[,variable...])
align を使用すると、指定したすべての変数のメモリー境界を integer バイト境界に揃えることができます (デフォルト値より優先されます)。ただし、次の制限があります。
integer は 1 と 128 の間の 2 の累乗である必要があります。有効な値は 1、2、4、8、16、32、64、および 128 です。
variable は大域または静的変数です。これを局所変数またはクラスメンバー変数にすることはできません。
指定された境界がデフォルトより小さい場合は、デフォルトが優先します。
プラグマ行は、指定した変数の宣言より前になければいけません。それ以外の場合は無視されます。
この #pragma 行で指定されていても、プラグマ行に続くコードの中で宣言されない変数は、すべて無視されます。次に、正しく宣言されている例を示します。
#pragma align 64 (aninteger, astring, astruct) int aninteger; static char astring[256]; struct S {int a; char *b;} astruct;
#pragma align を名前空間内で使用するときは、符号化された名前を使用する必要があります。たとえば、次のコード中の、#pragma align 文には何の効果もありません。この問題を解決するには、#pragma align 文の a、b、および c を符号化された名前に変更します。
namespace foo { #pragma align 8 (a, b, c) static char a; static char b; static char c; }
#pragma does_not_read_global_data(funcname[, funcname])
このプラグマは、指定したルーチンが直接的にも間接的にも大域データを読み取らないことをコンパイラに表明することで、そのようなルーチンの呼び出しに関するコードをさらに最適化することが可能です。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。
このプラグマを使用できるのは、指定した関数のプロトタイプを宣言したあとに限定されます。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。
プラグマがその引数として多重定義関数を処理する方法の詳細は、プラグマの引数としての多重定義関数を参照してください。
#pragma does_not_return(funcname[, funcname])
このプラグマは、指定した関数への呼び出しが復帰しないことをコンパイラに表明することで、コンパイラはその仮定と一致する最適化を実行できます。たとえば、レジスタの有効期間が呼び出し元で終了し、これがより多くの最適化を可能にします。
指定した関数が復帰した場合は、プログラムの動作は未定義になります。
次の例のとおり、このプラグマを使用できるのは、指定した関数のプロトタイプを宣言したあとだけです。
extern void exit(int); #pragma does_not_return(exit) extern void __assert(int); #pragma does_not_return(__assert)
プラグマがその引数として多重定義関数を処理する方法の詳細は、プラグマの引数としての多重定義関数を参照してください。
#pragma does_not_write_global_data(funcname[, funcname])
このプラグマは、指定したルーチンが直接的にも間接的にも大域データを書き込まないことをコンパイラに表明することで、そのようなルーチンの呼び出しに関するコードをさらに最適化することが可能です。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。
このプラグマを使用できるのは、指定した関数のプロトタイプを宣言したあとに限定されます。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。
プラグマがその引数として多重定義関数を処理する方法の詳細は、プラグマの引数としての多重定義関数を参照してください。
#pragma dumpmacros (value[,value...])
マクロがプログラム内でどのように動作しているかを調べたいときに、このプラグマを使用します。このプラグマは、定義済みマクロ、解除済みマクロ、実際の使用状況といった情報を提供します。マクロの処理順序に従って、標準エラー (stderr) に出力します。dumpmacros プラグマは、ファイルが終わるまで、または #pragma end_dumpmacro に到達するまで、有効です。#pragma end_dumpmacros を参照してください。次の表に、value の可能な値を示します。
|
dumpmacros プラグマとコマンド行オプションの効果は同じですが、プラグマはコマンド行オプションをオーバーライドします。-xdumpmacros[=value[,value...]]を参照してください。
dumpmacros プラグマは入れ子にならないので、次のコードでは #pragma end_dumpmacros が処理されるとマクロ情報の出力が停止します。
#pragma dumpmacros(defs, undefs) #pragma dumpmacros(defs, undefs) ... #pragma end_dumpmacros
dumpmacros プラグマの効果は累積的です。次のものは、
#pragma dumpmacros(defs, undefs) #pragma dumpmacros(loc)
次と同じ効果を持ちます。
#pragma dumpmacros(defs, undefs, loc)
オプション #pragma dumpmacros(use,no%loc) を使用した場合、使用したマクロそれぞれの名前が一度だけ出力されます。オプション #pragma dumpmacros(use,loc) を使用した場合、マクロを使用するたびに位置とマクロ名が出力されます。
#pragma end_dumpmacros
このプラグマは、dumpmacros pragma が終わったことを通知し、マクロ情報の出力を停止します。dumpmacros プラグマ終了時に end_dumpmacros プラグマを使用しなかった場合、dumpmacros プラグマはファイルが終わるまで出力を生成し続けます。
#pragma error_messages (on|off|default, tag… tag)
このエラーメッセージプラグマは、ソースプログラムの中から、コンパイラが発行するメッセージを制御可能にします。プラグマは警告メッセージにのみ効果があります。-w コマンド行オプションは、すべての警告メッセージを無効にすることでこのプラグマをオーバーライドします。
#pragma error_messages (on, tag… tag)
on オプションは、先行する #pragma error_messages オプション (off オプションなど) のスコープを終了して、-erroff オプションの効果をオーバーライドします。
#pragma error_messages (off, tag… tag)
off オプションは、コンパイラプログラムが指定トークンから始まる特定のメッセージを発行することを禁止します。この特定のエラーメッセージに対するプラグマの指定は、別の #pragma error_messages によって無効にされるか、コンパイルが終了するまで有効です。
#pragma error_messages (default, tag… tag)
default オプションは、指定タグについて、先行する #pragma error_messages ディレクティブのスコープを終了します。
#pragma fini (identifier[,identifier...])
fini を使用すると、identifier を「終了関数」にします。この関数は void 型で、引数を持ちません。この関数は、プログラム制御によってプログラムが終了する時、または関数内の共有オブジェクトがメモリーから削除されるときに呼び出されます。初期設定関数と同様に、終了関数はリンカーが処理した順序で実行されます。
ソースファイル内で #pragma fini で指定された関数は、そのファイルの中にある静的デストラクタのあとに実行されます。identifier は、この #pragma で指定する前に宣言しておく必要があります。
このような関数は #pragma fini 指令の中に登場するたびに、1 回呼び出されます。
hdrstop プラグマをソースファイルヘッダーに埋め込むと、活性文字列の終わりが指示されます。たとえば次のファイルがあるとします。
example% cat a.cc #include "a.h" #include "b.h" #include "c.h" #include <stdio.h> #include "d.h" . . . example% cat b.cc #include "a.h" #include "b.h" #include "c.h"
活性文字列は c.h で終わるので、各ファイルの c.h の後に #pragma hdrstop を挿入します。
#pragma hdrstop を挿入できる場所は、CC コマンドで指定したソースファイルの活性文字列の終わりだけです。#pragma hdrstop をインクルードファイル内に指定しないでください。
-xpch=vおよび -xpchstop=fileを参照してください。
#pragma ident string
ident を使用すると、実行可能ファイルの .comment 部に、string に指定した文字列を記述できます。
#pragma init(identifier[,identifier...])
init を使用すると、identifier (識別子) を「初期設定関数」にします。この関数は void 型で、引数を持ちません。この関数は、実行開始時にプログラムのメモリーイメージを構築する時に呼び出されます。共有オブジェクトの初期設定子の場合、共有オブジェクトをメモリーに入れるとき、つまりプログラムの起動時または dlopen() のような動的ロード時のいずれかに実行されます。初期設定関数の呼び出し順序は、静的と動的のどちらの場合でもリンカーが処理した順序になります。
ソースファイル内で #pragma init で指定された関数は、そのファイルの中にある静的コンストラクタのあとに実行されます。identifier は、この #pragma で指定する前に宣言しておく必要があります。
このような関数は #pragma init 指令の中に登場するたびに、1 回呼び出されます。
ivdep プラグマは、最適化の目的でループ内で検出された、配列参照へのループがもたらす依存関係の一部またはすべてを無視するようにコンパイラに指示します。これによってコンパイラは、マイクロベクトル化、分散、ソフトウェアパイプラインなど、それ以外の場合は不可能なさまざまなループ最適化を実行できます。これは、依存関係が重要ではない、または依存関係が実際に発生しないことをユーザーが把握している場合に使用されます。
#pragma ivdep 指令の解釈は、—xivdep オプションの値に応じて異なります。
#pragma must_have_frame(funcname[,funcname])
このプラグマは、(System V ABI で定義されているとおり) 完全なスタックフレームを必ず持つように、指定した関数リストをコンパイルすることを要求します。このプラグマで関数を列挙する前に、関数のプロトタイプを宣言する必要があります。
extern void foo(int); extern void bar(int); #pragma must_have_frame(foo, bar)
このプラグマを使用できるのは、指定した関数のプロトタイプの宣言後のみに限定されます。プラグマは関数の最後より先に記述する必要があります
void foo(int) { . #pragma must_have_frame(foo) . return; }
プラグマの引数としての多重定義関数を参照してください。
#pragma no_side_effect(name[,name...])
no_side_effect は、関数によって持続性を持つ状態が変更されないことを通知するためのものです。このプラグマは、指定された関数がどのような副作用も起こさないことをコンパイラに宣言します。つまり、これらの関数は、渡された引数だけに依存する結果値を返します。さらに、これらの関数と、そこから呼び出される関数は、次のように動作します。
呼び出し時点で呼び出し側が認識できるプログラム状態の一部に、読み出しまたは書き込みのためにアクセスすることはありません。
入出力を実行しません。
呼び出し時点で認識できるプログラム状態のどの部分も変更しません。
コンパイラは、この情報を最適化に使用します。
関数に副作用があると、この関数を呼び出すプログラムの実行結果は未定義になります。
name 引数で、現在の翻訳単位に含まれている関数の名前を指定します。プラグマは関数と同じスコープ内になければならず、また、関数宣言後に位置していなければいけません。プラグマは、関数定義の前に位置していなければいけません。
プラグマがその引数として多重定義関数を処理する方法の詳細は、プラグマの引数としての多重定義関数を参照してください。
#pragma opt level (funcname[, funcname])
funcname には、現在の翻訳単位内で定義されている関数の名前を指定します。 level の値は、指定した関数に対する最適化レベルです。0、1、2、3、4、5 いずれかの最適化レベルを割り当てることができます。level を 0 に設定すると、最適化を無効にできます。関数は、プラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。プラグマは、最適化する関数の定義を処理する必要があります。
プラグマ内に指定される関数の最適化レベルは、-xmaxopt の値に下げられます。-xmaxopt=off の場合、プラグマは無視されます。
プラグマがその引数として多重定義関数を処理する方法の詳細は、プラグマの引数としての多重定義関数を参照してください。
#pragma pack([n])
pack は、構造体メンバーの配置制御に使用します。
n を指定する場合は、0 または 2 の累乗にする必要があります。0 以外の値を指定すると、コンパイラは n バイトの境界整列と、データ型に対するプラットフォームの自然境界のどちらか小さい方を使用します。たとえば次のディレクティブは、自然境界整列が 4 バイトまたは 8 バイト境界である場合でも、ディレクティブのあと (および後続の pack ディレクティブの前) に定義されているすべての構造体のメンバーを 2 バイト境界を超えないように厳密にそろえます。
#pragma pack(2)
n が 0 であるか省略された場合、メンバー整列は自然境界整列の値に戻ります。
n の値がプラットフォームのもっとも厳密な境界整列と同じかそれ以上の場合には、自然境界整列になります。次の表に、各プラットフォームのもっとも厳密な境界整列を示します。
|
pack 指令は、次の pack 指令までに存在するすべての構造体定義に適用されます。別々の翻訳単位にある同じ構造体に対して異なる配置制御が指定されると、プログラムは予測できない状態で異常終了する場合があります。特に、コンパイル済みライブラリのインタフェースを定義するヘッダーをインクルードする場合は、その前に pack を使用しないでください。プログラムコード内では、pack 指令は境界整列を指定する構造体の直前に置き、#pragma pack() は構造体の直後に置くことをお勧めします。
SPARC プラットフォーム上で #pragma pack を使用して、型のデフォルトの境界整列よりも密に配置するには、アプリケーションのコンパイルとリンクの両方で -misalign オプションを指定する必要があります。次の表に、整数データ型のメモリーサイズとデフォルトの境界整列を示します。
#pragms rarely_called(funcname[, funcname])
このプラグマは、指定の関数がほとんど呼び出されないことをコンパイラに示唆することで、プロファイル収集フェーズのオーバーヘッドを生じさせることなくそのようなルーチンの呼び出し側でのプロファイルフィードバックスタイルの最適化をコンパイラが実行できるようにします。このプラグマはヒントの提示ですので、コンパイラは、このプラグマに基づく最適化を行わないこともあります。
#pragma rarely_called プリプロセッサ指令を使用できるのは、指定の関数のプロトタイプが宣言されたあとだけです。次は、#pragma rarely_called の例です。
extern void error (char *message); #pragma rarely_called(error)
プラグマがその引数として多重定義関数を処理する方法の詳細は、プラグマの引数としての多重定義関数を参照してください。
#pragma returns_new_memory(name[,name...])
このプラグマは、指定した関数が新しく割り当てられたメモリーのアドレスを返し、そのポインタがほかのポインタの別名として使用されないことをコンパイラに宣言します。この情報によって、オプティマイザはポインタ値の追跡性が向上し、メモリー位置を明確化できるため、結果としてスケジューリングとパイプラインが改善されます。
このプラグマの宣言が実際には誤っている場合は、該当する関数を呼び出したプログラムの実行結果は保証されません。
name 引数で、現在の翻訳単位に含まれている関数の名前を指定します。プラグマは関数と同じスコープ内になければならず、また、関数宣言後に位置していなければいけません。プラグマは、関数定義の前に位置していなければいけません。
プラグマがその引数として多重定義関数を処理する方法の詳細は、プラグマの引数としての多重定義関数を参照してください。
#pragma unknown_control_flow(name[,name...])
unknown_control_flow を使用すると、手続き呼び出しの通常の制御フロー属性に違反するルーチンの名前のリストを指定できます。たとえば、setjmp() の直後の文は、ほかのどんなルーチンを呼び出してもそこから返ってくることができます。これは、longjmp() を呼び出すことによって行います。
このようなルーチンを使用すると標準のフローグラフ解析ができないため、呼び出す側のルーチンを最適化すると安全性が確保できません。このような場合に #pragma unknown_control_flow を使用すると安全な最適化が行えます。
関数名が多重定義されている場合、最後に宣言された関数が選ばれます。
#pragma weak name1 [= name2]
weak を使用すると、弱い (weak) 大域シンボルを定義できます。このプラグマは主にソースファイルの中でライブラリを構築するために使用されます。リンカーは弱いシンボルを認識できなくてもエラーメッセージを出しません。
weak プラグマは、次の 2 つの書式でシンボルを指定できます。
文字列書式。文字列は C++ 変数または関数の符号化名である必要があります。無効な符号化名が指定された場合、その名前を参照したときの動作は予測できません。無効な符号化名を参照した場合、コンパイラがエラーを生成しないこともあります。エラーを生成するかどうかにかかわらず、無効な符号化名を使用したときのコンパイラの動作は予測できません。
識別子書式。識別子は以前コンパイル単位で宣言された C++ 関数の明確な識別子である必要があります。識別子書式は変数には使用できません。無効な識別子への参照を検出した場合、フロントエンド (ccfe) はエラーメッセージを生成します。
#pragma weak name という書式のディレクティブは、name を弱い (weak) シンボルに定義します。name のシンボル定義が見つからない場合、リンカーは指示を出しません。また、弱いシンボル定義を複数見つけた場合も、リンカーは警告しません。リンカーは単に最初に検出した定義を使用します。
プラグマ name の強い定義が存在しない場合、リンカーはシンボルの値を 0 にします。
次の指令は、ping を弱いシンボルに定義しています。ping という名前のシンボルの定義が見つからない場合でも、リンカーはエラーメッセージを生成しません。
#pragma weak ping
#pragma weak name1 = name2 という書式では、シンボル name1 を name2 への弱い参照として定義します。name1 がどこにも定義されていない場合、name1 の値は name2 の値になります。name1 が別の場所で定義されている場合、リンカーはその定義を使用し、name2 への弱い参照は無視します。次の指令では、bar がプログラムのどこかで定義されている場合、リンカーはすべての参照先を bar に設定します。そうでない場合、リンカーは bar への参照を foo にリンクします。
#pragma weak bar = foo
識別子書式では、name2 は現在のコンパイル単位内で宣言および定義しなければいけません。例:
extern void bar(int) {...} extern void _bar(int); #pragma weak _bar=bar
文字列書式を使用する場合、シンボルはあらかじめ宣言されている必要はありません。次の例において、_bar と bar の両方が extern "C" である場合、その関数はあらかじめ宣言されている必要はありません。しかし、bar は同じオブジェクト内で定義されている必要があります。
extern "C" void bar(int) {...} #pragma weak "_bar" = "bar"
識別子書式を使用するとき、プラグマのあるスコープ中には指定した名前を持つ関数は、1 つしか存在できません。多重定義関数に識別子書式の #pragma weak を使おうとすると、エラーになります。例:
int bar(int); float bar(float); #pragma weak bar // error, ambiguous function name
このエラーを回避するには、文字列書式を使用します。例を次に示します。
int bar(int); float bar(float); #pragma weak "__1cDbar6Fi_i_" // make float bar(int) weak
詳細は、Oracle Solaris の『リンカーとライブラリ』を参照してください。