この節では、C++ コンパイラが認識するプラグマキーワードについて説明します。
align
デフォルトを無効にして、パラメータ変数のメモリー境界を、指定したバイト境界に揃えます。
does_not_read_global_data
指定リストの関数が、大域データを直接的にも間接的にも読み込まないことをコンパイラに宣言します。
does_not_return
指定関数の呼び出しが返らないことをコンパイラに宣言します。
does_not_write_global_data
指定リストの関数が、大域データを直接的にも間接的にも書き込まないことをコンパイラに宣言します。
dump_macros
コード内でのマクロの使用方法を説明します。
end_dumpmacros
dump_macros プラグマの終わりであることを記します。
fini
指定した関数を終了関数にします。
hdrstop
プリコンパイル済みヘッダーの活性文字列の終わりを示します。
ident
実行可能ファイルの .comment 部に、指定した文字列を入れます。
init
指定した関数を初期化関数にします。
no_side_effect
永続性を持つ状態が関数によって変更されないことを通知します。
pack (n )
構造体オフセットの配置を制御します。n の値は、すべての構造体メンバーに合った最悪の場合の境界整列を指定する数字で、0、1、2、4、8 のいずれかにします。
rarely_called
指定した関数が呼び出されることはほとんどないことをコンパイラに通知します。
returns_new_memory
指定した関数が新しく割り当てられたメモリーのアドレスを返し、そのポインタがほかのポインタの別名として使用されないことをコンパイラに宣言します。
unknown_control_flow
手続き呼び出しの通常の制御フロー属性に違反するルーチンのリストを指定します。
weak
弱いシンボル結合を定義します。
#pragma align integer(variable [,variable...]) |
align を使用すると、指定したすべての変数 variable (変数) のメモリー境界を integer (整数) バイト境界に揃えることができます (デフォルト値より優先されます) 。ただし、次の制限があります。
integer は、1 〜 128 の範囲にある 2 の二乗、つまり、1、2、4、8、16、32、64、128 のいずれかでなければいけません。
variable には、大域変数か静的変数を指定します。局所変数またはクラスメンバー変数は指定できません。
指定されたメモリーの境界値がデフォルト値より小さいと、デフォルト値が使用されます。
この #pragma 行は、指定した変数の宣言より前になければいけません。前にないと、この #pragma 行は無視されます。
この #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]) |
このプラグマは、指定したルーチンが直接的にも間接的にも大域データを読み込まないことをコンパイラに宣言します。この結果、こういったルーチンの呼び出しの周辺のコードをより良く最適化できます。とくに、代入文や代入ストアの場合は、こういった呼び出しのまわりを移動させることができます。
このプラグマを使用できるのは、指定した関数のプロトタイプを宣言したあとに限定されます。大域アクセスに関する宣言が真でない場合、プログラムの動作は未定義となります。
プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。
#pragma does_not_return(funcname [, funcname]) |
このプラグマは、指定のルーチンの呼び出しが返らないことをコンパイラに宣言します。この結果、コンパイラは、この宣言に整合した最適化を行えます。たとえば、呼び出し側で終了したライフ回数を登録すると、より多くの最適化が可能となります。
指定の関数が返らない場合、プログラムの動作は未定義となります。
次の例のとおり、このプラグマを使用できるのは、指定した関数のプロトタイプを宣言したあとに限定されます。
extern void exit(int); #pragma does_not_return(exit) extern void __assert(int); #pragma does_not_return(__assert) |
プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。
#pragma does_not_write_global_data(funcname [, funcname]) |
このプラグマは、指定したルーチンが直接的にも間接的にも大域データを書き込まないことをコンパイラに宣言します。この結果、こういったルーチンの呼び出しの周辺のコードをより良く最適化できます。とくに、代入文や代入ストアの場合は、こういった呼び出しのまわりを移動させることができます。
このプラグマを使用できるのは、指定した関数のプロトタイプを宣言したあとに限定されます。大域アクセスに関する宣言が真でない場合、プログラムの動作は未定義となります。
プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。
#pragma dumpmacros (value[,value...]) |
マクロがプログラム内でどのように動作しているかを調べたいときに、このプラグマを使用します。このプラグマは、定義済みマクロ、解除済みマクロ、実際の使用状況といった情報を提供します。マクロの処理順序に従って、標準エラー (stderr) に出力します。dumpmacros プラグマは、ファイルが終わるまで、または #pragma end_dumpmacro に到達するまで、有効です。「B.2.6 #pragma end_dumpmacros」を参照してください。value の代わりに次の引数を使用できます。
値 |
意味 |
---|---|
defs |
すべての定義済みマクロを出力します |
undefs |
すべての解除済みマクロを出力します |
use |
使用されているマクロの情報を出力します |
loc |
defs、undefs、use の位置 (パス名と行番号) も出力します |
conds |
条件付き指令で使用したマクロの使用情報を出力します |
sys |
システムヘッダーファイルのマクロについて、すべての定義済みマクロ、解除済みマクロ、使用状況も出力します |
サブオプション loc、conds、sys は、オプション defs、undefs、use の修飾子です。loc、conds、sys は、単独では効果はありません。たとえば #pragma dumpmacros=loc,conds,sys には、何も効果はありません。
dumpmacros プラグマとコマンド行オプションの効果は同じですが、プラグマがコマンド行オプションを上書きします。「A.2.120 -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 fini (identifier[,identifier...]) |
fini を使用すると、identifier (識別子) を「終了関数」にします。この関数は void 型で、引数を持ちません。この関数は、プログラム制御によってプログラムが終了する時、または関数内の共有オブジェクトがメモリーから削除されるときに呼び出されます。初期設定関数と同様に、終了関数はリンカーが処理した順序で実行されます。
ソースファイル内で #pragma fini で指定された関数は、そのファイルの中にある静的デストラクタのあとに実行されます。identifier は、この #pragma で指定する前に宣言しておく必要があります。
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 をインクルードファイル内に指定しないでください。
「A.2.155 -xpch=v」および 「A.2.156 -xpchstop=file」を参照してください。
#pragma ident string |
ident を使用すると、実行可能ファイルの .comment 部に、string に指定した文字列を記述できます。
#pragma init(identifier[,identifier...]) |
init を使用すると、identifier (識別子) を「初期設定関数」にします。この関数は void 型で、引数を持ちません。この関数は、実行開始時にプログラムのメモリーイメージを構築する時に呼び出されます。共有オブジェクトの初期設定子の場合、共有オブジェクトをメモリーに入れるとき、つまりプログラムの起動時または dlopen() のような動的ロード時のいずれかに実行されます。初期設定関数の呼び出し順序は、静的と動的のどちらの場合でもリンカーが処理した順序になります。
ソースファイル内で #pragma init で指定された関数は、そのファイルの中にある静的コンストラクタのあとに実行されます。identifier は、この #pragma で指定する前に宣言しておく必要があります。
#pragma no_side_effect(name[,name...]) |
no_side_effect は、関数によって持続性を持つ状態が変更されないことを通知するためのものです。このプラグマは、指定された関数がどのような副作用も起こさないことをコンパイラに宣言します。すなわち、これらの関数は、渡された引数だけに依存する値を返します。さらに、これらの関数と、そこから呼び出される関数は、次の処理も行なってはいけません。
呼び出された時点で、呼び出し元から参照可能になっているプログラム状態の読み取りや書き込み
入出力の実行
呼び出された時点で参照可能になっていないプログラム状態の変更
コンパイラは、この情報を最適化に使用します。
このプラグマで指定した関数が、実際には副作用を与える場合は、この関数を呼び出したプログラムの実行結果は保証されません。
name 引数で、現在の翻訳単位に含まれている関数の名前を指定します。プラグマは関数と同じスコープ内になければならず、また、関数宣言後に位置していなければいけません。プラグマは、関数定義の前に位置していなければいけません。
プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。
#pragma opt level (funcname[, funcname]) |
funcname には、現在の翻訳単位内で定義されている関数の名前を指定します。level の値は、指定した関数に対する最適化レベルです。0、1、2、3、4、5 のいずれかの最適化レベルを割り当てることができます。level を 0 に設定することによって、最適化を無効にできます。関数は、プラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。このプラグマは、関数定義の前に位置していなければいけません。このプラグマは、関数定義の前に位置していなければいけません。
このプラグマに指定された関数の最適化レベルは、-xmaxopt の値に下げられます。-xmaxopt=off の場合、このプラグマは無視されます。
プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。
#pragma pack([n]) |
pack は、構造体メンバーの配置制御に使用します。
n を指定する場合は、 0 または 2 の累乗にする必要があります。0 以外の値を指定すると、コンパイラは n バイトの境界整列と、データ型に対するプラットフォームの自然境界のどちらか小さい方を使用します。たとえば次の指令は、自然境界整列が 4 バイトまたは 8 バイト境界である場合でも、指令のあと (および後続の pack 指令の前) に定義されているすべての構造体のメンバーを 2 バイト境界を超えないように揃えます。
#pragma pack(2) |
n が 0 であるか省略された場合、メンバー整列は自然境界整列の値に戻ります。
n の値がプラットフォームのもっとも厳密な境界整列と同じかそれ以上の場合には、自然境界整列になります。次の表に、各プラットフォームのもっとも厳密な境界整列を示します。
表 B–1 プラットフォームのもっとも厳密な境界整列
プラットフォーム |
もっとも厳密な境界整列 |
---|---|
x86 |
4 |
SPARC 一般、V8、V8a、V8plus、V8plusa、V8plusb |
8 |
SPARC V9、V9a、V9b |
16 |
pack 指令は、次の pack 指令までに存在するすべての構造体定義に適用されます。別々の翻訳単位にある同じ構造体に対して異なる境界整列が指定されると、プログラムは予測できない状態で異常終了する場合があります。特に、コンパイル済みライブラリのインタフェースを定義するヘッダーをインクルードする場合は、その前に pack を使用しないでください。プログラムコード内では、pack 指令は境界整列を指定する構造体の直前に置き、#pragma pack() は構造体の直後に置くことをお勧めします。
SPARC プラットフォーム上で #pragma pack を使用して、型のデフォルトの境界整列よりも密に配置するには、アプリケーションのコンパイルとリンクの両方で -misalign オプションを指定する必要があります。次の表に、整数データ型のメモリーサイズとデフォルトの境界整列を示します。
表 B–2 メモリーサイズとデフォルトの境界整列 (単位はバイト数)
型 |
SPARC V8 サイズ、境界整列 |
SPARC V9 サイズ、境界整列 |
x86 サイズ、境界整列 |
---|---|---|---|
bool |
1, 1 |
1, 1 |
1, 1 |
char |
1, 1 |
1, 1 |
1, 1 |
short |
2, 2 |
2, 2 |
2, 2 |
wchar_t |
4, 4 |
4, 4 |
4, 4 |
int |
4, 4 |
4, 4 |
4, 4 |
long |
4, 4 |
8, 8 |
4, 4 |
float |
4, 4 |
4, 4 |
4, 4 |
double |
8, 8 |
8, 8 |
8, 4 |
long double |
16, 8 |
16, 16 |
12, 4 |
データへのポインタ |
4, 4 |
8, 8 |
4, 4 |
関数へのポインタ |
4, 4 |
8, 8 |
4, 4 |
メンバーデータへのポインタ |
4, 4 |
8, 8 |
4, 4 |
メンバー関数へのポインタ |
8, 4 |
16, 8 |
8, 4 |
#pragms rarely_called(funcname[, funcname]) |
このプラグマは、指定の関数がほとんど呼び出されないことをコンパイラに示唆します。この結果、こういったルーチンの呼び出し側に対するプロファイルフィードバック形式の最適化を、プロファイルの収集を行わずにコンパイラが実施できます。このプラグマが示すことは示唆に過ぎないので、このプラグマに基づいた最適化をコンパイラが行わないこともあります。
#pragma rarely_called プリプロセッサ指令を使用できるのは、指定の関数のプロトタイプが宣言されたあとだけです。次は、#pragma rarely_called の例です。
extern void error (char *message); #pragma rarely_called(error) |
プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。
#pragma returns_new_memory(name[,name...]) |
このプラグマは、指定した関数が新しく割り当てられたメモリーのアドレスを返し、そのポインタがほかのポインタの別名として使用されないことをコンパイラに宣言します。この情報により、オプティマイザはポインタ値をより正確に追跡し、メモリー位置を明確化することができます。この結果、スケジューリングとパイプライン化が改善されます。
このプラグマの宣言が実際には誤っている場合は、該当する関数を呼び出したプログラムの実行結果は保証されません。
name 引数で、現在の翻訳単位に含まれている関数の名前を指定します。プラグマは関数と同じスコープ内になければならず、また、関数宣言後に位置していなければいけません。プラグマは、関数定義の前に位置していなければいけません。
プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。
#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 // あいまいな関数名により、エラー |
このエラーを回避するには、文字列書式を使用します。例を次に示します。
int bar(int); float bar(float); #pragma weak "__1cDbar6Fi_i_" // float bar(int) を弱いシンボルにする |
詳細は、Solaris の『リンカーとライブラリ』を参照してください。