コンパイル時にコードを生成したり抑制したい事は良くあります。
そのため多くの高級言語に条件付きコンパイルという仕組みがあります。
条件付きコンパイル
Object Pascal の条件付きコンパイルを使えば、下記のように特定のシンボルが定義されているかどうかによって、コードの生成を制御できます。
{$IFDEF SOMETHING}
procedure Foo(const AText: String);
begin
WriteLn(AText);
end;
{$ENDIF}
上記の例では SOMETHING
という条件シンボルが定義されている時だけ Foo という手続きのコードが生成されます。
ただ問題なのは、上記 Foo 手続きを呼び出す箇所も IFDEF~ENDIF で囲まないと行けないことです(定義されていない時、Foo の呼び出しは未定義エラーになるため)。
procedure Bar;
begin
{$IFDEF SOMETHING}
Foo('Hello, Foo');
{$ENDIF}
end;
呼び出し箇所が数カ所ならまだ何とかなりますが、何十箇所もあるととても面倒です。
呼び出し側に手を加えずコンパイルを通す方法
下記の様に IFDEF~ENDIF の範囲を手続き宣言の中に入れてしまえば、呼び出し側に手を加えずともコンパイルが通るようになります。
procedure Foo(const AText: String);
begin
{$IFDEF SOMETHING}
WriteLn(AText);
{$ENDIF}
end;
// 呼び出し側は IFDEF~ENDIF が要らない
procedure Bar;
begin
Foo('Hello, Foo');
end;
ですが、これだと Foo 本体と呼び出しコードは残ります(呼び出しに渡している文字列などの引数も残ります)。
シンボルが未定義の時は手続きの定義も呼び出すコードも一気に全部無くなればいいのに!
すべての呼び出しを無に帰す…
では、Foo の定義に手を加えて… inline 指令を付けてみます。
procedure Foo(const AText: String); inline; //← ここに inline !
begin
{$IFDEF SOMETHING}
WriteLn(AText);
{$ENDIF}
end;
inline 指令を付けるとコンパイラはこの関数をインライン展開しようとします。
さて、ここで SOMETHING
が定義されていない場合を考えます。
そうすると Foo は以下のようになります。
procedure Foo(const AText: String); inline;
begin
end;
つまり何もしない手続きになります。
この手続き Foo をコンパイルしようとすると、コンパイラは「inline 展開したくても展開する物が何も無いよ~😣」という状態になります。
そこで、コンパイラはこの手続きの存在を消去します。
呼び出しコードがあったとしたら、その呼び出しも消去します!
Log('Hello, Foo'); // ←このコードも跡形も無く消える!
「手続き (procedure)」と書きましたが「関数 (function)」は戻り値を利用するかも知れないため存在は抹消されません。
なお、メソッドであっても procedure であれば存在は抹消されます。
実験
では次のコードをコンパイルして、逆アセンブル結果を確認します。
program Project1;
{$DEFINE SOMETHING} // ←ここをコメントアウトすると SOMETHING を未定義化する
procedure Foo(const AText: String); inline;
begin
{$IFDEF SOMETHING}
WriteLn(AText);
{$ENDIF}
end;
begin
Foo('Hello, Foo');
end.
SOMETHING が定義済みの場合
↓コードにするとこんな状態になっているのが解ります。
begin
Foo('Hello, Foo'); // Foo が展開されて WriteLn 呼び出しに変わっている
end.
SOMETHING が未定義の場合
↓コードにするとこんな状態で、Foo や WriteLn の呼び出しコードが無くなっているのが解ります。
begin
end.
まとめ
条件付きコンパイルで procedure の存在を消しさりたい場合は inline を組み合わせると全部消えるので良いですよ。
procedure Something; inline;
begin
{$IFDEF SOMETHING}
// シンボルが定義されていない時は全部消え去る…
{$ENDIF}
end;