C++ プログラミングガイド |
第 3 章
プラグマ
この章では、プラグマについて説明します。「プラグマ」とは、コンパイラに特定の情報を渡すために使用するコンパイラ指令です。プラグマを使用すると、コンパイル内容を詳細に渡って制御できます。たとえば、
pack
プラグマを使用すると、構造体の中のデータの配置を変えることができます。プラグマは「指令」とも呼ばれます。プリプロセッサキーワード
pragma
は C++ 標準の一部です。ただし、プラグマの書式、内容、および意味はコンパイラごとに異なります。プラグマは C++ 標準には定義されていません。したがってプラグマに依存するコードには移植性はありません。プラグマの書式
次に、Sun WorkShop C++ コンパイラのプラグマのさまざまな書式を示します。
#pragma keyword#pragma keyword (a [ , a] ... )[ , keyword ( a [, a ] ... )] ,...#pragma sun keyword変数 keyword は特定の指令を示し、a は引数を示します。
Sun WorkShop C++ コンパイラが認識する一般的なプラグマのキーワードを次に示します。
align
- デフォルトを無効にして、パラメータ変数のメモリー境界を、指定したバイト境界に揃えます。init
- 指定した関数を初期化関数にします。fini
- 指定した関数を終了関数にします。ident
- 実行可能ファイルの.comment
部に、指定した文字列を入れます。pack
(
n)
- 構造体オフセットの配置を制御します。n の値は、すべての構造体メンバーに合った最悪の場合の境界整列を指定する数字で、0、1、2、4、8 のいずれかにします。unknown_control_flo
w - 手続き呼び出しの通常の制御フロー属性に違反するルーチンのリストを指定します。weak
- 弱いシンボル結合を定義します。プラグマ一覧
この節では、Sun WorkShop C++ コンパイラが認識するプラグマキーワードについて説明します。
#pragma
align
#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
init
#pragma
init
(identifier [,identifier]...)
init
を使用すると、identifier (識別子) を「初期設定関数」にします。この関数はvoid
型で、引数を持ちません。この関数は、実行開始時にプログラムのメモリーイメージを構築する時に呼び出されます。共有オブジェクトの初期設定子の場合、共有オブジェクトをメモリーに入れるとき、つまりプログラムの起動時またはdlopen()
のような動的ロード時のいずれかに実行されます。初期設定関数の呼び出し順序は、静的と動的のどちらの場合でもリンカーが処理した順序になります。ソースファイル内で
#pragma
init
で指定された関数は、そのファイルの中にある静的コンストラクタの後に実行されます。identifier は、この#pragma
で指定する前に宣言しておく必要があります。
#pragma
fini
#pragma
fini
(identifier [,identifier]...)
fini
を使用すると、identifier (識別子) を「終了関数」にします。この関数はvoid
型で、引数を持ちません。この関数は、プログラム制御によってプログラムが終了する時、または関数内の共有オブジェクトがメモリーから削除されるときに呼び出されます。初期設定関数と同様に、終了関数はリンカーが処理した順序で実行されます。ソースファイル内で #pragma fini で指定された関数は、そのファイルの中にある静的デストラクタの後に実行されます。identifier は、この #pragma で指定する前に宣言しておく必要があります。
#pragma
ident
#pragma ident string
ident
を使用すると、実行可能ファイルの .comment 部に、string に指定した文字列を記述することができます。
#pragma
pack(
n)
# pragma
pack
([
n])
n を指定する場合、0 であるか 2 の累乗でなければなりません。0 以外の値を指定すると、コンパイラは n バイトの境界整列と、データ型に対するプラットフォームの自然境界のどちらか小さい方を使用します。たとえば次の指令は、自然境界整列が 4 バイトまたは 8 バイト境界である場合でも、指令の後 (および後続の
pack
指令の前) に定義されているすべての構造体のメンバーを 2 バイト境界を超えないように揃えます。
#pragma weak pack(2)n が 0 であるか省略された場合、メンバー整列は自然境界整列の値に戻ります。
n の値がプラットフォームの最も厳密な境界整列と同じかそれ以上の場合には、自然境界整列になります。次の表に、各プラットフォームの最も厳密な境界整列を示します。
表 3-1 プラットフォームの最も厳密な境界整列 IA 4 SPARC 一般、V7、V8、V8a、V8plus、V8plusa 8 SPARC V9、V9a、V9b 16
pack
指令は、次のpack
指令までに存在するすべての構造体定義に適用されます。別々の翻訳単位にある同じ構造体に対して異なる境界整列が指定されると、プログラムは予測できない状態で異常終了する場合があります。特に、コンパイル済みライブラリのインタフェースを定義するヘッダーをインクルードする場合は、その前にpack
を使用しないでください。プログラムコード内では、pack
指令は境界整列を指定する構造体の直前に置き、#pragma
pack(
)
は構造体の直後に置くことをお勧めします。SPARC プラットフォーム上で
#pragma
pack
を使用して、型のデフォルトの境界整列よりも密に配置するには、アプリケーションのコンパイルとリンクの両方で-misalign
オプションを指定する必要があります。次の表に、整数データ型のメモリーサイズとデフォルトの境界整列を示します。
#pragma
unknown_control_flow
#pragma unknown_control_flow (name,[,name]...)
unknown_control_flow
を使用すると、手続き呼び出しの通常の制御フロー属性に違反するルーチンの名前のリスト name,[,name]... を指定できます。たとえば、setjmp()
の直後の文は、他のどんなルーチンを呼び出してもそこから返ってくることができます。これは、longjmp() を呼び出すことによって行います。このようなルーチンを使用すると標準のフローグラフ解析ができないため、呼び出す側のルーチンを最適化すると安全性が確保できません。このような場合に
#pragmaunknown_control_flow
を使用すると安全な最適化が行えます。
#pragma
weak
#pragma weak name1 [=name2]
weak
を使用すると、弱い (weak
) 大域シンボルを定義できます。このプラグマは主にソースファイルの中でライブラリを構築するために使用されます。リンカーは弱いシンボルを認識できなくてもエラーメッセージを出しません。
weak
プラグマは、次の 2 つの書式でシンボルを指定できます。
- 文字列
- 文字列は、C++ の変数または関数の符号化された名前でなければなりません。無効な符号化名が指定された場合、その名前を参照したときの動作は予測できません。無効な符号化名を参照した場合、バックエンドがエラーを生成するかどうかは不明です。エラーを生成するかどうかに関わらず、無効な符号化名を参照したときのバックエンドの動作は予測できません。
- 識別子
#pragma
weak
name
#pragma
weak
name という書式の指令は、name を弱い (weak
) シンボルに定義します。name のシンボル定義が見つからなくても、リンカーはエラーメッセージを生成しません。また、弱いシンボルの定義を複数見つけた場合でも、リンカーはエラーメッセージを生成しません。リンカーは単に最初に検出した定義を使用します。他のコンパイル単位に関数または変数の強い (strong) 定義が存在する場合、name はその定義にリンクされます。name の強い定義が存在しない場合、リンカーはシンボルの値を 0 にします。
次の指令は、
ping
を弱いシンボルに定義しています。ping
という名前のシンボルの定義が見つからない場合でも、リンカーはエラーメッセージを生成しません。
#pragma weak ping
#pragma
weak
name1 = name2
#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) を弱いシンボルにする
サン・マイクロシステムズ株式会社 Copyright information. All rights reserved. |
ホーム | 目次 | 前ページへ | 次ページへ | 索引 |