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++ コンパイラが認識する一般的なプラグマのキーワードを次に示します。

プラグマ一覧

この節では、Sun WorkShop C++ コンパイラが認識するプラグマキーワードについて説明します。

#pragma align

 #pragma align integer (variable [,variable]...) 

align を使用すると、指定したすべての変数 variable (変数) のメモリー境界を integer (整数) バイト境界に揃えることができます (デフォルト値より優先されます) 。ただし、次の制限があります。

#pragma align を名前空間内で使用するときは、符号化された名前を使用する必要があります。たとえば、次のコード中の、#pragma align 文には何の効果もありません。この問題を解決するには、#pragma align 文の ab、および 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])

pack は、構造体メンバーの配置制御に使用します。

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 オプションを指定する必要があります。次の表に、整数データ型のメモリーサイズとデフォルトの境界整列を示します。

表 3-2   メモリーサイズとデフォルトの境界整列 (単位はバイト数)  
SPARC V8
サイズ、境界整列
SPARC V9 サイズ、境界整列 IA サイズ、境界整列
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


#pragma unknown_control_flow

 #pragma unknown_control_flow (name,[,name]...) 

unknown_control_flow を使用すると、手続き呼び出しの通常の制御フロー属性に違反するルーチンの名前のリスト name,[,name]... を指定できます。たとえば、setjmp()の直後の文は、他のどんなルーチンを呼び出してもそこから返ってくることができます。これは、longjmp() を呼び出すことによって行います。

このようなルーチンを使用すると標準のフローグラフ解析ができないため、呼び出す側のルーチンを最適化すると安全性が確保できません。このような場合に
#pragma
unknown_control_flow を使用すると安全な最適化が行えます。

#pragma weak

 #pragma weak name1 [=name2]

name1name2 はシンボル名を示します。

weak を使用すると、弱い (weak) 大域シンボルを定義できます。このプラグマは主にソースファイルの中でライブラリを構築するために使用されます。リンカーは弱いシンボルを認識できなくてもエラーメッセージを出しません。

weak プラグマは、次の 2 つの書式でシンボルを指定できます。

#pragma weak name

name はシンボル名を示します。

#pragma weak name という書式の指令は、name を弱い (weak) シンボルに定義します。name のシンボル定義が見つからなくても、リンカーはエラーメッセージを生成しません。また、弱いシンボルの定義を複数見つけた場合でも、リンカーはエラーメッセージを生成しません。リンカーは単に最初に検出した定義を使用します。

他のコンパイル単位に関数または変数の強い (strong) 定義が存在する場合、name はその定義にリンクされます。name の強い定義が存在しない場合、リンカーはシンボルの値を 0 にします。

次の指令は、ping を弱いシンボルに定義しています。ping という名前のシンボルの定義が見つからない場合でも、リンカーはエラーメッセージを生成しません。

#pragma weak ping

#pragma weak name1 = name2

#pragma weak name1 = name2 という書式の指令は、シンボル name1name2 への弱い参照として定義します。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

文字列書式を使用する場合、シンボルはあらかじめ宣言されている必要はありません。次の例において、_barbar の両方が 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.
ホーム   |   目次   |   前ページへ   |   次ページへ   |   索引