Sun Studio 12 Update 1: C++ ユーザーズガイド

付録 B プラグマ

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

プリプロセッサキーワード pragma は C++ 標準の一部ですが、書式、内容、および意味はコンパイラごとに異なります。プラグマは C++ 標準には定義されていません。


注 –

したがってプラグマに依存するコードには移植性はありません。プラグマに依存するコードは移植性がありません。


B.1 プラグマの書式

次に、C++ コンパイラのプラグマのさまざまな書式を示します。


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

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

B.1.1 プラグマの引数としての多重定義関数

ここで示すいくつかのプラグマは、引数として関数名をとります。その関数が多重定義されている場合、プラグマは、その引数として、その直前の関数宣言を使用します。次の例を考えてみましょう。


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 の定義の直後にプログラムを置く必要があります。

次のプラグマは、この節で説明した方法で選択を行います。

B.2 プラグマの詳細

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

B.2.1 #pragma align


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

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


#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 文の ab、および c を符号化された名前に変更します。


namespace foo {
    #pragma align 8 (a, b, c)
    static char a;
    static char b;
    static char c;
}

B.2.2 #pragma does_not_read_global_data


#pragma does_not_read_global_data(funcname [, funcname])

このプラグマは、指定したルーチンが直接的にも間接的にも大域データを読み込まないことをコンパイラに宣言します。この表明により、そうしたルーチンへの呼び出し前後のコードをさらに最適化することができます。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。

このプラグマを使用できるのは、指定した関数のプロトタイプを宣言したあとに限定されます。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。

プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。

B.2.3 #pragma does_not_return


#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 プラグマの引数としての多重定義関数」を参照してください。

B.2.4 #pragma does_not_write_global_data


#pragma does_not_write_global_data(funcname [, funcname])

このプラグマは、指定したルーチンが直接的にも間接的にも大域データを書き込まないことをコンパイラに宣言します。この表明により、そうしたルーチンへの呼び出し前後のコードをさらに最適化することができます。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。

このプラグマを使用できるのは、指定した関数のプロトタイプを宣言したあとに限定されます。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。

プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。

B.2.5 #pragma dumpmacro s


#pragma dumpmacros (value[,value...])

マクロがプログラム内でどのように動作しているかを調べたいときに、このプラグマを使用します。このプラグマは、定義済みマクロ、解除済みマクロ、実際の使用状況といった情報を提供します。マクロの処理順序に従って、標準エラー (stderr) に出力します。dumpmacros プラグマは、ファイルが終わるまで、または #pragma end_dumpmacro に到達するまで、有効です。「B.2.6 #pragma end_dumpmacros を参照してください。value の代わりに次の引数を使用できます。

値  

意味  

defs 

すべての定義済みマクロを出力します 

undefs 

すべての解除済みマクロを出力します 

use 

使用されているマクロの情報を出力します 

loc 

defsundefsuse の位置 (パス名と行番号) も出力します

conds 

条件付き指令で使用したマクロの使用情報を出力します 

sys 

システムヘッダーファイルのマクロについて、すべての定義済みマクロ、解除済みマクロ、使用状況も出力します 


注 –

サブオプション loccondssys は、オプション defsundefsuse の修飾子です。loccondssys は、単独では効果はありません。たとえば #pragma dumpmacros=loc,conds,sys には、何も効果はありません。


dumpmacros プラグマとコマンド行オプションの効果は同じですが、プラグマがコマンド行オプションを上書きします。「A.2.123 -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 を使用した場合、マクロを使用するたびに位置とマクロ名が出力されます。

B.2.6 #pragma end_dumpmacros


#pragma end_dumpmacros

このプラグマは、dumpmacrospragma が終わったことを通知し、マクロ情報の出力を停止します。dumpmacros プラグマ終了時に end_dumpmacros プラグマを使用しなかった場合、dumpmacros プラグマはファイルが終わるまで出力を生成し続けます。

B.2.7 #pragma fini


#pragma fini (identifier[,identifier...])

fini を使用するとidentifier を「終了関数」にします。この関数は void 型で、引数を持ちません。この関数は、プログラム制御によってプログラムが終了する時、または関数内の共有オブジェクトがメモリーから削除されるときに呼び出されます。初期設定関数と同様に、終了関数はリンカーが処理した順序で実行されます。

ソースファイル内で #pragma fini で指定された関数は、そのファイルの中にある静的デストラクタのあとに実行されます。identifier は、この #pragma で指定する前に宣言しておく必要があります。

B.2.8 #pragma hdrstop

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.162 -xpch=vおよび 「A.2.163 -xpchstop=file を参照してください。

B.2.9 #pragma ident


#pragma ident string

ident を使用すると、実行可能ファイルの .comment 部に、string に指定した文字列を記述できます。

B.2.10 #pragma init


#pragma init(identifier[,identifier...])

init を使用すると、identifier (識別子) を「初期設定関数」にします。この関数は void 型で、引数を持ちません。この関数は、実行開始時にプログラムのメモリーイメージを構築する時に呼び出されます。共有オブジェクトの初期設定子の場合、共有オブジェクトをメモリーに入れるとき、つまりプログラムの起動時または dlopen() のような動的ロード時のいずれかに実行されます。初期設定関数の呼び出し順序は、静的と動的のどちらの場合でもリンカーが処理した順序になります。

