ObjectScript マクロとマクロ・プリプロセッサ
ObjectScript コンパイラには、プリプロセッサが組み込まれており、ObjectScript にはプリプロセッサ指示文に対するサポートが含まれています。これらの指示文を使用すると、アプリケーション (メソッドとルーチンの両方) で使用するマクロを作成できます。これらのマクロは、コード内で単純なテキストを置き換える機能を提供します。Caché 自体にもさまざまな事前定義済みマクロが組み込まれています。これらについては、一連の Caché ドキュメント内の関連するコンテキストで説明されています。
この章は以下の 3 つの部分から構成されています。
-
マクロの使用 — マクロを定義して使用する方法を説明します。
-
システム・プリプロセッサ・コマンド・リファレンス — マクロの定義で使用できるプリプロセッサ指示文のリストを提供します。
-
システムにより提供されるマクロの使用 — Caché で使用できる定義済みマクロの使用法を説明し、さらにそれらのマクロのリストを提供します。
プリプロセッサは、ObjectScript パーサによって埋め込み SQL または埋め込み HTML コードが処理される前にマクロを拡張します。つまり、埋め込み HTML 内には、マクロのためのサポートがありません。プリプロセッサは、埋め込みまたは遅延いずれかのコンパイル・モードの埋め込み SQL をサポートしています。プリプロセッサは、ダイナミック SQL でのマクロ拡張は行いません。
ObjectScript パーサは、プリプロセッサ指示文を解析する前に、複数の行から成るコメントを削除します。そのため、/* と */ で囲んで記述した複数行コメントの中で指定したマクロ・プリプロセッサ指示文は、実行されません。
マクロの使用
このセクションでは、以下のトピックについて説明します。
カスタム・マクロの作成
マクロは、1 行の置換定義によって、ObjectScript の多様な機能をサポートできます。その基本形式は、#Define 指示文を使用して作成します。例えば以下のコードは、StringMacro というマクロを生成し、そのマクロを文字列 “Hello, World!” に置換します。
#Define StringMacro "Hello, World!"
ObjectScript では、以下のような “$$$” 構文を使用して、マクロを呼び出すことができます。
WRITE $$$StringMacro
この場合、文字列 “Hello, World!” を表示します。以下は、サンプルの全コードです。
#Define StringMacro "Hello, World!"
WRITE $$$StringMacro
以下はサポート対象の機能です。
-
上記の例のような文字列置換
-
数値置換
#Define NumberMacro 22
#Define 25M ##Expression(25*1000*1000)
通常、ObjectScript のマクロ定義では、文字列には引用符が必要ですが、数値には必要ありません。
-
変数置換
#Define VariableMacro Variable
マクロ名は、既に定義済みの変数名に置換されます。変数が未定義の場合、<UNDEFINED> エラーが発生します。
-
コマンドと引数の呼び出し
#Define CommandArgumentMacro(%Arg) WRITE %Arg,!
マクロ引数名は、上記の %Arg 引数のように、“%” 文字で始まる必要があります。このマクロは、%Arg 引数を使用する WRITE コマンドを呼び出します。
-
関数、式、演算子の使用
#Define FunctionExpressionOperatorMacro ($ZDate(+$Horolog))
ここでは、マクロ全体が式で、その値は、$ZDate 関数の返り値になります。$ZDate は、システム変数 $Horolog に格納されているシステム時刻に “+” 演算子を付けた演算の結果として生じる式を処理します。上記のように、式を括弧で囲むことをお勧めします。これにより、式が使用されている文とのやり取りが最小限に抑えられます。
-
他のマクロの参照
#Define ReferenceOtherMacroMacro WRITE $$$ReferencedMacro
このマクロは、WRITE コマンドへの引数として、他のマクロの式の値を使用します。
Note:1 つのマクロが別のマクロを参照する場合、参照先のマクロは、参照元のマクロの前にコンパイル済みのコード行に表示されている必要があります。
マクロの名前付け規約
-
最初の文字は、英数字またはパーセント記号 (%) でなくてはならない。
-
2 番目以降の文字は英数字でなくてはならない。マクロ名には、スペース、アンダースコア、ハイフンなどの記号文字を含めることはできません。
-
マクロ名では大文字と小文字が区別される。
-
マクロ名は最長 500 文字。
-
マクロ名には日本語の全角文字および半角かな文字を使用可能。詳細は、このドキュメントの “演算子と式” の章の "パターン・マッチング" のセクションにある “パターン・コード” 表を参照してください。
-
ISCname.inc ファイルがシステム用に予約されているため、マクロ名を ISC で開始することはできません。
マクロの空白規約
-
規約では、マクロ指示文をインデントさせずに、列 1 にて記述することになっています。しかし、マクロ指示文はインデントさせても構いません。
-
1 つ以上のスペースをマクロ指示文に続けることができます。マクロ内では、任意数のスペースをマクロ指示文、マクロ名、マクロ値の間に入れることができます。
-
マクロ指示文は単一行の文となります。マクロ指示文、マクロ名、およびマクロ値はすべて同一行で記述する必要があります。
-
#If および #ElseIf 指示文はテスト式の形をとります。テスト式にはスペースを含めることはできません。
-
#If 式、#ElseIf 式、#Else 指示文、および #EndIf 指示文はすべて、それら専用の行にて記述します。同一行にてこれらの指示文のいずれかに続くものはコメントと見なされるので、解析はされません。
マクロ・コメントとスタジオ・アシスト
マクロには、その定義の一部として渡すコメントを記述できます。/* と */、//、#;、;、および ;; で区切ったコメントは、すべて通常どおりに機能します。コメントの基本的な情報については、"Caché ObjectScript の使用法" の “構文規則” の章にある “コメント” を参照してください。
/// で始まるコメントには特別な機能があります。インクルード・ファイルにあるマクロでスタジオ・アシストを使用する場合、マクロの定義の直前の行に /// コメントを記述します。これにより、そのマクロの名前がスタジオ・アシストのポップアップに表示されます (現在のファイルにあるすべてのマクロがスタジオ・アシストのポップアップに表示されます)。例えば、以下のコードを #Include 指示文で参照する場合、最初のマクロの名前はスタジオ・アシストのポップアップに表示されますが、2 番目のマクロの名前は表示されません。
/// A macro that is visible with Studio Assist
#Define MyAssistMacro 100
//
// ...
//
// A macro that is not visible with Studio Assist
#Define MyOtherMacro -100
インクルード・ファイルでマクロを使用可能にする方法については、“外部マクロ (インクルード・ファイル) の参照” を参照してください。スタジオ・アシストに関する詳細は、"スタジオの使用法" の “スタジオ・オプションの設定” の章の “エディタ・オプション” のセクションを参照してください。
カスタム・マクロの保存
マクロは、呼び出されるファイルか、別々のインクルード・ファイルのいずれかに置くことができます (通常はインクルード・ファイルです)。マクロをクラス全体で使用可能にする (つまり、どのメンバからでも呼び出し可能にする) には、その定義をインクルード・ファイルに記述し、そのファイルをクラスでインクルードします。
マクロをインクルード・ファイルに記述するには、スタジオで以下の手順に従います。
-
[ファイル] メニューから [保存] あるいは [名前を付けて保存] を選択します。
-
[名前を付けて保存] ダイアログで、[ファイル・タイプ] フィールドから Include File (*.inc) 値を指定します。
Note:このフィールドの Macro Routine (*.mac) の値は、ObjectScript マクロの正しいファイル・タイプではありません。
-
ディレクトリとファイル名を入力し、マクロを保存します。
マクロの呼び出し
定義するメソッドまたはルーチンの中にマクロの定義が存在する場合、または定義するメソッドまたはルーチンで #Include 指示文を使用して外部ソースの定義を参照している場合に、マクロを呼び出すことができます。#Include の詳細は、外部マクロの参照 セクション、または #Include の参照セクションを参照してください。
ObjectScript コード内からマクロを呼び出す場合、“$$$” を名前の前に付けて呼び出します。したがって、MyMacro というマクロを定義している場合、$$$MyMacro という名前で呼び出します。このマクロ名の大文字と小文字は区別されます。
マクロはテキスト置換であることに注意してください。マクロが置換された後、結果として出力された文の構文が正確かどうかをチェックします。したがって、式を定義するマクロは、式を必要とするコンテキスト内で呼び出される必要があります。 また、コマンドとその引数に対するマクロは、ObjectScript の独立した行として置くことができます。
外部マクロ (インクルード・ファイル) の参照
マクロを別個のファイルに保存した場合、#include 指示文を使用すれば、そのマクロを利用できます。指示文は大文字と小文字が区別されないので、#Include で記述することができます。
クラス内またはルーチン開始部にマクロを含む場合、指示文は以下の形式になります。
#include MacroIncFile
ここで、MacroIncFile は、MacroIncFile.inc と呼ばれるマクロを含むインクルード・ファイルを参照しています。参照先ファイルが #include の引数となっている場合には、.inc 接尾語が参照先ファイルの名前に含まれないことに注意してください。
例えば、MyMacros.inc というファイルに 1 つ以上のマクロがある場合、以下の呼び出しに組み込むことができます。
#include MyMacros
YourMacros.inc というファイルに他のマクロがある場合、以下の呼び出しでそれらすべてを組み込むことができます。
#include MyMacros
#include YourMacros
ルーチンで #include を使用する場合、各インクルード・ファイルで別々の行に個別の #include 文を指定する必要があります。
クラス定義の開始部にインクルード・ファイルを組み込むには、構文を以下の形式とします。
include MyMacros
クラス定義の開始部に複数のインクルード・ファイルを組み込むには、構文を以下の形式とします。
include (MyMacros, YourMacros)
この include 構文には、先頭にシャープ記号がないことに注意してください。この構文は #include には使用できません。また、スタジオでのコンパイルにより、単語の形式が変換されるので、最初の文字が大文字化され、後続の文字は小文字となります。
ObjectScript コンパイラは、外部マクロを含めることを許可する /defines 修飾子を提供します。詳細は、"Caché ObjectScript リファレンス" の $SYSTEM 特殊変数の参照ページ内の "コンパイラ修飾子" のテーブルを参照してください。
また、#Include のリファレンス・セクションも参照してください。
システム・プリプロセッサ・コマンド・リファレンス
Caché では、以下のシステム・プリプロセッサ指示文がサポートされています。
マクロ・プリプロセッサ指示文には、大文字と小文字の区別がありません。このドキュメントでは、見やすくするために先頭の文字を大文字としていますが、必ずしもそうする必要はありません。
#;
#; プリプロセッサ指示文は、.int コードには表示されない単行のコメントを作成します。このコメントは .mac コードまたはインクルード・ファイルでのみ表示されます。#; は行の開始部 (1 列目) に表示されます。その行の残りの部分がコメントになります。以下の形式をとります。
#; Comment here...
ここで、“#;” の後にコメントが続きます。
#; は行全体をコメントにします。現在の行の残りの部分をコメントにする、##; プリプロセッサ指示文と比較します。
#Def1Arg
#Def1Arg プリプロセッサ指示文は、引数が 1 つのみのマクロを定義します。この引数にはコンマを記述できます。#Def1Arg は、#Define の特別なバージョンです。#Define は、引数の間区切り文字として引数内のコンマを扱うためです。以下の形式をとります。
#Def1Arg Macro(%Arg) Value
以下はその説明です。
-
Macro は、定義されるマクロの名前で、1 つのみ引数を取ります。有効なマクロ名は、英数字文字列です。
-
%Arg は、マクロの引数の名前です。マクロの引数を指定する変数の名前は、パーセント符号で始まる必要があります。
-
Value は、マクロの値です。これには、実行時に指定される %Arg の値の使用も含まれます。
#Def1Arg の行には、##; コメントを含むことができます。
マクロの定義に関する一般的な情報は、#Define のエントリを参照してください。
例えば、以下の MarxBros マクロは、引数としてマルクス兄弟の名前をコンマで区切ったリストを受け入れます。
#Def1Arg MarxBros(%MBNames) WRITE "%MBNames:",!,"The Marx Brothers!",!
// some movies have all four of them
$$$MarxBros(Groucho, Chico, Harpo, and Zeppo)
WRITE !
// some movies only have three of them
$$$MarxBros(Groucho, Chico, and Harpo)
ここで、MarxBros マクロは、%MBNames という引数を 1 つ取ります。この引数は、マルクス兄弟の名前をコンマで区切ったリストを受け入れます。
#Define
#Define プリプロセッサ指示文は、マクロを定義します。以下の形式をとります。
#Define Macro[(Args)] [Value]
以下はその説明です。
-
Macro は、定義されるマクロの名前です。有効なマクロ名は、英数字文字列です。
-
Args (オプション) は、受け入れる 1 つ以上の引数です。これらは、(arg1, arg2, ...) の形式をとります。マクロの引数を指定する各変数の名前は、パーセント符号で始まる必要があります。引数の値にコンマを含めることはできません。
-
Value (オプション) は、マクロに割り当てられる値です。この値には、任意の有効な ObjectScript コードを指定できます。リテラルなどの単純な値にすることも、式などの複雑な値にすることもできます。
マクロが値付きで定義されている場合、その値によって ObjectScript コード内のマクロが置き換えられます。値を持たないマクロを定義すると、コードでは他のプリプロセッサ指示文を使用して、そのマクロが存在するかどうかをテストし、その結果に応じてアクションを実行できます。
#Define の行には、##; コメントを含むことができます。
値付きのマクロ
値付きのマクロは、ObjectScript コード内で単純なテキストを置き換える機能を提供します。ObjectScript コンパイラがマクロの呼び出し ($$$MacroName の形式で) に遭遇するたびに、ObjectScript コードの現在の位置で、マクロに指定されている値が置き換えられます。マクロの値は、任意の有効な ObjectScript コードにできます。これには以下が含まれます。
-
文字列
-
数値
-
クラス・プロパティ
-
メソッド、関数、または他のコードの呼び出し
マクロの引数にコンマを含めることはできません。コンマが必要な場合は、#Def1Arg 指示文を使用できます。
以下は、さまざまな方法で使用されるマクロの定義の例です。
#Define Macro1 22
#Define Macro2 "Wilma"
#Define Macro3 x+y
#Define Macro4 $Length(x)
#Define Macro5 film.Title
#Define Macro6 +$h
#Define Macro7 SET x = 4
#Define Macro8 DO ##class(%Library.PopulateUtils).Name()
#Define Macro9 READ !,"Name: ",name WRITE !,"Nice to meet you, ",name,!
#Define Macro1A(%x) 22+%x
#Define Macro2A(%x) "Wilma" _ ": %x"
#Define Macro3A(%x) (x+y)*%x
#Define Macro4A(%x) $Length(x) + $Length(%x)
#Define Macro5A(%x) film.Title _ ": " _ film.%x
#Define Macro6A(%x) +$h - %x
#Define Macro7A(%x) SET x = 4+%x
#Define Macro8A(%x) DO ##class(%Library.PopulateUtils).Name(%x)
#Define Macro9A(%x) READ !,"Name: ",name WRITE !,"%x ",name,!
#Define Macro9B(%x,%y) READ !,"Name: ",name WRITE !,"%x %y",name,!
マクロの値の規則
マクロは任意の値を持つことができますが、マクロをリテラル式または完全な実行可能な行とすることが規則です。例えば、以下は有効な ObjectScript の構文です。
#Define Macro7 SET x =
ここで、マクロは以下のようなコードで呼び出される可能性があります。
$$$Macro7 22
プリプロセッサは、以下のコードに展開します。
SET x = 22
これは、明らかに有効な ObjectScript 構文ですが、このようにマクロを使用することはお勧めしません。
値なしのマクロ
マクロは、値なしに定義できます。この場合、マクロの存在 (または存在しないこと) は、特定の条件が存在することを指定します。次に、他のプリプロセッサ指示文を使用して、マクロが存在するかどうかをテストし、その結果に従ってアクションを実行できます。例えば、Unicode 実行可能プログラムまたは 8 ビット実行可能プログラムのいずれかとしてアプリケーションがコンパイルされる場合、以下のようなコードとなる可能性があります。
#Define Unicode
#IfDef Unicode
// perform actions here to compile a Unicode
// version of a program
#Else
// perform actions here to compile an 8-bit
// version of a program
#EndIf
JSON エスケープ円記号の制限
マクロは、\" エスケープ規則が含まれる JSON 文字列を受け入れません。マクロ値または引数は、リテラル円記号に JSON \" エスケープ・シーケンスを使用できません。このエスケープ・シーケンスは、マクロの本文またはマクロ拡張に渡される仮引数で使用することは認められていません。代わりに、\" エスケープを \u0022 に変換できます。この代わりの方法は、キー名と要素値の両方として使用される JSON 構文文字列に有効です。リテラル円記号が含まれる JSON 文字列が、JSON 配列または JSON オブジェクトの要素値として使用される場合は、別の方法として、\" が含まれる JSON 文字列を、同じ文字列値に評価する ObjectScript 文字列式に置き換えることができます。この文字列は括弧で囲む必要があります。
#Dim
#Dim プリプロセッサ指示文は、目的のタイプの変数を指定します。以下の形式をとります。
#Dim VariableName As DataTypeName
#Dim VariableName As DataTypeName = InitialValue
#Dim VariableName As List Of DataTypeName
#Dim VariableName As Array Of DataTypeName
各項目の内容は次のとおりです。
-
VariableName は、定義する変数です。
-
DataTypeName はVariableName のタイプです。
-
InitialValue は、VariableName について必要に応じて指定する値です (この構文はリストや配列には使用できません)。
#Else
#Else プリプロセッサ指示文は、一連のプリプロセッサ条件でフォールスルー・ケースの開始を指定します。この後に、#IfDef、#If、または #ElseIf を置くことができます。#EndIf がこの後に続きます。以下の形式をとります。
#Else
// subsequent indented lines for specified actions
#EndIf
#Else 指示文のキーワードは、1 行に単独で記述する必要があります。同一行にて #Else に続くものはコメントと見なされるので、解析はされません。
#If と組み合わせて #Else を使用する例は、#If 指示文の説明を参照してください。#EndIf と共に使用する例は、#EndIf 指示文の説明を参照してください。
リテラル値の 0 および 1 以外の引数を持つ #Else をメソッド・コードで使用すると、コンパイラでは、スーパークラスにあるそのメソッドを呼び出さず、サブクラスにコードを生成します。このコードの生成を回避するには、0 または 1 の値の条件をテストします。この値を指定したほうが簡潔なコードになり、パフォーマンスも最適化できます。
#ElseIf
#ElseIf プリプロセッサ指示文は、#If で始まる一連のプリプロセッサ条件で 2 番目のケースの開始を指定します。したがって、この後に、#If または別の #ElseIf を置くことができます。この後に、別の #ElseIf、#Else、または #EndIf を記述できます (#ElseIf 指示文は、#IfDef や #IfNDef では使用できません)。以下の形式をとります。
#ElseIf <expression>
// subsequent indented lines for specified actions
// next preprocessor directive
ここで、<expression> は有効な ObjectScript 式です。<expression> がゼロ以外の値の場合に True になります。
任意数のスペースにて、#ElseIf と <expression> を区切ることもできます。ただし、<expression> 内ではスペースが認められません。同一行にて <expression> に続くものはコメントと見なされるので、解析はされません。
使用例は、#If を参照してください。
#ElseIf には、#ElIf の代替の名前があります。2 つの名前は同一の動作をします。
#EndIf
#EndIf プリプロセッサ指示文は、一連のプリプロセッサ条件を終了します。この後に、#IfDef、#IfUnDef、#If、#ElseIf、および #Else を記述できます。以下の形式をとります。
// #IfDef, #If, or #Else specifying the beginning of a condition
// subsequent indented lines for specified actions
#EndIf
#EndIf 指示文のキーワードは、1 行に単独で記述する必要があります。同一行にて #EndIf に続くものはコメントと見なされるので、解析はされません。
使用例は、#If を参照してください。
#Execute
#Execute プリプロセッサ指示文は、コンパイル時に ObjectScript の行を実行します。以下の形式をとります。
#Execute <ObjectScript code>
ここで、#Execute に続く内容は、有効な ObjectScript コードです。このコードは、コンパイル時に値を持つ任意の変数またはプロパティを参照できます。また、コンパイル時に使用可能な任意のメソッドまたはルーチンを呼び出すことができます。ObjectScript コマンドと関数は、常に呼び出すことができます。
#Execute は、コードの実行に成功したかどうかを示す値を返しません。コードの実行結果を示す状態コードなどの情報はアプリケーション・コード側で確認する必要があります。この確認操作には、別の #Execute 指示文や他のコードを使用できます。
ローカル変数と共に #Execute を使用すると、予期しない結果が生じる場合があります。この理由は、以下のとおりです。
-
コンパイル時に使用される変数が、実行時に範囲外になる可能性があります。
-
複数のルーチンまたはメソッドで、参照時に変数が利用できない場合があります。この問題は、アプリケーション・プログラマがコンパイル順序を制御しないと、悪化する場合があります。
例えば、コンパイル時に曜日を決定し、以下のコードを使用して保存できます。
#Execute KILL ^DayOfWeek
#Execute SET ^DayOfWeek = $ZDate($H,12)
WRITE "Today is ",^DayOfWeek,".",!
この ^DayOfWeek グローバルは、コンパイルを実行するたびに更新されます。
#If
#If プリプロセッサ指示文は、条件テキストのブロックを開始します。この指示文は、ObjectScript 式で引数の真理値をテストして、引数の真理値が True の場合にコード・ブロックをコンパイルします。このコード・ブロックは、#Else、#ElseIf、または #EndIf のいずれかの指示文で終了します。
#If <expression>
// subsequent indented lines for specified actions
// next preprocessor directive
ここで、<expression> は有効な ObjectScript 式です。<expression> がゼロ以外の値の場合に True になります。
次に例を示します。
KILL ^MyColor, ^MyNumber
#Define ColorDay $ZDate($H,12)
#If $$$ColorDay="Monday"
SET ^MyColor = "Red"
SET ^MyNumber = 1
#ElseIf $$$ColorDay="Tuesday"
SET ^MyColor = "Orange"
SET ^MyNumber = 2
#ElseIf $$$ColorDay="Wednesday"
SET ^MyColor = "Yellow"
SET ^MyNumber = 3
#ElseIf $$$ColorDay="Thursday"
SET ^MyColor = "Green"
SET ^MyNumber = 4
#ElseIf $$$ColorDay="Friday"
SET ^MyColor = "Blue"
SET ^MyNumber = 5
#Else
SET ^MyColor = "Purple"
SET ^MyNumber = -1
#EndIf
WRITE ^MyColor, ", ", ^MyNumber
このコードは、コンパイル時に、ColorDay マクロの値を曜日の名前に設定します。#If で開始する条件文は、ColorDay の値を使用して、^MyColor 変数の値を設定する方法を決定します。このコードには、ColorDay (月曜日の後の各曜日に 1 つずつ) に適用できる複数の条件があります。これらをチェックするために、コードで #ElseIf 指示文を使用します。フォールスルー・ケースは、#Else 指示文に続くコードです。#EndIf は、条件文を終了します。
任意数のスペースにて、#If と <expression>. を区切ることもできます。ただし、<expression> 内ではスペースが認められません。同一行にて <expression> に続くものはコメントと見なされるので、解析はされません。
リテラル値の 0 および 1 以外の引数を持つ #If をメソッド・コードで使用すると、コンパイラでは、スーパークラスにあるそのメソッドを呼び出さず、サブクラスにコードを生成します。このコードの生成を回避するには、0 または 1 の値の条件をテストします。この値を指定したほうが簡潔なコードになり、パフォーマンスも最適化できます。
#IfDef
#IfDef プリプロセッサ指示文は、マクロが定義済みであることを実行の条件とする条件コード・ブロックの開始を指定します。以下の形式をとります。
#IfDef macro-name
ここで、macro-name は、先頭に “$$$” 文字が付かずに表示されます。同一行にて macro-name に続くものはコメントと見なされるので、解析はされません。
コードの実行は、マクロが定義済みであることを条件とします。この実行は、#Else 指示文に到達するか、#EndIf 指示文を終了するまで続きます。
#IfDef がチェックするのは、値が何かではなく、マクロが定義済みであるかどうかだけです。したがって、マクロが存在し、0 (ゼロ) の値を持つ場合、マクロが存在するので、#IfDef はそれでも条件コードを実行します。
また、#IfDef はマクロが存在するかどうかのみをチェックするので、他の可能性は 1 つのみ (マクロが定義されていない場合) です。これは #Else 指示文で処理します。#ElseIf 指示文は、#IfDef では使用できません。
例えば、以下のコードは、マクロの存在に基づいて、簡単な 2 つのメッセージのいずれかを表示します。
#Define Heads
#IfDef Heads
WRITE "The coin landed heads up.",!
#Else
WRITE "The coin landed tails up.",!
#EndIf
#IfNDef
#IfNDef プリプロセッサ指示文は、マクロが定義済みでないことを実行の条件とする条件コード・ブロックの開始を指定します。以下の形式をとります。
#IfNDef macro-name
ここで、macro-name は、先頭に “$$$” 文字が付かずに表示されます。同一行にて macro-name に続くものはコメントと見なされるので、解析はされません。
コードの実行は、マクロが定義されていないことを条件とします。この実行は、#Else 指示文に到達するか、#EndIf 指示文を終了するまで続きます。#ElseIf 指示文は、#IfNDef では使用できません。
#IfNDef には、#IfUnDef の代替の名前があります。2 つの名前は同一の動作をします。
例えば、以下のコードは、マクロが定義されていないことに基づいて、簡単なバイナリ・スイッチを提供します。
#Define Multicolor 256
#IfNDef Multicolor
SET NumberOfColors = 2
#Else
SET NumberOfColors = $$$Multicolor
#EndIf
WRITE "There are ",NumberOfColors," colors in use.",!
#Import
#Import プリプロセッサ指示文は、1 つまたは複数の検索するスキーマ名を指定して、埋め込み SQL クエリで未修飾のテーブル名のスキーマを指定します。単一のスキーマ名を指定できるほか、複数のスキーマ名をコンマ区切りリストで指定することもできます。すべてのスキーマが現在のネームスペースに存在している必要があります。以下の例に示すとおり、Sample.Person テーブルが検出されます。
#Import Voters,Sample
&sql(SELECT Name,DOB INTO :n,:date FROM Person)
WRITE "name: ",n," birthdate: ",date,!
WRITE "SQLCODE=",SQLCODE
#Import 指示文を指定すると、システム全体の既定のスキーマの検索が防止されます。システム全体の既定を検索する場合は、スキーマ名の #Import リスト内でそれを名前によって指定する必要があります。
#Import および #SQLCompile Path の両方は、未修飾のテーブル名を解決するために使用される 1 つまたは複数のスキーマ名を指定します。これらの 2 つの指示文の違いを以下に示します。
#Import 指示文は付加的です。2 番目の #Import を指定しても、前の #Import で指定されたスキーマ名のリストは無効化されません。#SQLCompile Path 指示文を指定すると、以前の #SQLCompile Path 指示文で指定されたパスは上書きされます。以前の #Import 指示文で指定されたスキーマ名は上書きされません。
#Import は、あいまいなテーブル名を検出します。#Import はすべての指定されたスキーマを検索します。#SQLCompile Path は、最初の一致が検出されるまで、スキーマの指定されたリストを左から右の順に検索します。そのため、#Import はあいまいなテーブル名を検出できます。#SQLCompile Path ではできません。例えば、#Import は Voters および Sample 中に Person の発生を必ず 1 つ検出します。このテーブル名を複数検出した場合、以下の SQLCODE -43 エラーが発生します。Voters と Sample のいずれにおいても Person テーブルが未検出の場合は、SQLCODE -30 エラーが発生します。
Caché は、#Import 指示文に存在しないスキーマ名を無視します。Caché は、#Import 指示文の重複したスキーマ名を無視します。
テーブル名が修飾済みの場合、#Import 指示文は適用されません。以下はその例です。
#Import Voters
#Import Bloggers
&sql(SELECT Name,DOB INTO :n,:date FROM Sample.Person)
WRITE "name: ",n," birthdate: ",date,!
WRITE "SQLCODE=",SQLCODE
この場合、Caché は Sample スキーマで Person テーブルを検索します。Voters スキーマと Bloggers スキーマでは検索されません。
1 つまたは複数のスキーマ名で #Import 指示文を指定した場合、Caché はシステム全体の既定のスキーマを検索しません。指定されたスキーマ内でテーブルが検出されなかった場合、SQLCODE -30 エラーが生成されます。#Import 指示文が指定されていない場合、Caché はシステム全体の既定のスキーマを検索します。
#Import は SQL DDL 文には影響しません。未修飾のテーブルまたはビューを作成する場合、システム全体の既定のスキーマで作成されます。これは、管理ポータルの [システム管理]、[構成]、[SQL およびオブジェクトの設定]、[一般SQL設定] (システム, 構成, 一般SQL設定) で定義されます。
#SQLCompile Path プリプロセッサ指示文と比較します。
#Include
#Include プリプロセッサ指示文は、プリプロセッサ指示文を記述した、指定の名前のファイルをロードします。以下の形式をとります。
#Include <filename>
filename は、.inc 接頭語を除く、インクルード・ファイルの名前です。インクルード・ファイルは通常、呼び出し元のファイルと同じディレクトリにあります。名前は大文字と小文字が区別されます。
例えば、マクロを含む OS.inc ヘッダ・ファイルがあるとします。
#Include OS
#IfDef Windows
WRITE "The operating system is not case-sensitive.",!
#Else
WRITE "The operating system is case-sensitive.",!
#EndIf
ストアド・プロシージャのコードで #Include を使用する場合は、以下のようにコロン文字 “:” を前に記述する必要があります。
CREATE PROCEDURE SPxx() Language OBJECTSCRIPT {
:#Include %occConstant
SET x=##Lit($$$NULLOREF)
}
クラスの開始部にてファイルを組み込む場合、指示文にはシャープ記号を含めません。したがって、単一ファイルでは以下のようになります。
Include MyMacros
複数ファイルでは以下のようになります。
Include (MyMacros, YourMacros)
OS.inc の内容には、以下の行のいずれかが含まれている場合があります。
#Define Windows
#Define UNIX
#NoShow
#NoShow プリプロセッサ指示文は、インクルード・ファイルに記述されているコメント・セクションを終了します。以下の形式をとります。
#NoShow
#NoShow は、#Show 指示文の後に続きます。コメント・セクションがファイルの最後まで続く場合でも、それぞれの #Show に対応する #NoShow を指定することを強くお勧めします。使用例は、#Show のエントリを参照してください。
#Show
#Show プリプロセッサ指示文は、インクルード・ファイルに記述されているコメント・セクションを開始します。既定で、インクルード・ファイル内のコメントは、呼び出し元のコードには表示されません。したがって、#Show から #NoShow の範囲外にあるインクルード・ファイルのコメントは、参照元コードには表示されません。
この指示文の形式は、以下のとおりです。
#Show
コメント・セクションがファイルの最後まで続く場合でも、それぞれの #Show に対応する #NoShow を指定することを強くお勧めします。
次の例で、#Include の例として挙げたファイル OS.inc には以下のコメントが記述されています。
#Show
// If compilation fails, check the file
// OS-errors.log for the statement "No valid OS."
#NoShow
// Valid values for the operating system are
// Windows or UNIX (and are case-sensitive).
ここで、最初の 2 行のコメント (“If compilation fails...” で始まる) は、インクルード・ファイルを含むコードに表示され、次の 2 行のコメント (“Valid values...” で始まる) は、インクルード・ファイル自体にのみ表示されます。
#SQLCompile Audit
#SQLCompile Audit プリプロセッサ指示文は、後続の埋め込み SQL 文を監査するかどうかを指定するブーリアンです。以下の形式をとります。
#SQLCompile Audit=value
ここで value は ON または OFF のいずれかです。
このマクロ・プリプロセッサ指示文に効力を発揮させるには、%System/%SQL/EmbeddedStatement システム監査イベントを有効にする必要があります。デフォルトでは、このシステム監査イベントは有効になっていません。
#SQLCompile Mode
#SQLCompile Mode プリプロセッサ指示文は、それ以降に記述されている埋め込み SQL 文のコンパイル・モードを指定します。以下の形式をとります。
#SQLCompile Mode=value
valueは以下のいずれかになります。
-
Embedded — ObjectScript コードおよび埋め込み SQL コードを実行前にコンパイルします。これが既定です。
-
Deferred — ObjectScript コードはコンパイルしますが、埋め込み SQL コードのコンパイルは実行時まで延期します。これにより、コンパイル時にはまだ存在していないテーブルを参照する SQL を含むルーチンをコンパイルできます。
Note:#SQLCompile Mode=Deferred は、似た名前を持つもののまったく異なる目的で使用される %SYSTEM.SQL.SetCompileModeDeferred() メソッドと混同しないようにしてください。
このモード指定以外の文は、遅延モードでも埋め込みモードでも同一です。遅延モードの文と埋め込みモードの文は、両方とも静的です。つまり、どちらのモードの文であっても、実行時に動的にアセンブルし、処理を行うために送信することはできません。動的なコードが必要な場合は、"Caché SQL の使用法" に説明があるダイナミック SQL を使用します。
遅延モードは、INSERT、UPDATE、および DELETE の各処理と、データ行を 1 つだけ返す SELECT 文で使用できます。カーソルを宣言して複数行のデータをフェッチする複数行の SELECT 文では、遅延 SQL を使用できません。これを試みると、#5663 コンパイル・エラーが生成されます。埋め込み SQL と同様に、遅延 SQL は、特権を確認しません。
遅延モードでは、テーブル、ユーザ定義関数、およびコンパイル時に存在しない他のエンティティを SQL で参照できます。
埋め込み SQL 文に無効な SQL 文 (SQL 構文エラーなど) が含まれている場合、マクロ・プロセッサは、コード "** SQL Statement Failed to Compile **" を生成し、ObjectScript コードのコンパイルを続行します。このように、無効な埋め込み SQL を含むメソッドでクラスをコンパイルすると、SQL エラーが報告されますが、メソッドは生成されます。このメソッドを実行すると、無効な SQL によるエラーが発生します。
埋め込み SQL では最適な SQL パフォーマンスが得られるので、可能な限りこのモードを使用するようにします。遅延 SQL は、通常、ダイナミック SQL より効率的です。遅延 SQL は、Transact-SQL のストアド・プロシージャまたは Informix SPL のストアド・プロシージャを Caché に変換するときに必要です。ストアド・プロシージャ用の Caché 変換ツールは、この機能をサポートしています。
詳細は、"Caché SQL の使用法" の "埋め込み SQL" の章を参照してください。
#SQLCompile Path
#SQLCompile Path プリプロセッサ指示文は、それ以降に記述されている埋め込み SQL クエリ文のスキーマ検索パスを指定します。以下の形式をとります。
#SQLCompile Path=schema1[,schema2[,...]]
schema は、現在のネームスペースで未修飾の SQL テーブル名または CALL プロシージャ名を検索する際に使用するスキーマ名です。単一のスキーマ名を指定できるほか、複数のスキーマ名をコンマ区切りリストで指定することもできます。複数のスキーマは指定の順序で検索されます。スキーマは指定された順序で検索されるため、あいまいなテーブル名は検出されません。#Import プリプロセッサ指示文も、スキーマ名のリストから未修飾の SQL テーブル名にスキーマ名を指定します。#Import はあいまいなテーブル名を検出します。
#SQLCompile Path を使用して、SQL クエリの未修飾テーブル名、および INSERT、UPDATE、および DELETE の各操作の未修飾テーブル名を解決できます。#SQLCompile Path を使用すると、SQL の CALL 文にある未修飾プロシージャ名を解決することもできます。CREATE TABLE やその他の CREATE 文、ALTER 文、DROP 文などの DDL 文の未修飾テーブル名には使用できません。
以下の例では、未修飾テーブル名 Person を Sample.PersonOpens in a new tab テーブルに解決します。最初に Cinema スキーマ を検索しますが、これには Person というテーブルが含まれていないので、続いて Sample スキーマを検索します。
#SQLCompile Path=Cinema,Sample
&sql(SELECT Name,Age
INTO :a,:b
FROM Person)
WRITE "Name is: ",a,!
WRITE "Age is: ",b
検索パスの項目としてスキーマ名を指定した上で、以下のキーワードを指定できます。
-
CURRENT_PATH : 前の #SQLCompile Path プリプロセッサ指示文で定義されている現在のスキーマ検索パスを指定します。これは、以下の例のように、既存のスキーマ検索パスの先頭や末尾にスキーマを付加するときに広く使用します。
#SQLCompile Path=schema_A,schema_B,schema_C #SQLCompile Path=CURRENT_PATH,schema_D
-
CURRENT_SCHEMA : 現在のスキーマのコンテナのクラス名を指定します。クラス・メソッドで #SQLCompile Path を定義している場合、CURRENT_SCHEMA は現在のクラス・パッケージにマップされたスキーマになります。.MAC ルーチンで #SQLCompile Path を定義している場合、CURRENT_SCHEMA は構成の既定のスキーマになります。
例えば、#SQLCompile Path=CURRENT_SCHEMA を指定するクラス・メソッドをクラス User.MyClass で定義している場合、User パッケージの既定のスキーマ名は SQLUser なので、CURRENT_SCHEMA は既定で SQLUser に解決されます。これは、さまざまなパッケージにスーパークラスおよびサブクラスがあり、未修飾テーブル名を使用する SQL クエリを持つスーパークラスでメソッドを定義する場合に便利です。CURRENT_SCHEMA を使用して、テーブル名をスーパークラスのスーパークラス・スキーマおよびサブクラスのサブクラス・スキーマに解決できます。CURRENT_SCHEMA の検索パスを設定していない場合、テーブル名は両方のクラスのスーパークラス・スキーマに解決されます。
トリガに #SQLCompile Path=CURRENT_SCHEMA を使用している場合は、スキーマ・コンテナ・クラス名が使用されます。例えば、クラス pkg1.myclass に #SQLCompile Path=CURRENT_SCHEMA を指定するトリガがあり、クラス pkg2.myclass が pkg1.myclass の拡張である場合、pkg2.myclass クラスをコンパイルするときに、このトリガの SQL 文に記述された非修飾テーブル名はパッケージ pkg2 のスキーマに解決されます。
-
DEFAULT_SCHEMA : システムが定義した既定のスキーマを指定します。
スキーマ検索パスを指定している場合、SQL クエリ・プロセッサで未修飾の名前を解決するとき、その指定のスキーマ検索パスが最初に使用されます。指定されたテーブルまたはプロシージャがそのスキーマに見つからない場合は、#Import で指定されている既定のスキーマまたは構成されたシステム全体の既定の SQL スキーマ名を調べます。指定されたテーブルがこれらの場所のどこにも見つからない場合は、SQLCODE -30 エラーが生成されます。
#SQLCompile Path は、Embedded または Deferred を指定した #SQLCompile Mode 値と組み合わせて使用できます。詳細は、"Caché SQL の使用法" の "埋め込み SQL" の章を参照してください。
スキーマ検索パスのスコープは、それが定義されているルーチンまたはメソッドです。クラス・メソッドでスキーマ・パスを指定している場合、これはそのクラス・メソッドのみに適用され、同じクラスにある他のメソッドには適用されません。.MAC ルーチンで指定したスキーマ検索パスの場合は、その指定した位置から別の #SQLCompile Path 指示文が現れるまで、またはルーチンの最後に達するまでがスコープとなります。
スキーマは現在のネームスペースに対して定義されます。
#Import プリプロセッサ指示文と比較します。
#SQLCompile Select
#SQLCompile Select プリプロセッサ指示文は、それ以降に記述されている埋め込み SQL 文のデータ形式モードを指定します。以下の形式をとります。
#SQLCompile Select=value
valueは以下のいずれかになります。
-
Display — 画面に合わせてデータ形式を設定し、出力します。
-
Logical — データをメモリ内の形式のままにします。
-
ODBC — ODBC または JDBC 経由での表示に合わせてデータ形式を設定します。
-
Runtime — 表示形式 (DISPLAY または ODBC) から実行時間選択モード値に基づく論理格納形式への入力データ値の自動変換がサポートされています。出力値は現在のモードへ変換されます。
実行時間選択モード値の取得には、%SYSTEM.SQLOpens in a new tab クラスの GetSelectModeOpens in a new tab メソッドを使用できます。実行時間選択モード値の設定には、%SYSTEM.SQLOpens in a new tab クラスの SetSelectModeOpens in a new tab メソッドを使用できます。
-
Text — Display の同義語です。
-
FDBMS — 埋め込み SQLが FDBMS と同じデータをフォーマットできるようにします。
このマクロの値によって、SELECT 出力ホスト変数の埋め込み SQL 出力データ形式、および埋め込み SQL INSERT、UPDATE、および SELECT 入力ホスト変数の必須の入力データ形式が決まります。詳細は、"Caché SQL の使用法" の “埋め込み SQL の使用法” の章の "マクロ・プリプロセッサ" を参照してください。
以下の埋め込み SQL の例は、さまざまなコンパイル・モードを使用して、Sample.PersonOpens in a new tab テーブルにある Name (文字列フィールド)、DOB (日付フィールド)、および Home (リスト・フィールド) の 3 つのフィールドを返します。
#SQLCOMPILE SELECT=Logical
&sql(SELECT Name,DOB,Home
INTO :n,:d,:h
FROM Sample.Person)
WRITE "name is: ",n,!
WRITE "birthdate is: ",d,!
WRITE "home is: ",h
#SQLCOMPILE SELECT=Display
&sql(SELECT Name,DOB,Home
INTO :n,:d,:h
FROM Sample.Person)
WRITE "name is: ",n,!
WRITE "birthdate is: ",d,!
WRITE "home is: ",h
#SQLCOMPILE SELECT=ODBC
&sql(SELECT Name,DOB,Home
INTO :n,:d,:h
FROM Sample.Person)
WRITE "name is: ",n,!
WRITE "birthdate is: ",d,!
WRITE "home is: ",h
#SQLCOMPILE SELECT=Runtime
&sql(SELECT Name,DOB,Home
INTO :n,:d,:h
FROM Sample.Person)
WRITE "name is: ",n,!
WRITE "birthdate is: ",d,!
WRITE "home is: ",h
#UnDef
#UnDef プリプロセッサ指示文は、既に定義されているマクロの定義を削除します。以下の形式をとります。
#UnDef macro-name
ここで、macro-name は、既に定義されているマクロです。
#UnDef は、#Define または #Def1Arg の呼び出しの後に続きます。これは、#IfDef および関連付けられたプリプロセッサ指示文 (#Else、#EndIf、および #IfNDef) と連動します。
以下の例では、マクロが定義済みであること、および未定義であることを条件とするコードを示します。
#Define TheSpecialPart
#IfDef TheSpecialPart
WRITE "We're in the special part of the program.",!
#EndIf
//
// code here...
//
#UnDef TheSpecialPart
#IfDef TheSpecialPart
WRITE "We're in the special part of the program.",!
#Else
WRITE "We're no longer in the special part of the program.",!
#EndIf
#IfNDef TheSpecialPart
WRITE "We're still outside the special part of the program.",!
#Else
WRITE "We're back inside the special part of the program.",!
#EndIf
これに対する .int コードは以下のとおりです。
WRITE "We're in the special part of the program.",!
//
// code here...
//
WRITE "We're no longer in the special part of the program.",!
WRITE "We're still outside the special part of the program.",!
##;
##; プリプロセッサ指示文は、現在の行の残りの部分を .int コードには表示されないコメントにします。このコメントは .mac コードまたはインクルード・ファイルでのみ表示されます。##; は ObjectScript コード行もしくは SQL コード行のあらゆる位置で使用できます。その行の残りの部分がコメントになります。以下の形式をとります。
WRITE "Invoking Embedded SQL",! ##; Comment One
&sql(SELECT Name INTO :a ##; Comment Two
FROM Sample.Person)
##; Comment Three
WRITE "The SQL error code is ",SQLCODE,!
WRITE "The name is ",a
ここで、“##;” の後にコメントが続きます。
##; は、埋め込み HTML または埋め込み JavaScript の評価の前に評価されます。
列 1 に表示され行全体をコメントにする、#; と比較します。##; は、現在の行の残りの部分をコメントにします。##; が行の最初の列に表示された場合は、#; プリプロセッサ指示文と機能的に同じになります。
##; コメント文字は、#Define、#Def1Arg、または ##Continue プリプロセッサ指示文と同一行に記すことができます。
##Continue
##Continue プリプロセッサ指示文は、次の行にマクロ定義を継続して複数の行でそのマクロ定義をサポートします。マクロ定義の行の最後に現れて、以下の形式で継続していることを示します。
#Define <beginning of macro definition> ##Continue
<continuation of macro definition>
マクロ定義では、複数の ##Continue 指示文を使用できます。
以下はその例です。
#Define Multiline(%a,%b,%c) ##Continue
SET line1="%a" ##Continue
SET line2="%b" ##Continue
SET line3="%c"
$$$Multiline(Scarecrow,Tin Woodman,Lion)
WRITE "Here is line 1: ",line1,!
WRITE "Here is line 2: ",line2,!
WRITE "Here is line 3: ",line3,!
ここで、定義されるマクロは、3 つの引数を受け入れます。マクロのコードは、マクロに引数として渡される値に等しい 3 つのローカル変数を設定します。WRITE コマンドは、これらの値を表示します。
##Continue の行には、##; コメントを含むことができます。
##Expression
##Expression プリプロセッサ指示文は、コンパイル時に ObjectScript 式を評価します。以下の形式をとります。
##Expression(content)
ここで、content は、引用符で囲まれた文字列または他のプリプロセッサ指示文を含まない、有効な ObjectScript コードです (下記の入れ子にされた ##Expression を除く)。
プリプロセッサは、コンパイル時に指示文の引数の値を評価し、ObjectScript .int コードの評価で ##Expression(content) を置き換えます。##Expression 内で変数を引用符で囲んで表示する必要があります。そうしない場合、変数はコンパイル時に評価されます。##Expression は、埋め込み HTML または埋め込み JavaScript の評価の前に評価されます。
以下の例では、簡単な式をいくつか示します。
#Define NumFunc ##Expression(1+2*3)
#Define StringFunc ##Expression("""This is"_" a concatenated string""")
WRITE $$$NumFunc,!
WRITE $$$StringFunc,!
以下の例は、現在のルーチンのコンパイル・タイムスタンプを含む式を定義しています。
#Define CompTS ##Expression("""Compiled: " _ $ZDATETIME($HOROLOG) _ """,!")
WRITE $$$CompTS
ここで、##Expression の引数は、3 箇所で解析され、“_” 演算子を使用して連結されます。
-
最初の文字列、"""Compiled: "。これは、二重引用符で区切られています。その中で、二重引用符のペアは、1 つの二重引用符が評価の後に表示されるように指定しています。
-
値、$ZDATETIME($HOROLOG)。$ZDATETIME 関数により変換および書式設定された、コンパイル時の $HOROLOG 特殊変数の値。
-
最後の文字列、""",!"。これも、二重引用符で区切られています。その中に、二重引用符が 1 組みあります (評価後、1 つの二重引用符となります)。定義される値は WRITE コマンドに渡されるので、WRITE コマンドに改行が含まれるように、最後の文字列には ,! が含まれています。
ルーチンの中間 (.int) コードには、以下のような行が含まれることになります。
WRITE "Compiled: 05/19/2014 07:49:30",!
##Expression とリテラル文字列
##Expression で解析しても、リテラル文字列は認識されません。引用符内で文字を括弧で囲っても、特別に扱われません。例えば、以下の指示文を考えます。
#Define MyMacro ##Expression(^abc(")",1))
引用符が付いた右側の括弧は、引数を指定するための閉じ括弧であるかのように扱われます。
##Expression の入れ子
Caché では、入れ子にした ##Expression を使用できます。他の ##Expression に展開するマクロを含む ##Expression は、その展開が ObjectScript レベルで評価可能 (つまり、プリプロセッサ指示文がない) であって、ObjectScript 変数に格納可能であれば、定義可能です。入れ子にした ##Expression では、##Expression 式を持つマクロが最初に展開された後、入れ子にした ##Expression が展開されます。##Expression 内で、他の ## プリプロセッサ関数を入れ子にすることはできません。
##Expression、サブクラス、および ##SafeExpression
メソッドが ##Expression を含んでいる場合、クラスのコンパイル時にこれが検出されます。コンパイラでは ##Expression の内容を解析しないため、この ##Expression によってサブクラスで本来とは異なるコードが生成される可能性があります。これを回避するために、Caché では、サブクラスごとにコンパイラでメソッド・コードを再生成するようにしています。例えば、##Expression(%classname) では現在のクラス名を挿入しますが、サブクラスのコンパイル時には、サブクラスのクラス名が挿入されている必要があります。Caché では、メソッドを強制的にサブクラス内で再生成することで、この状態が確実に発生するようになっています。
サブクラス内のコードが別のものにならないことがわかっている場合は、サブクラスごとのメソッドの再生成を回避してもかまいません。そのためには、##SafeExpression プリプロセッサ指示文を ##Expression に置き換えます。それ以外の場合、これら 2 つのプリプロセッサ指示文は同じ機能となります。
##Expression の動作
以下に示すように ##Expression への引数には、ObjectScript XECUTE コマンドで値を設定します。
SET value="Set value="_expression XECUTE value
ここで、expression は、value の値を決定する ObjectScript 式です。この式には、マクロや ##Expression 指示文は使用できません。
ただし、XECUTE value の結果には、マクロや別の ##Expression を使用できます。この例のように、ObjectScript プリプロセッサは、これらのどれであってもさらに展開を進めます。
ルーチン A.mac に次の式が記述されているとします。
#Define BB ##Expression(10_"_"_$$aa^B())
SET CC = $$$BB
QUIT
また、ルーチン B.mac には次の式が記述されているとします。
aa()
QUIT "##Expression(10+10+10)"
この結果、A.int の内容は次のようになります。
SET CC = 10_30
QUIT
##Function
##Function プリプロセッサ指示文は、コンパイル時に ObjectScript 関数を評価します。以下の形式をとります。
##Function(content)
ここで、content は ObjectScript 関数で、ユーザ定義にできます。##Function は、関数からの返り値で ##Function(content) を置き換えます。
例えば、GetCurrentTime.mac ファイルに関数があるとします。
Tag1()
KILL ^x
SET ^x = """" _ $Horolog _ """"
QUIT ^x
以下に示すように、ShowTimeStamps.mac と呼ばれる別個のルーチンでこのコードを呼び出すことができます。
Tag2
#Define CompiletimeTimeStamp ##function($$Tag1^GetCurrentTime())
#Define RuntimeTimeStamp $$Tag1^GetCurrentTime()
SET x=$$$CompiletimeTimeStamp
WRITE x,!
SET y=$$$RuntimeTimeStamp
WRITE y,!
ターミナルでの出力は、以下のようになります。
USER>d ^ShowTimeStamps
60569,43570
"60569,53807"
USER>
ここで、出力の最初の行は、コンパイル時の $Horolog の値で、2 行目は、実行時の $Horolog の値です。(出力の最初の行は引用符で囲まれておらず、2 行目は囲まれています。これは、x がその値の代わりに引用符で囲まれた文字列を使用するためです。したがって、引用符はターミナルに表示されませんが、y は引用符に囲まれた文字列を直接ターミナルに出力します。)
##Function 呼び出しの返り値が、呼び出しのコンテキストで意味と構文の両方が理解できることを確認するのは、アプリケーション・プログラマの責任です。
##Lit
##Lit プリプロセッサ指示文は、リテラル形式でその引数の内容を保持します。
##Lit(content)
ここで、content は、有効な ObjectScript 式である文字列です。##Lit プリプロセッサ指示文は、受け取る文字列を評価せずに、リテラル・テキストとして扱うことを確認します。
以下はコードの例です。
#Define Macro1 "Row 1 Value"
#Define Macro2 "Row 2 Value"
##Lit(;;) Column 1 Header ##Lit(;) Column 2 Header
##Lit(;;) Row 1 Column 1 ##Lit(;) $$$Macro1
##Lit(;;) Row 2 Column 1 ##Lit(;) $$$Macro2
.int コード内のテーブルを生成する一連の行を作成します。
;; Column 1 Header ; Column 2 Header
;; Row 1 Column 1 ; "Row 1 Value"
;; Row 2 Column 1 ; "Row 2 Value"
##Lit 指示文を使用することにより、.int コードの中でマクロが評価され、セミコロンで区切って記述されます。
##Quote
##Quote プリプロセッサ指示文は、単一の引数を取り、その引数を引用符付きで返します。その引数に既に引用符文字が付いている場合、引用符文字を二重にすることによってこれらの引用符文字をエスケープします。以下の形式をとります。
##Quote(value)
value は、引用符付きの文字列に変換されるリテラルです。value では、括弧文字または引用符文字はペアで使用する必要があります。例えば、以下は有効な value です。
#Define qtest ##Quote(He said "Yes" after much debate)
ZZWRITE $$$qtest
これは、"He said ""Yes"" after much debate" を返します。##Quote(This (") is a quote character) は有効な value ではありません。
value 文字列内の括弧はペアになっている必要があります。以下は有効な value です。
#Define qtest2 ##Quote(After (a lot of) debate)
ZZWRITE $$$qtest2
以下の例は、##Quote の使用法を示しています。
#Define AssertEquals(%e1,%e2) DO AssertEquals(%e1,%e2,##Quote(%e1)_" == "_##Quote(%e2))
Main ;
SET a="abstract"
WRITE "Test 1:",!
$$$AssertEquals(a,"abstract")
WRITE "Test 2:",!
$$$AssertEquals(a_"","abstract")
WRITE "Test 3:",!
$$$AssertEquals("abstract","abstract")
WRITE "All done"
QUIT
AssertEquals(e1,e2,desc) ;
WRITE desc_" is "_$SELECT(e1=e2:"true",1:"false"),!
QUIT
##SQL
##SQL プリプロセッサ指示文は、コンパイル時に指定された SQL 文を呼び出します。以下の形式をとります。
##SQL(SQL-statement)
ここで SQL-statement は有効な SQL 文です。##SQL プリプロセッサ指示文は、&SQL 指示文に類似しています。##SQL() はコンパイル時に文を呼び出し、&SQL() は実行時に文を呼び出します。
##SQL 指示文に無効な SQL (構文エラーなど) が含まれている場合、または存在しないテーブルや列を参照している場合、このマクロ・プリプロセッサがコンパイル・エラーを生成します。
例えば、以下のコードはコンパイル時にまずクエリを実行し、実行時に再度クエリを実行します。
##sql(SELECT COUNT(*) INTO :count1 FROM Sample.Person)
&sql(SELECT COUNT(*) INTO :count2 FROM Sample.Person)
WRITE "Number of instances of Sample.Person at compile time: ",count1,!
WRITE "Number of instances of Sample.Person at runtime: ",count2,!
##Unique
##Unique プリプロセッサ指示文は、コンパイル時または実行時に使用するマクロ定義内に、新規で一意のローカル変数を作成します。この指示文は、#Define または #Def1Arg 呼び出しの一部としてのみ使用できます。以下の形式をとります。
##Unique(new)
##Unique(old)
ここで、new は、新しい一意の変数の作成を指定し、old は、その同じ変数への参照を指定します。
SET ##Unique(new) によって作成される変数は %mmmu1 という名前のローカル変数です。後続の SET ##Unique(new) では、%mmmu2、%mmmu3、以降同様の名前のローカル変数が作成されます。これらのローカル変数は、すべての % ローカル変数と同じ有効範囲ルール (% 変数は常にパブリック変数です) に従います。これらは、あらゆるローカル変数と同様に、ZWRITE を使用して表示でき、引数なしの KILL を使用して削除できます。
ユーザ・コードは他の ObjectScript 変数を参照できるように、##Unique(old) 変数を参照できます。##Unique(old) 構文は、作成された変数を参照するために、何度でも使用できます。
その後 ##Unique(new) の呼び出すと、新しい変数が作成されます。##Unique(new) を再度呼び出した後に ##Unique(old) を呼び出すと、次に作成された変数が参照されます。
例えば、以下のコードは、##Unique(new) と ##Unique(old) を使用して、2 つの変数の値を交換します。
#Define Switch(%a,%b) SET ##Unique(new)=%a, %a=%b, %b=##Unique(old)
READ "First variable value? ",first,!
READ "Second variable value? ",second,!
$$$Switch(first,second)
WRITE "The first value is now ",first," and the second is now ",second,!
これらの変数の一意性を維持するには、以下のようにします。
-
#Define または #Def1Arg プリプロセッサ指示文の外側に ##Unique(new) を設定しようとしない。
-
メソッド内またはプロシージャ内のプリプロセッサ指示文で ##Unique(new) を設定しない。これらはメソッド (%mmmu1) に固有の変数名を生成しますが、これは % 変数であるため、グローバルに有効範囲が設定されます。##Unique(new) を設定する別のメソッドを呼び出しても %mmmu1 が作成され、最初のメソッドで作成された変数を上書きします。
-
%mmmu1 変数を直接設定しない。Caché は、システムで使用するためにすべての % 変数 (%z 変数および %Z 変数を除く) を予約しています。これらをユーザ・コードで設定しないでください。
システムにより提供されるマクロの使用
このセクションでは、Caché で使用できる定義済みマクロのいくつかに関連するトピックについて説明します。トピックは以下のとおりです。
システムにより提供されるマクロをアクセス可能にする方法
これらのマクロは、%RegisteredObjectOpens in a new tab のすべてのサブクラスで使用可能です。%RegisteredObjectOpens in a new tab の拡張でないルーチンまたはクラスの中でこれらのマクロを使用可能にするには、以下の適切なファイルをインクルードします。
-
状態関連のマクロの場合は、%occStatus.inc をインクルードします。
-
メッセージ関連のマクロの場合は、%occStatus.inc をインクルードします。
どのインクルード・ファイルが必要になるかについては、以下に示す各マクロを参照してください。
そのような文の構文は以下のとおりです。
#Include %occStatus
これらのインクルード・ファイルでは、大文字と小文字が区別されます。外部で定義されたマクロの使用法の詳細は、セクション “外部マクロ (インクルード・ファイル) の参照” を参照してください。
システムにより提供されるマクロのリファレンス
マクロ名では大文字と小文字が区別される。Caché では、以下のマクロが提供されます。
ADDSC マクロは、%StatusOpens in a new tab コード (sc2) を既存の %StatusOpens in a new tab コード (sc1) に付加します。このマクロには %occStatus.inc が必要です。
EMBEDSC マクロは、%StatusOpens in a new tab コード (sc2) を既存の %StatusOpens in a new tab コード (sc1) に埋め込みます。このマクロには %occStatus.inc が必要です。
ERROR マクロは、オブジェクト・エラー・コード (errorcode) を使用して、%StatusOpens in a new tab オブジェクトを作成します。オブジェクト・エラー・コードの関連テキストは、%1、%2 などの形式の引数を数個受け付けます。その後、ERROR はそれらの引数を、errorcode (arg1、arg2 など) に続くマクロ引数に置換します。その際には、この追加引数の順序に基づいて置換されます。このマクロには %occStatus.inc が必要です。
システム定義のエラー・コードのリストは、"Caché エラー・リファレンス" の “一般的なエラー・メッセージ” を参照してください。
FormatMessage マクロを使用すると、同じマクロ呼び出し内で、メッセージ・ディクショナリからテキストを取得し、メッセージ引数をテキストに置き換えることができます。これは %StringOpens in a new tab を返します。
引数 | 説明 |
---|---|
language | RFC1766Opens in a new tab 言語コード。CSP コンテキストでは、%response.Language を指定して既定のロケールを使用できます。 |
domain | メッセージ・ドメイン。CSP コンテキストでは、%response.Domain を指定できます。 |
id | メッセージ ID。 |
default | language、domain、および id で識別されるメッセージが見つからない場合に使用する文字列。 |
arg1、arg2 など | メッセージ引数の置換テキスト。これらはすべてオプションなので、メッセージに引数がない場合でも $$$FormatMessage を使用できます。 |
メッセージ・ディクショナリの詳細は、"Caché Server Pages (CSP) の使用法" の “CSP アプリケーションにおけるテキストのローカライズ” を参照してください。
このマクロには %occMessages.inc が必要です。
%Library.MessageDictionaryOpens in a new tab の FormatMessage() インスタンス・メソッドも参照してください。
FormatText マクロは入力テキスト・メッセージ (text) を受け付けます。このテキスト・メッセージには %1、%2 などの形式で引数を含めることができます。FormatText はそれらの引数を、text 引数 (arg1、arg2 など) に続くマクロ引数に置換します。その際には、この追加引数の順序に基づいて置換されます。その後、結果の文字列を返します。 このマクロには %occMessages.inc が必要です。
FormatTextHTML マクロは入力テキスト・メッセージ (text) を受け付けます。このテキスト・メッセージには %1、%2 などの形式で引数を含めることができます。FormatTextHTML はそれらの引数を、text 引数 (arg1、arg2 など) に続くマクロ引数に置換します。その際には、この追加引数の順序に基づいて置換されます。またこのマクロは、その結果に HTML エスケープを適用します。その後、結果の文字列を返します。 このマクロには %occMessages.inc が必要です。
FormatTextJS マクロは入力テキスト・メッセージ (text) を受け付けます。このテキスト・メッセージには %1、%2 などの形式で引数を含めることができます。FormatTextJS はそれらの引数を、text 引数 (arg1、arg2 など) に続くマクロ引数に置換します。その際には、この追加引数の順序に基づいて置換されます。またこのマクロは、その結果に JavaScript エスケープを適用します。その後、結果の文字列を返します。 このマクロには %occMessages.inc が必要です。
GETERRORCODE マクロは、指定の %StatusOpens in a new tab コード (sc) のエラー・コード値を返します。 このマクロには %occStatus.inc が必要です。
ISERR マクロは、指定の %StatusOpens in a new tab コード (sc) がエラー・コードの場合に True を返します。それ以外は False を返します。このマクロには %occStatus.inc が必要です。
ISOK マクロは、指定の %StatusOpens in a new tab コード (sc) が正常に完了した場合に True を返します。それ以外は False を返します。このマクロには %occStatus.inc が必要です。
OK マクロは、正常終了を表す %StatusOpens in a new tab コードを作成します。このマクロには %occStatus.inc が必要です。
Text マクロは、ローカライズに使用されます。Text マクロは、コンパイル時に新しいメッセージを生成し、実行時にはそのメッセージを取得するコードを生成します。 このマクロには %occMessages.inc が必要です。
このマクロの詳細は、"Caché Server Pages (CSP) の使用法" の “CSP アプリケーションにおけるテキストのローカライズ” の章にある “コンパイル時および実行時の $$$Text マクロ” を参照してください。
TextHTML マクロは、ローカライズに使用されます。Text マクロと同じ処理を実行し、その結果に HTML エスケープを適用します。その後、結果の文字列を返します。 このマクロには %occMessages.inc が必要です。
このマクロの詳細は、"Caché Server Pages (CSP) の使用法" の “CSP アプリケーションにおけるテキストのローカライズ” の章にある “コンパイル時および実行時の $$$Text マクロ” を参照してください。
TextJS マクロは、ローカライズに使用されます。Text マクロと同じ処理を実行し、その結果に JavaScript エスケープを適用します。その後、結果の文字列を返します。 このマクロには %occMessages.inc が必要です。
このマクロの詳細は、"Caché Server Pages (CSP) の使用法" の “CSP アプリケーションにおけるテキストのローカライズ” の章にある “コンパイル時および実行時の $$$Text マクロ” を参照してください。
ThrowOnError マクロは、指定された %StatusOpens in a new tab コード (sc) を評価します。sc がエラー状態を示す場合、ThrowOnError は THROW 操作を実行して、%Exception.StatusExceptionOpens in a new tab タイプの例外を例外ハンドラにスローします。このマクロには %occStatus.inc が必要です。詳細は、このドキュメントの “エラー処理” の章の "TRY-CATCH メカニズム" を参照してください。
THROWONERROR マクロは式 (expr) を評価します。その式の値は %StatusOpens in a new tab コードと見なされ、マクロは sc として渡された変数内に %StatusOpens in a new tab コードを格納します。%StatusOpens in a new tab がエラーの場合、THROWONERROR は THROW 処理を実行し、%Exception.StatusExceptionOpens in a new tab タイプの例外を例外ハンドラへスローします。このマクロには %occStatus.inc が必要です。
ThrowStatus マクロは、指定された %StatusOpens in a new tab コード (sc) を使用し、THROW 処理を実行して、%Exception.StatusExceptionOpens in a new tab タイプの例外を例外ハンドラへスローします。このマクロには %occStatus.inc が必要です。詳細は、このドキュメントの “エラー処理” の章の "TRY-CATCH メカニズム" を参照してください。