Skip to main content

This is documentation for Caché & Ensemble. See the InterSystems IRIS version of this content.Opens in a new tab

For information on migrating to InterSystems IRISOpens in a new tab, see Why Migrate to InterSystems IRIS?

メソッド・ジェネレータとトリガ・ジェネレータの定義

メソッド・ジェネレータは、独自の実行時コードを生成する特殊なメソッドです。同様に、トリガ・ジェネレータは、独自の実行時コードを生成するトリガです。この章では、これらについて説明します。また、以下の項目についても説明します。

この章では、主にメソッド・ジェネレータについて説明しますが、トリガ・ジェネレータの場合も詳細は同様のものになります。

メソッドの定義と呼び出し” の章および “永続クラスのその他のオプション” の章の “トリガの追加” も参照してください。

このドキュメントをオンラインで表示している場合は、このドキュメントの "序文" を使用すると、他のトピックをすばやく見つけることができます。

概要

Caché には、メソッド・ジェネレータを定義できるという強力な機能があります。メソッド・ジェネレータは、メソッドの実行時コードを生成する小さいプログラムであり、クラス・コンパイラによって呼び出されます。同様に、トリガ・ジェネレータもクラス・コンパイラによって呼び出され、トリガの実行時コードを生成します。

メソッド・ジェネレータは、Caché クラス・ライブラリ内で広範囲に使用されます。例えば、%PersistentOpens in a new tab クラスのメソッドの多くは、メソッド・ジェネレータとして実装されます。これにより、効率が少し下がる汎用コードの代わりに、カスタマイズされたストレージ・コードを各永続クラスに提供できます。多くの Caché データ型クラス・メソッドも、メソッド・ジェネレータとして実装されます。またこれにより、これらのクラスが使用される内容により、カスタム実装を行えるようになります。

メソッド・ジェネレータとトリガ・ジェネレータは、ユーザ独自のアプリケーション内で使用できます。メソッド・ジェネレータの一般的な使用方法は、1 つまたは複数の ユーティリティ・スーパークラスを定義し、使用するサブクラスの専用メソッドを提供することです。これらのユーティリティ・クラス内のメソッド・ジェネレータは、それらを使用するクラスの定義 (プロパティ、メソッド、パラメータ値など) に基づいて特別なコードを作成します。この技術の例としては、Caché ライブラリで提供されている %PopulateOpens in a new tab クラスと %XML.AdaptorOpens in a new tab クラスがあります。

基本

メソッド・ジェネレータは、Caché クラスのメソッドで、CodeMode キーワードに “objectgenerator” が設定されたものです。