ソースファイル内で #pragma init で指定された関数は、そのファイルの中にある静的コンストラクタのあとに実行されます。identifier は、この #pragma で指定する前に宣言しておく必要があります。

B.2.11 #pragma must_have_frame


#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;
  }

「B.1.1 プラグマの引数としての多重定義関数」を参照してください。

B.2.12 #pragma no_side_effect


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

no_side_effect は、関数によって持続性を持つ状態が変更されないことを通知するためのものです。このプラグマは、指定された関数がどのような副作用も起こさないことをコンパイラに宣言します。すなわち、これらの関数は、渡された引数だけに依存する値を返します。さらに、これらの関数と、そこから呼び出される関数は、次の処理も行なってはいけません。

コンパイラは、この情報を最適化に使用します。

関数に副作用があると、この関数を呼び出すプログラムの実行結果は未定義になります。

name 引数で、現在の翻訳単位に含まれている関数の名前を指定します。プラグマは関数と同じスコープ内になければならず、また、関数宣言後に位置していなければいけません。プラグマは、関数定義の前に位置していなければいけません。

プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。

B.2.13 #pragma opt


#pragma opt level (funcname[, funcname])

funcname には、現在の翻訳単位内で定義されている関数の名前を指定します。level の値は、指定した関数に対する最適化レベルです。最適化レベル 0、1、2、3、4、5 のいずれかを割り当てることができます。level を 0 に設定すると、最適化を無効にできます。関数は、プラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。プラグマは、最適化する関数の定義を処理する必要があります。

プラグマ内に指定される関数の最適化レベルは、-xmaxopt の値に下げられます。-xmaxopt=off の場合、プラグマは無視されます。

プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。

B.2.14 #pragma pack( n)


#pragma pack([n])

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

n を指定する場合は、0 または 2 の累乗にする必要があります。0 以外の値を指定すると、コンパイラは n バイトの境界整列と、データ型に対するプラットフォームの自然境界のどちらか小さい方を使用します。たとえば次の指令は、自然境界整列が 4 バイトまたは 8 バイト境界である場合でも、指令のあと (および後続の pack 指令の前) に定義されているすべての構造体のメンバーを 2 バイト境界を超えないように揃えます。


#pragma pack(2)

n が 0 であるか省略された場合、メンバー整列は自然境界整列の値に戻ります。

n の値がプラットフォームのもっとも厳密な境界整列と同じかそれ以上の場合には、自然境界整列になります。次の表に、各プラットフォームのもっとも厳密な境界整列を示します。

表 B–1 プラットフォームのもっとも厳密な境界整列

プラットフォーム  

もっとも厳密な境界整列  

x86 

SPARC 一般、V8、V8a、V8plus、V8plusa、V8plusb 

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 

B.2.15 #pragma rarely_called


#pragms rarely_called(funcname[, funcname])

このプラグマは、指定の関数がほとんど呼び出されないことをコンパイラに示唆します。このヒントにより、コンパイラは、プロファイル収集段階に負担をかけることなく、ルーチンの呼び出し元でプロファイルフィードバック方式の最適化を行うことができます。このプラグマはヒントの提示ですので、コンパイラは、このプラグマに基づく最適化を行わないこともあります。

#pragma rarely_called プリプロセッサ指令を使用できるのは、指定の関数のプロトタイプが宣言されたあとだけです。次は、#pragma rarely_called の例です。


extern void error (char *message);
#pragma rarely_called(error)

プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。

B.2.16 #pragma returns_new_memory


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

このプラグマは、指定した関数が新しく割り当てられたメモリーのアドレスを返し、そのポインタがほかのポインタの別名として使用されないことをコンパイラに宣言します。この情報により、オプティマイザはポインタ値をより正確に追跡し、メモリー位置を明確化することができます。この結果、スケジューリングとパイプライン化が改善されます。

このプラグマの宣言が実際には誤っている場合は、該当する関数を呼び出したプログラムの実行結果は保証されません。

name 引数で、現在の翻訳単位に含まれている関数の名前を指定します。プラグマは関数と同じスコープ内になければならず、また、関数宣言後に位置していなければいけません。プラグマは、関数定義の前に位置していなければいけません。

プラグマがその引数として多重定義関数を処理する方法の詳細は、「B.1.1 プラグマの引数としての多重定義関数」を参照してください。

B.2.17 #pragma unknown_control_flow


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

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

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

関数名が多重定義されている場合、最後に宣言された関数が選ばれます。

B.2.18 #pragma weak


#pragma weak name1 [= name2]

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

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

B.2.18.1 #pragma weak name

#pragma weak name という書式の指令は、name を弱い (weak) シンボルに定義します。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        // error, ambiguous function name

このエラーを回避するには、文字列書式を使用します。例を次に示します。


int bar(int);
float bar(float);
#pragma weak "__1cDbar6Fi_i_" // make float bar(int) weak

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