手続きと関数の呼び出し(Delphi)
手続きと関数:インデックス への移動
このトピックでは、以下の事項について説明します。
- プログラム制御とルーチン パラメータ
- オープンな配列コンストラクタ
- inline 指令
プログラム制御とパラメータ
プロシージャまたは関数が呼び出されると、プログラム制御は、呼び出しが行われたポイントから、そのルーチンの本体へと移ります。呼び出しは、ルーチンの宣言された名前を(限定子ありまたはなしで)使うか、または、ルーチンを指すプロシージャ型変数を使用して行うことができます。どちらの場合も、ルーチンがパラメータと共に宣言されていれば、それに対する呼び出しでは、ルーチンのパラメータ リストで指定されている通りの順序、型で、パラメータを渡さなければなりません。ルーチンに渡すパラメータは、実パラメータといい、ルーチンの宣言内のパラメータは、仮パラメータと呼ばれます。
ルーチンを呼び出す際、次の点に留意します:
- 型付き const および値パラメータを渡す式では、相当する仮パラメータと代入の互換性がなければなりません。
- var および out のパラメータを渡す式では、相当する仮パラメータが型なしでない限り、仮パラメータと型が同一でなければなりません。
- 代入可能式のみ、var および out のパラメータを渡せます。
- ルーチンの仮パラメータが型なしの場合、数値や数値付きの true 定数は、実パラメータとして使用できません。
デフォルトのパラメータ値を使用するルーチンを呼び出す際、デフォルト値をいずれかの実パラメータで採用したら、それ以降のすべての実パラメータでデフォルト値を採用しなければなりません。したがって、SomeFunction(,,X) 形式の呼び出しは無効となります。
すべてのパラメータをデフォルト パラメータのみでルーチンに渡す際には、かっこを省略することができます。たとえば、次のプロシージャがあるとします:
procedure DoSomething(X: Real = 1.0; I: Integer = 0; S: string = '');
次の呼び出しは同じです:
DoSomething();
DoSomething;
オープンな配列コンストラクタ
オープン配列コンストラクタにより、関数およびプロシージャ呼び出し内で、直接、配列を構築することができます。それらは、オープン配列パラメータ、または、バリアント オープン配列パラメータとしてのみ、渡すことができます。
オープン配列コンストラクタは、設定コンストラクタと同様、式のシーケンスは、カンマで区切られ、ブラケットで囲まれています。
たとえば、次のように宣言されているとします。
var I, J: Integer;
procedure Add(A: array of Integer);
Add プロシージャは次の文で呼び出すことができます:
Add([5, 7, I, I + J]);
これは以下と同等です。
var Temp: array[0..3] of Integer;
// …
Temp[0] := 5;
Temp[1] := 7;
Temp[2] := I;
Temp[3] := I + J;
Add(Temp);
オープン配列コンストラクタは、値パラメータか const パラメータとしてのみ、渡すことができます。コンストラクタでの式は、配列パラメータの基本型と、代入の互換性がなければなりません。バリアント オープン配列パラメータの場合、式は、異なる型でも構いません。
inline 指令の利用
Delphi コンパイラでは、パフォーマンス向上のため、関数およびプロシージャに、inline 指令でタグをつけることができます。関数やプロシージャが一定の基準を満たす場合、コンパイラは、呼び出しを生成する代わりに、コードを直接挿入します。インライン化はパフォーマンスの最適化の一種で、コードは高速化されますが、領域が消費されます。インライン化により、コンパイラは常に大きなバイナリ ファイルを生成します。inline 指令は、他の指令と同様、関数やプロシージャの宣言および定義で使用されます。
procedure MyProc(x:Integer); inline;
begin
// …
end;
function MyFunc(y:Char) : String; inline;
begin
// …
end;
inline 指令は、コンパイラへの提案です。インライン化が行われない環境は多数あるため、コンパイラが特定のルーチンをインライン化する保証はありません。次のリストでは、インライン化が発生するまたは発生しない条件を示しています:
- インライン化は、実行時バインド メソッドの形式では発生しません。これには、仮想、動的、メッセージの各メソッドが含まれます。
- アセンブリ コードを含むルーチンは、インライン化されません。
- コンストラクタおよびデストラクタは、インライン化されません。
- main プログラム ブロック、ユニット初期化ブロック、ユニット終了処理ブロックは、インライン化されません。
- 使用される前に定義されていないルーチンは、インライン化されません。
- オープン配列パラメータをとるルーチンは、インライン化されません。
- パッケージ内のコードはインライン化されますが、パッケージの境界を超えるインライン化は発生しません。
- 循環的に依存するユニット間では、インライン化は行われません。これには、間接的な循環依存関係も含まれ、たとえば、unit A が unit B を使用、unit B が unit C を使用し、unit C がさらに unit A を使用する場合です。この例では、unit A のコンパイル時に、unit B や unit C からのコードが、unit A にインライン化されません。
- コンパイラは、あるユニットが循環的依存関係にある場合でも、インライン化されるコードが、循環関係の外部のユニットからのものである場合には、インライン化できます。先の例の場合、unit A がまた unit D も使用していた場合、unit D からのコードは A にインライン化される可能性があります。なぜなら、unit D は、循環依存関係に関与していないからです。
- ルーチンが interface セクションで定義されており、implementation セクションで定義されているシンボルにアクセスする場合、そのルーチンはインライン化されません。
- inline マークの付いたルーチンが、他のユニットからの外部シンボルを使用する場合、それらすべてのユニットを、uses 文に列記する必要があります。そうでなければ、ルーチンはインライン化されません。
- ユニット内で、インライン関数の本体は、その関数への呼び出しが行われる前に定義されていなければなりません。そうでなければ、関数の本体は、コンパイラがその呼び出しサイトへ到達したときにはまだ未知の状態になり、インラインに展開されません。
インライン化ルーチンの実装を変更すると、その関数を使用するすべてのユニットが再コンパイルされます。これは従来の再ビルド ルールとは異なり、従来は再ビルドは、ユニットの interface セクションにおける変更によってのみ発生していました。
{$INLINE} コンパイラ指令により、インライン化をさらに詳細に制御できます。{$INLINE} 指令は、呼び出し側だけでなく、ルーチンのインライン化される定義側でも使用することができます。以下は、可能な値とその意味についてです:
値 | 定義側の意味 | 呼び出し側の意味 |
---|---|---|
{$INLINE ON}(デフォルト) |
ルーチンは、inline 指令でタグ付けされている場合には、インライン可能としてコンパイルされる。 |
ルーチンは、可能であればインラインに展開される。 |
{$INLINE AUTO} |
{$INLINE ON} と同じ動作をするのに加え、inline でマークされていないルーチンも、それらのコード サイズが 32 バイト以下の場合には、インライン化される。 |
{$INLINE AUTO} は、ルーチンが呼び出し側で使用されている際に、それがインライン化されるかどうかについては、影響しない。 |
{$INLINE OFF} |
ルーチンは、たとえば inline のタグが付いていても、インライン可能とされない。 |
ルーチンは、インラインに展開されない。 |