C++ プログラミングガイド

第 3 章 プラグマ

この章では、プラグマについて説明します。「プラグマ」とは、コンパイラに特定の情報を渡すために使用するコンパイラ指令です。プラグマを使用すると、コンパイル内容を詳細に渡って制御できます。たとえば、pack プラグマを使用すると、構造体の中のデータの配置を変えることができます。プラグマは「指令」とも呼ばれます。

プラグマの書式


注 -

プラグマは、C++ 標準の一部ではありません。


次に、CC プラグマのさまざまな書式を示します。


#pragma keyword
#pragma keyword (a [ , a] ... )[ ,  keyword ( a [, a ] ... )] ,...
#pragma sun keyword 

変数 keyword は特定の指令を示し、a は引数を示します。

CC が認識する一般的な指令を次に示します。

プラグマ一覧

#pragma align

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

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

#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 プラットフォームの最も厳密な境界整列

プラットフォーム 

最も厳密な境界整列 

x86 

SPARC 一般、v7、v8、v8a、v8plus、v8plusa 

SPARC v9、v9a 

16 

pack 指令は、次の pack 指令までに存在するすべての構造体定義に適用されます。別々の翻訳単位にある同じ構造体に対して異なる境界整列が指定されると、プログラムは予測できない状態で異常終了する場合があります。特に、コンパイル済みライブラリのインタフェースを定義するヘッダーをインクルードする場合は、その前に pack を使用しないでください。プログラムコード内では、pack 指令は境界整列を指定する構造体の直前に置き、#pragma pack( ) は構造体の直後に置くことをお勧めします。

#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 function-name1 [=function-name2]

function-name1 は関数名を、function-name2 は複数の関数を指定する場合の 2 つ目の関数名を示します。

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

次の指令は、bar を弱いシンボルとして定義しています。リンカーが bar という関数の定義を見つけられなくても、エラーメッセージは生成されません。


#pragma weak bar

次の指令は、プログラム内の任意の位置に bar が定義されている場合は bar に対するすべての参照を解決し、bar が定義されていない場合は foo に対するすべての参照を解決するようにリンカーに指示します。


#pragma weak bar = foo

weak プラグマ内で関数を使用するには、使用前にその関数の宣言を行う必要があります。次に例を示します。


extern void bar(int)
extern void _bar(int)
#pragma weak _bar=bar

#pragma weak を使用する場合の効果は次のとおりです。

詳細は、Solaris のマニュアル『リンカーとライブラリ』を参照してください。


注 -

プラグマ内の名前は、リンカーが認識できるものでなければなりません。つまり、関数に C++ リンケージがある場合には「符号化された」名前でなければなりません。