Class MyApp.MyClass Extends %RegisteredObject
{
Method MyMethod() [ CodeMode = objectgenerator ]
    {
        Do %code.WriteLine(" Write """ _ %class.Name _ """")
        Do %code.WriteLine(" Quit")
        Quit $$$OK
    }
}

クラス MyApp.MyClass がコンパイルされるとき、最終的には以下の実装を持った MyMethod メソッドが生成されます。

 Write "MyApp.MyClass"
 Quit
Note:

前述の例の CodeMode の値は、“objectgenerator” です。これは、このメソッド・ジェネレータが推奨されているオブジェクト・ベースのメソッド・ジェネレータ・メカニズムを使用することを示しています。バージョン 5 より前の Caché では、別の推奨メカニズムが使用されており、CodeMode の値は “generator” でした。この古いメカニズムは互換性のために保持されているので、新規のアプリケーションでは “objectgenerator” を使用します。

トリガ・ジェネレータを定義することもできます。これを行うには、トリガの定義内で CodeMode = “objectgenerator” を使用します。トリガ内で使用できる値は、メソッド・ジェネレータ内で使用できる値と多少異なります。

ジェネレータの機能

メソッド・ジェネレータはクラスをコンパイルするときに有効になります。メソッド・ジェネレータの操作は簡単です。クラス定義がコンパイルされるとき、クラス・コンパイラは以下のことを行います。

  1. クラスの継承を解決します (継承されたすべてのメンバのリストを構築します)。

  2. (各メソッドの CodeMode キーワードから) メソッド・ジェネレータとして指定されているすべてのメソッドのリストを作成します。

  3. すべてのメソッド・ジェネレータからコードを集め、それを 1 つまたは複数の一時的なルーチンにコピーし、コンパイルします (これによってメソッド・ジェネレータ・コードを実行することができます)。

  4. コンパイルされるクラスの定義を示す、一時的な一連のオブジェクトを作成します。これらのオブジェクトは、メソッド・ジェネレータ・コードで使用できます。

  5. メソッド・ジェネレータのすべてのコードを実行します。

    コンパイラは、各メソッドに GenerateAfter キーワードが存在すればその値を参照して、それぞれのメソッドを呼び出す順序を調整します。このキーワードによって、メソッド間にコンパイラのタイミングの依存性がある場合の制御を提供します。

  6. 各メソッド・ジェネレータの結果を (コード行と他のメソッド・キーワードに対するすべての変更)、コンパイルされたクラス構造にコピーします (クラスの実際のコードを生成するのに使用)。

    元のメソッド・シグニチャ (引数と返りタイプ) は、すべてのメソッド・キーワード値と同様に、生成されたメソッドに使用されます。%IntegerOpens in a new tab の返りタイプを持たせるようにメソッド・ジェネレータを指定する場合、実際のメソッドは %IntegerOpens in a new tab の返りタイプを持ちます。

  7. メソッド・ジェネレータによって生成されたコードと、メソッド・ジェネレータではないメソッドで生成されたコードを結合させることによって、実行可能なクラスのコードを生成します。

詳細は、トリガ・ジェネレータについても同じです。

メソッド・ジェネレータで使用できる値

メソッド・ジェネレータの実装には、メソッド・ジェネレータ・コードが実行されるコンテキストを理解することが重要です。前のセクションで説明したように、クラス・コンパイラはメソッド・ジェネレータ・コードを、クラス継承が解決された後、クラスのコードを生成する前の時点で呼び出します。メソッド・ジェネレータ・コードが呼び出されるときに、クラス・コンパイラは以下の変数をメソッド・ジェネレータ・コードで使用できるようにします。

メソッド・ジェネレータで使用できる変数
変数 説明
%code %Stream.MethodGeneratorOpens in a new tab クラスのインスタンス。これは、メソッドのコードを出力するストリームです。
%class %Dictionary.ClassDefinitionOpens in a new tab クラスのインスタンス。クラスの元の定義を含みます。
%method %Dictionary.MethodDefinitionOpens in a new tab クラスのインスタンス。メソッドの元の定義を含みます。
%compiledclass %Dictionary.CompiledClassOpens in a new tab クラスのインスタンス。コンパイルされるクラスの、コンパイルされた定義を含みます。つまり、継承が解決された後のクラスに関する情報を含むということです (スーパークラスから継承されたものを含む、すべてのプロパティやメソッドのリストなど)。
%compiledmethod %Dictionary.CompiledMethodOpens in a new tab クラスのインスタンス。生成されるメソッドの、コンパイルされた定義を含みます。
%parameter パラメータ名によってインデックスされたすべてのクラス・パラメータの値を含む配列。例えば、%parameter("MYPARAM") は、現在のクラスに対する MYPARAM クラス・パラメータの値を含みます。この変数は、%class オブジェクト経由で使用できる、パラメータ定義のリストを使用する簡単な方法として提供されています。

トリガ・ジェネレータで使用できる値

メソッドと同様に、トリガもジェネレータとして定義できます。つまり、トリガの定義内で CodeMode = “objectgenerator” を使用できます。トリガ・ジェネレータで使用できる変数を以下に示します。

トリガ・ジェネレータで使用できる追加の変数
変数 説明
%code%class%compiledclass、および %parameter 前のセクションを参照してください。
%trigger %Dictionary.TriggerDefinitionOpens in a new tab クラスのインスタンス。トリガの元の定義を含みます。
%compiled%trigger %Dictionary.CompiledTriggerOpens in a new tab クラスのインスタンス。生成されるトリガのコンパイル済みの定義を含みます。

メソッド・ジェネレータの定義

メソッド・ジェネレータを定義するには、以下の手順を実行します。

  1. メソッドを定義し、その CodeMode キーワードを “objectgenerator” に設定します。

  2. メソッドのボディで、クラスがコンパイルされるときに、実際のメソッド・コードを生成するコードを記述します。このコードは、コードを出力するために %code オブジェクトを使用します。これは、使用できる他のオブジェクトをインプットとし、どのようなコードを生成するかを決定します。

以下は、クラスが属するすべてのプロパティの名前をリストにするメソッドを生成する、メソッド・ジェネレータの例です。

ClassMethod ListProperties() [ CodeMode = objectgenerator ]
{
    For i = 1:1:%compiledclass.Properties.Count() {
        Set prop = %compiledclass.Properties.GetAt(i).Name
        Do %code.WriteLine(" Write """ _ prop _ """,!")
    }
    Do %code.WriteLine(" Quit")
    Quit $$$OK
}

このジェネレータは、以下に類似した実装を持つメソッドを生成します。

 Write "Name",!
 Write "SSN",!
 Quit

メソッド・ジェネレータ・コードについて、以下のことに注意してください。

  1. %code オブジェクトの WriteLine メソッドを使用して、メソッドの実際の実装を含むストリームにコード行を書き込みます。(Write メソッドを使用して、行末の文字なしでテキストを出力することもできます)。

  2. 生成されたコードの各行の文頭には、スペース文字があります。ObjectScript が行の最初のスペースにはコマンドを許可しないので、これは必須です。使用しているメソッド・ジェネレータが Basic、または Java コードを作成している場合は必要ありません。

  3. 作成されたコード行は文字列に表示されるので、引用符文字を二重にすることで ("") エスケープすることに注意する必要があります。

  4. クラスのプロパティのリストを検索するには、%compiledclass オブジェクトを使用します。%class オブジェクトを使用することもできますが、その場合は継承されたプロパティはリストにせず、コンパイルされているクラス内で定義されたプロパティのみをリストにします。

  5. $$$OK のステータス・コードを返します。これは、メソッド・ジェネレータが正常に実行したことを示します。この返り値は、メソッドの実際の実装には関係ありません。

他の言語でのメソッド・ジェネレータ

ユーザは異なる言語のコードを生成できます。そのためには、%code オブジェクトの Language プロパティを設定し、目的の言語を指定してください。

既定では、生成コードの言語はコード・ジェネレータ・メソッドの記述に使用される言語 (Language キーワードで指定) と同じです。

メソッド・ジェネレータ内で CodeMode を指定する

既定で、メソッド・ジェネレータは “code” メソッドを作成します (つまり、生成されたメソッドに対する CodeMode キーワードが “code” に設定されるということ)。これは、%code オブジェクトの CodeMode プロパティを使用して変更できます。

例えば、以下のメソッド・ジェネレータは ObjectScript 式のメソッドを生成します。

Method Double(%val As %Integer) As %Integer [ CodeMode = objectgenerator ]
{
    Set %code.CodeMode = "expression"
    Do %code.WriteLine("%val * 2")
}

ジェネレータおよび INT コード

メソッド・ジェネレータとトリガ・ジェネレータでは、クラスのコンパイル後に対応する INT コードを表示すると非常に便利になる場合があります。"Caché プログラミング入門ガイド" の “習得する必要がある役立つスキル” の章で “INT コードの表示” を参照してください。

ジェネレータがカーネルで実装されるほど十分に単純である場合、対応して生成される .INT コードが存在しないことに注意してください。

ジェネレータ・メソッドとサブクラス

ここでは、ジェネレータ・メソッドを定義したクラスのサブクラスに含まれるジェネレータ・メソッドに特有のトピックについて説明します。

当然のことですが、サブクラスはスーパークラスのコンパイル後にコンパイルする必要があります。

サブクラス内のメソッド生成

ジェネレータ・メソッドを定義するクラスをサブクラス化するとき、Caché は、この章で前述したものと同じコンパイル・ルールを使用します。ただし、生成されるコードの外見がスーパークラスで生成されるコードと同じになる場合、Caché はサブクラスのメソッドをリコンパイルしません。このロジックでは、両方のクラスのインクルード・ファイルが同じかどうかについては考慮しません。インクルード・ファイルで定義されたマクロをメソッドが使用する場合、別のインクルード・ファイルをサブクラスで使用するとしても、Caché はサブクラスのメソッドをリコンパイルしません。ただし、どのクラスのジェネレータ・メソッドも強制的にリコンパイルすることは可能です。そのためには、そのメソッドの ForceGenerate メソッド・キーワードを指定します。このキーワードは、別のシナリオでも必要になることがあります。

スーパークラスのメソッドの呼び出し

サブクラスでは、ローカルに生成されたメソッドではなく、スーパークラスのために生成されたメソッドを使用することが必要になる場合があります。その場合は、$$$OK を返すだけのジェネレータ・メソッドをサブクラスで定義します。以下に例を示します。

ClassMethod Demo1() [ CodeMode = objectgenerator ]
{
    quit $$$OK
}

生成されたメソッドの削除

生成されたメソッドをサブクラスから削除して、そのクラスのメソッドの呼び出しを不可能にすることができます。そのためには、スーパークラスのジェネレータ・メソッドを定義するときに、現在のクラスの名前を検証して目的のシナリオでのみコードを生成するロジックを組み込みます。以下に例を示します。

ClassMethod Demo3() [ CodeMode = objectgenerator ]
{
    if %class.Name="RemovingMethod.ClassA" {
        Do %code.WriteLine(" Write !,""Hello from class: " _ %class.Name _ """")
    }
    quit $$$OK
}

このメソッドをサブクラスから呼び出そうとすると、<METHOD DOES NOT EXIST> エラーを受け取ることになります。

このロジックは、前のセクションで説明したものとはわずかに違う点に注意してください。特定のクラスにジェネレータ・メソッドが存在していても、メソッドが NULL 実装である場合は、スーパークラスのメソッドが代用されます (存在する場合)。ただし、特定のクラスのジェネレータ・メソッドが、特定のサブクラスにはコードを生成しない場合、そのサブクラスにメソッドは存在しなくなり、そのメソッドは呼び出しできなくなります。

FeedbackOpens in a new tab