ObjectScript マクロとマクロ・プリプロセッサ
ObjectScript コンパイラには、プリプロセッサが組み込まれており、ObjectScript にはプリプロセッサ指示文に対するサポートが含まれています。これらの指示文を使用すると、アプリケーション (メソッドとルーチンの両方) で使用するマクロを作成できます。これらのマクロは、コード内で単純なテキストを置き換える機能を提供します。InterSystems IRIS® 自体にもさまざまな事前定義済みマクロが組み込まれています。これらについては、このドキュメントの該当コンテキストで説明します。
マクロの使用
このセクションでは、以下のトピックについて説明します。
カスタム・マクロの作成
マクロは、1 行の置換定義によって、ObjectScript の多様な機能をサポートできます。その基本形式では、#define 指示文を使用してマクロを作成します。例えば以下のコードは、StringMacro というマクロを生成し、そのマクロを文字列 “Hello, World!” に置換します。
#define StringMacro "Hello, World!"
(##continue を使用すると、#define 指示文を次の行に継続できます。)
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 つ以上のスペースをマクロ指示文に続けることができます。マクロ内では、任意数のスペースをマクロ指示文、マクロ名、マクロ値の間に入れることができます。
-
マクロ指示文は単一行の文となります。マクロ指示文、マクロ名、およびマクロ値はすべて同一行で記述する必要があります。##continue を使用すると、マクロ指示文を次の行に継続できます。
-
#if 指示文と #elseIf 指示文はテスト式の形をとります。テスト式にはスペースを含めることはできません。
-
#if 式、#elseIf 式、#else 指示文、および #endif 指示文はすべて、それぞれの独立した行に記述します。同一行にてこれらの指示文のいずれかに続くものはコメントと見なされるので、解析はされません。
マクロ・コメントとスタジオ・アシスト
マクロには、その定義の一部として渡すコメントを記述できます。/* と */、//、#;、;、および ;; で区切ったコメントは、すべて通常どおりに機能します。コメントの基本的な情報については、"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 という名前で呼び出します。このマクロ名の大文字と小文字は区別されます。
変数値を指定できないコンテキストで、マクロを呼び出して代わりの値を使用できます。以下に例を示します。
#define StringMacro "Hello",!,"World!"
WRITE $$$StringMacro
#define myclass "Sample.Person"
SET x=##class($$$myclass).%New()
マクロはテキスト置換であることに注意してください。マクロが置換された後、結果として出力された文の構文が正確かどうかをチェックします。したがって、式を定義するマクロは、式を必要とするコンテキスト内で呼び出される必要があります。 また、コマンドとその引数に対するマクロは、ObjectScript の独立した行として置くことができます。
外部マクロ (インクルード・ファイル) の参照
複数のマクロをそれぞれ別々のファイルに保存した場合でも、#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 修飾子を提供します。詳細は、"ObjectScript リファレンス" の $SYSTEM 特殊変数の参照ページ内の "コンパイラ修飾子" のテーブルを参照してください。
また、#include のリファレンスも参照してください。
システム・プリプロセッサ・コマンド・リファレンス
InterSystems IRIS では、以下のシステム・プリプロセッサ指示文がサポートされています。
マクロ・プリプロセッサ指示文には、大文字と小文字の区別がありません。例えば、#include は #INCLUDE (大文字と小文字の他の組み合わせも同様) と同等に扱われます。
#;
#; プリプロセッサ指示文は、.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 コード内のマクロが置き換えられます。値を持たないマクロを定義すると、コードでは他のプリプロセッサ指示文を使用して、そのマクロが存在するかどうかをテストし、その結果に応じてアクションを実行できます。
##continue を使用すると、#define 指示文を次の行に継続できます。##; を使用すると、#define 行にコメントを追加できます。ただし、##continue と ##; を同じ行で使用することはできません。
値付きのマクロ
値付きのマクロは、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 は、コードを記述するための便利なオプションとして提供されています。ObjectScript は "型のない" 言語で、変数のデータ型を宣言することも、#dim で指定されたデータ型を強制することもありません。以下の形式をとります。
#dim VariableName As DataTypeName
#dim VariableName As DataType = InitialValue
#dim VariableName As List Of DataType
#dim VariableName As Array Of DataType
各項目の内容は次のとおりです。
-
VariableName には、定義する変数、またはコンマで区切られた変数のリストを指定します。
-
DataType には、VariableName の型を指定します。データ型の指定はオプションです。As DataType を省略して =InitialValue だけを指定できます。
-
InitialValue は、VariableName について必要に応じて指定する値です これは SET VariableName=InitialValue と同じです。(この構文はリストや配列には使用できません)。
DataType は、主としてスタジオ・アシストで使用されます。例えば、[スタジオアシスト] ドロップダウン・メニューを使用して、ユーザ定義のデータ型のパッケージとクラスを選択できます。詳細は、このドキュメントの “変数” の章の "変数の宣言" を参照してください。
InitialValue
-
VariableName にデータ変数のコンマ区切りのリストを指定し、DataType を省略すると、すべての変数に同じ初期値が割り当てられます。例を以下に示します。
#dim a,b,c = ##class(Sample.Person).%New()
これは以下と同じです。
SET (a,b,c) = ##class(Sample.Person).%New()
各変数に同じ OREF を割り当てます。
-
VariableName にデータ変数のコンマ区切りのリストを指定し、DataType が標準の %Library データ型の場合、すべての変数に同じデータ型と初期値が割り当てられます。例を以下に示します。
#dim d,e,f As %String = ##class(Sample.Person).%New()
これは以下と同じです。
SET (d,e,f) = ##class(Sample.Person).%New()
各変数に同じ OREF を割り当てます。
-
VariableName にオブジェクト変数のコンマ区切りのリストを指定し、DataType が %Library データ型でない場合、各変数に別個の OREF が割り当てられます。例を以下に示します。
#dim j,k,l As Sample.Person = ##class(Sample.Person).%New()
各変数に別個の OREF を割り当てます。
-
VariableName に変数のコンマ区切りのリストを指定し、DataType がダイナミック・データ型の場合、各変数に別個の OREF が割り当てられます。例を以下に示します。
#dim m,n,o As %DynamicObject = {"name":"Fred"} #dim p,q,r As %DynamicArray = ["element1","element2"]
各変数に別個の OREF を割り当てます。
#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 プリプロセッサ指示文は、それ以降に記述されている埋め込み SQL DML 文のスキーマ検索パスを指定します。
#import は、1 つまたは複数の検索するスキーマ名を指定して、未修飾のテーブル名、ビュー名、またはストアド・プロシージャ名にスキーマ名を指定します。単一のスキーマ名を指定できるほか、複数のスキーマ名をコンマ区切りリストで指定することもできます。スキーマは現在のネームスペースで検索されます。以下の例に示すとおり、Employees.Person テーブルが検出されます。
#import Customers,Employees,Sales
&sql(SELECT Name,DOB INTO :n,:date FROM Person)
WRITE "name: ",n," birthdate: ",date,!
WRITE "SQLCODE=",SQLCODE
#import 指示文に指定されたすべてのスキーマが検索されます。Person テーブルは、#import にリストされたスキーマのうちのまさに 1 つで見つかる必要があります。#import は、スキーマ検索パス内での一致が必要なため、システム全体の既定のスキーマは使用されません。
ダイナミック SQL では、%SchemaPath プロパティを使用して、未修飾名を解決するためのスキーマ検索パスを指定します。
#import および #sqlcompile path の両方は、未修飾のテーブル名を解決するために使用される 1 つまたは複数のスキーマ名を指定します。これらの 2 つの指示文の違いを以下に示します。
-
#import は、あいまいなテーブル名を検出します。#import は、指定されたすべてのスキーマを検索し、すべての一致を検出します。#sqlcompile path は、スキーマの指定されたリストを左から右の順に、最初の一致が検出されるまで検索します。そのため、#import はあいまいなテーブル名を検出できます。#sqlcompile path ではできません。例えば、#import Customers,Employees,Sales は Customers スキーマ、Employees スキーマ、および Sales スキーマの中に Person の出現をちょうど 1 つだけ検出する必要があります。このテーブル名の出現を複数検出した場合、「Table 'PERSON' is ambiguous within schemas」という SQLCODE -43 エラーが発生します。
-
#import では、システム全体の既定値を使用できません。#import が、リストされているスキーマのいずれでも Person テーブルを検出できない場合、SQLCODE -30 エラーが発生します。#sqlcompile path が、リストされているスキーマのいずれでも Person テーブルを検出できない場合、システム全体の既定のスキーマが確認されます。
-
#import 指示文は付加的です。#import 指示文が複数ある場合、すべての指示文のスキーマでちょうど 1 つの一致のみを解決する必要があります。2 番目の #import を指定しても、前の #import で指定されたスキーマ名のリストは無効化されません。#sqlcompile path 指示文を指定すると、以前の #sqlcompile path 指示文で指定されたパスは上書きされます。以前の #import 指示文で指定されたスキーマ名が #sqlcompile path によって上書きされることはありません。
InterSystems IRIS は、#import 指示文に存在しないスキーマ名を無視します。InterSystems IRIS は、#import 指示文の重複したスキーマ名を無視します。
テーブル名が修飾済みの場合、#import 指示文は適用されません。以下はその例です。
#import Voters
#import Bloggers
&sql(SELECT Name,DOB INTO :n,:date FROM Sample.Person)
WRITE "name: ",n," birthdate: ",date,!
WRITE "SQLCODE=",SQLCODE
この場合、InterSystems IRIS は Sample スキーマで Person テーブルを検索します。Voters スキーマと Bloggers スキーマでは検索されません。
-
#import は SQL DML 文に適用されます。これを使用して、SQL SELECT クエリの未修飾テーブル名とビュー名、および INSERT、UPDATE、および DELETE 演算の未修飾テーブル名とビュー名を解決できます。#import は、SQL の CALL 文にある未修飾プロシージャ名の解決にも使用できます。
-
#import は SQL DDL 文には適用されません。これは、CREATE TABLE やその他の CREATE 文、ALTER 文、DROP 文などのデータ定義文内の未修飾のテーブル名、ビュー名、およびプロシージャ名の解決には使用できません。この項目の定義を作成、変更、または削除する場合に、テーブル、ビュー、またはストアド・プロシージャに未修飾名を指定すると、InterSystems IRIS では #import の値が無視され、システム全体の既定のスキーマが使用されます。
#sqlcompile path プリプロセッサ指示文と比較します。
#include
#include プリプロセッサ指示文は、プリプロセッサ指示文を記述した、指定の名前のファイルをロードします。以下の形式をとります。
#include <filename>
filename は、.inc 接頭語を除く、インクルード・ファイルの名前です。インクルード・ファイルは通常、呼び出し元のファイルと同じディレクトリにあります。名前は大文字と小文字が区別されます。
システム提供の #include filename をすべてリストするには、以下のコマンドを発行します。
ZWRITE ^rINC("%occInclude",0)
これらの #include ファイルのうち、1 つのファイルの内容をリストするには、目的のインクルード・ファイルを指定します。以下に例を示します。
ZWRITE ^rINC("%occStatus",0)
INT ルーチンの生成時に事前処理された #include ファイルをリストするには、^ROUTINE グローバルを使用します。これらの #include 指示文を ObjectScript コードで参照する必要はないことに注意してください。
ZWRITE ^ROUTINE("myroutine",0,"INC")
ストアド・プロシージャのコードで #include を使用する場合は、以下のようにコロン文字 “:” を前に記述する必要があります。
CREATE PROCEDURE SPxx() Language OBJECTSCRIPT {
:#include %occConstant
SET x=##lit($$$NULLOREF)
}
クラスの開始部にてファイルを組み込む場合、指示文にはシャープ記号を含めません。したがって、単一ファイルでは以下のようになります。
Include MyMacros
複数ファイルでは以下のようになります。
Include (MyMacros, YourMacros)
例えば、マクロを含む OS.inc ヘッダ・ファイルがあるとします。
#define Windows
#define UNIX
#include OS
#ifDef Windows
WRITE "The operating system is not case-sensitive.",!
#else
WRITE "The operating system is case-sensitive.",!
#endif
#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 プリプロセッサ指示文は非推奨です。InterSystems IRIS 2020.1 では、ほとんどの操作 (SELECT、INSERT、UPDATE、DELETE を含む) の埋め込み SQL コードは、このプリプロセッサ指示文の設定に関係なく、この SQL コードを含むルーチンのコンパイル時ではなく、SQL コードの実行時 (ランタイム) にコンパイルされます。
以前のリリースでは、#sqlcompile mode=value によって、このプリプロセッサ指示文に続くコードの埋め込み SQL に対してコンパイル・モードが指定されていました。これによって、特定の埋め込み SQL の DML コマンドに、Embedded (コンパイル時に埋め込み SQL を処理) と Deferred (実行時まで埋め込み SQL の処理を遅延) のいずれかのコンパイル・モードが指定されていました。
InterSystems IRIS 2020.1 では、埋め込み SQL の DML コマンドはすべて実行時まで遅延され、実行時にクエリ・キャッシュとして処理されます。したがって、埋め込み SQL は常に、コンパイル時に存在しないテーブル、ユーザ定義関数、および他の SQL エンティティを参照できます。
埋め込み SQL 文は、コンパイル時に解析されます。埋め込み SQL 文に無効な SQL (SQL 構文エラーなど) が含まれている場合、コンパイラは、コード "** SQL Statement Failed to Compile **" を生成し、ObjectScript コードのコンパイルを続行します。このように、無効な埋め込み SQL を含むメソッドでクラスをコンパイルすると、SQL エラーが報告されますが、メソッドは生成されます。このメソッドを実行すると、無効な SQL によるエラーが発生します。
詳細は、"InterSystems SQL の使用法" の "埋め込み SQL" の章を参照してください。
#sqlcompile mode=Deferred を、名前が似ているもののまったく異なる目的で使用される $SYSTEM.SQL.Util.SetOption(“CompileModeDeferred”) メソッドと混同しないようにしてください。
#sqlcompile path
#sqlcompile path プリプロセッサ指示文は、それ以降に記述されている埋め込み SQL DML 文のスキーマ検索パスを指定します。以下の形式をとります。
#sqlcompile path=schema1[,schema2[,...]]
schema は、現在のネームスペースで未修飾の SQL テーブル名、ビュー名、またはプロシージャ名を検索する際に使用するスキーマ名です。単一のスキーマ名を指定できるほか、複数のスキーマ名をコンマ区切りリストで指定することもできます。複数のスキーマは指定の順序で検索されます。最初の一致が出現すると、検索は終了し、DML 操作が実行されます。いずれのスキーマにも一致が含まれていない場合、システム全体の既定のスキーマが検索されます。
スキーマは指定された順序で検索されるため、あいまいなテーブル名は検出されません。#import プリプロセッサ指示文も、スキーマ名のリストから未修飾の SQL テーブル名、ビュー名、プロシージャ名にスキーマ名を指定します。#import はあいまいな名前を検出します。
InterSystems IRIS は、#sqlcompile path 指示文に存在しないスキーマ名を無視します。InterSystems IRIS は、#sqlcompile path 指示文の重複したスキーマ名を無視します。
-
#sqlcompile path は SQL DML 文に適用されます。これを使用して、SQL SELECT クエリの未修飾テーブル名とビュー名、および INSERT、UPDATE、および DELETE 演算の未修飾テーブル名とビュー名を解決できます。#sqlcompile path は、SQL の CALL 文にある未修飾プロシージャ名の解決にも使用できます。
-
#sqlcompile path は SQL DDL 文には適用されません。これは、CREATE TABLE やその他の CREATE 文、ALTER 文、DROP 文などのデータ定義文内の未修飾のテーブル名、ビュー名、およびプロシージャ名の解決には使用できません。この項目の定義を作成、変更、または削除する場合に、テーブル、ビュー、またはストアド・プロシージャに未修飾名を指定すると、InterSystems IRIS では #sqlcompile path の値が無視され、システム全体の既定のスキーマが使用されます。
ダイナミック SQL では、%SchemaPath プロパティを使用して、未修飾名を解決するためのスキーマ検索パスを指定します。
以下の例では、未修飾テーブル名 Person を Sample.Person テーブルに解決します。最初に 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 クエリ・プロセッサで未修飾の名前を解決するとき、その指定のスキーマ検索パスが最初に使用されます。指定されたテーブルまたはプロシージャが見つからない場合、SQL クエリ・プロセッサは #import (指定されている場合) で指定されているスキーマ、または構成されているシステム全体の既定のスキーマを検索します。指定されたテーブルがこれらの場所のどこにも見つからない場合は、SQLCODE -30 エラーが生成されます。
スキーマ検索パスのスコープは、それが定義されているルーチンまたはメソッドです。クラス・メソッドでスキーマ・パスを指定している場合、これはそのクラス・メソッドのみに適用され、同じクラスにある他のメソッドには適用されません。.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 入力ホスト変数の必須の入力データ形式が決まります。詳細は、"InterSystems SQL の使用法" の “埋め込み SQL の使用法” の章の "マクロ・プリプロセッサ" を参照してください。
以下の埋め込み SQL の例は、さまざまなコンパイル・モードを使用して、Sample.Person テーブルにある 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 コードまたはインクルード・ファイルでのみ表示されます。プリプロセッサ指示文のコメントには、常に ##; コメント文字を使用する必要があります。
#define alphalen ##function($LENGTH("abcdefghijklmnopqrstuvwxyz")) ##; + 100
WRITE $$$alphalen," is the length of the alphabet"
##; コメント文字は、#define、#def1arg、または #dim の各プリプロセッサ指示文に記述できます。このコメント文字を ##continue プリプロセッサ指示文に続けて使用することはできません。// または ; といった行の残りの部分をコメントと認識させる文字は、プリプロセッサ指示文内では使用しないでください。
##; を ObjectScript コード行または埋め込み SQL コード行の任意の場所で使用して、.int コードに表示されないコメントを指定することもできます。その行の残りの部分がコメントになります。
##; は、埋め込み HTML または埋め込み JavaScript の評価の前に評価されます。
列 1 に表示され行全体をコメントにする、#; と比較します。##; は、現在の行の残りの部分をコメントにします。##; が行の最初の列に表示された場合は、#; プリプロセッサ指示文と機能的に同じになります。
##beginquote ...##EndQuote
##beginquote text ##EndQuote プリプロセッサ指示文は、text 文字列を引用符で囲み、text 内のすべての引用符を二重にします。##quote 指示文と目的は似ていますが、##BeginQuote ... ##EndQuote では、以下の例に示すように、ペアになっていない括弧または引用符を使用できます。
SET code($i(code))=##beginquote SET def="SQL code-generation" &SQL(SELECT Name ##EndQuote
SET code($i(code))=##beginquote FROM Sample.Person) ##EndQuote
##continue
##continue プリプロセッサ指示文は、次の行にマクロ定義を継続して複数の行でそのマクロ定義をサポートします。マクロ定義の行の最後に現れて、以下の形式で継続していることを示します。
#define <beginning of macro definition> ##continue
<continuation of macro definition>
マクロ定義では、複数の ##continue 指示文を使用できます。
以下はその例です。
#define Multiline(%a,%b,%c) ##continue
SET v=" of Oz" ##continue
SET line1="%a"_v ##continue
SET line2="%b"_v ##continue
SET line3="%c"_v
$$$Multiline(Scarecrow,Tin Woodman,Lion)
WRITE "Here is line 1: ",line1,!
WRITE "Here is line 2: ",line2,!
WRITE "Here is line 3: ",line3,!
##continue は、最終行を除くすべてのマクロ定義行の末尾に記す必要があります。これには、コメント行も含まれます。したがって、##continue は ##; または #; 1 行コメント または複数行の /* コメントテキスト */ の各行を次のように終了させる必要があります。
#define <beginning of macro definition> ##continue
#; single line comment ##continue
/* Multi-line long ##continue
wordy comment */ ##continue
<continuation of macro definition>
##expression
##expression プリプロセッサ関数は、コンパイル時に ObjectScript 式を評価します。以下の形式をとります。
##expression(content)
ここで、content は、引用符で囲まれた文字列または他のプリプロセッサ指示文を含まない、有効な ObjectScript コードです (下記の入れ子にされた ##expression を除く)。
プリプロセッサは、コンパイル時にこの関数の引数の値を評価し、ObjectScript .int コードの評価で ##expression(content) を置き換えます。##expression 内では変数を引用符で囲んで記述する必要があります。そうしない場合、その変数がコンパイル時に評価されます。
以下の例では、簡単な式をいくつか示します。
#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/29/2018 07:49:30",!
##expression とリテラル文字列
##expression で解析しても、リテラル文字列は認識されません。引用符内で文字を括弧で囲っても、特別に扱われません。例えば、以下の指示文を考えます。
#define MyMacro ##expression(^abc(")",1))
引用符が付いた右側の括弧は、引数を指定するための閉じ括弧であるかのように扱われます。
##expression の入れ子
InterSystems IRIS では、入れ子にした ##expression を使用できます。他の ##expression に展開するマクロを含む ##expression は、その展開が ObjectScript レベルで評価可能 (つまり、プリプロセッサ指示文がない) であって、ObjectScript 変数に格納可能であれば、定義可能です。入れ子にした ##expression では、##expression 式を持つマクロが最初に展開された後、入れ子にした ##expression が展開されます。
##expression では、##BeginLit...##EndLit、##function、##lit、##quote、##SafeExpression、##stripq、##unique、##this の各マクロ関数を入れ子にすることもできます。
##expression、サブクラス、および ##SafeExpression
メソッドが ##expression を含んでいる場合、クラスのコンパイル時にこれが検出されます。コンパイラでは ##expression の内容を解析しないため、この ##expression によってサブクラスで本来とは異なるコードが生成される可能性があります。これを回避するために、InterSystems IRIS では、サブクラスごとにコンパイラでメソッド・コードを再生成するようにしています。例えば、##expression(%classname) では現在のクラス名を挿入しますが、サブクラスのコンパイル時には、サブクラスのクラス名が挿入されている必要があります。InterSystems IRIS では、メソッドを強制的にサブクラス内で再生成することで、この状態が確実に発生するようになっています。
サブクラス内のコードが別のものにならないことがわかっている場合は、サブクラスごとのメソッドの再生成を回避してもかまいません。そのためには、##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) を置き換えます。
以下の例は、ObjectScript 関数から値を返します。
#define alphalen ##function($LENGTH("abcdefghijklmnopqrstuvwxyz"))
WRITE $$$alphalen
以下の例では、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
64797,43570
"64797,53807"
USER>
ここで、出力の最初の行は、コンパイル時の $Horolog の値で、2 行目は、実行時の $Horolog の値です。(出力の最初の行は引用符で囲まれておらず、2 行目は囲まれています。これは、x がその値の代わりに引用符で囲まれた文字列を使用するためです。したがって、引用符はターミナルに表示されませんが、y は引用符に囲まれた文字列を直接ターミナルに出力します。)
##function 呼び出しの返り値が、呼び出しのコンテキストで意味と構文の両方が理解できることを確認するのは、アプリケーション・プログラマの責任です。
##function の入れ子
InterSystems IRIS では、##function 内の入れ子がサポートされます。##function では、##BeginLit...##EndLit、##function、##lit、##quote、 ##expression、##SafeExpression、##stripq、##unique、および ##this の各マクロ関数を入れ子にすることができます。
##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
##beginquote .... ##EndQuote 指示文では、ペアになっていない括弧文字または引用符文字を使用できます。
以下の例は、##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
##quote の入れ子
InterSystems IRIS では、##quote 関数内の入れ子がサポートされます。##quote では、##BeginLit...##EndLit、##function、##lit、##quote、##expression、##SafeExpression、##stripq、##unique、および ##this の各マクロ関数を入れ子にすることができます。
##quoteExp
##quoteExp プリプロセッサ関数は、コンパイル時に評価される式を引数として取ります。この式には、入れ子/再帰 MPP 関数を含めることができます。その後、このプリプロセッサ関数は、コンパイルされた結果を引用符付きの文字列として返します。その引数に既に引用符文字が付いている場合、引用符文字を二重にすることによってこれらの引用符文字をエスケープします。以下の形式をとります。
##quoteExp(expression)
expression には、##BeginLit...##EndLit、##expression、##function、##lit、##quote、##quoteExp、##SafeExpression、##stripq、##unique、および ##This のいずれの入れ子/再帰 MPP 関数も記述できます。
##quoteExp を使用すると、可変個数の添え字を受け入れ、その参照を引用符付き文字列として返す複雑な汎用グローバル・マクロを作成できます。このとき、添え字の値が数値と文字列のいずれとして渡されるかは関係ありません。#def1arg 指示文を使用して、複雑なグローバル参照としてマクロを定義します。この複雑なグローバル参照を引用符付き文字列として返すには、マクロに指定した添え字に関係なく、このマクロを ##quoteExp にラップします。このマクロは、##quoteExp に渡された expression 引数を評価し、この値を引用符付き文字列として返します。
以下に例を示します。
#def1arg complexGlobal(%subs) ^GLO("dd"##expression($s(%literalargs'=$lb(""):","_$LTS(%literalargs,","),1:"")))
#def1arg complexGlobalQE(%subs) ##quoteExp($$$complexGlobal(%subs))
##sql
##sql プリプロセッサ指示文は、実行時に指定された埋め込み SQL 文を呼び出します。以下の形式をとります。
##sql(SQL-statement)
ここで SQL-statement は有効な埋め込み SQL 文です。##sql プリプロセッサ指示文は、&SQL 埋め込み SQL マーカーとまったく同じです。どちらの場合も、括弧で囲まれた SQL コードが、これを含むルーチンのコンパイル時ではなく、実行時にコンパイルされます (最初の実行)。詳細は "埋め込み SQL のコンパイル" を参照してください。
##stripq
##stripq プリプロセッサ関数は、単一の引数を取り、引用符を削除してその引数を返します。これは、##quote マクロ関数とは逆の関数です。
##stripq(value)
value はリテラルまたは変数で、これが引用符で囲まれている場合は引用符が削除されます。
##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 変数を直接設定しない。InterSystems IRIS は、システムで使用するためにすべての % 変数 (%z 変数および %Z 変数を除く) を予約しています。これらをユーザ・コードで設定しないでください。
システムにより提供されるマクロの使用
このセクションでは、InterSystems IRIS で使用できる定義済みマクロのいくつかに関連するトピックについて説明します。トピックは以下のとおりです。
システムにより提供されるマクロをアクセス可能にする方法
これらのマクロは、%RegisteredObjectOpens in a new tab のすべてのサブクラスで使用可能です。%RegisteredObjectOpens in a new tab の拡張でないルーチンまたはクラスの中でこれらのマクロを使用可能にするには、以下の適切なファイルをインクルードします。
-
状態関連のマクロの場合は、%occStatus.inc をインクルードします。
-
メッセージ関連のマクロの場合は、%occStatus.inc をインクルードします。
どのインクルード・ファイルが必要になるかについては、以下に示す各マクロを参照してください。
そのような文の構文は以下のとおりです。
#include %occStatus
これらのインクルード・ファイルでは、大文字と小文字が区別されます。外部で定義されたマクロの使用法の詳細は、セクション “外部マクロ (インクルード・ファイル) の参照” を参照してください。
システムにより提供されるマクロのリファレンス
マクロ名では大文字と小文字が区別される。InterSystems IRIS では、以下のマクロが提供されます。
ADDSC マクロは、%Status コード (sc2) を既存の %StatusOpens in a new tab コード (sc1) に付加します。このマクロには %occStatus.inc が必要です。
EMBEDSC マクロは、%Status コード (sc2) を既存の %StatusOpens in a new tab コード (sc1) 内に埋め込みます。このマクロには %occStatus.inc が必要です。
ERROR マクロは、オブジェクト・エラー・コード (errorcode) を使用して、%Status オブジェクトを作成します。オブジェクト・エラー・コードの関連テキストは、%1、%2 などの形式の引数を数個受け付けます。その後、ERROR はそれらの引数を、errorcode (arg1、arg2 など) に続くマクロ引数に置換します。その際には、この追加引数の順序に基づいて置換されます。このマクロには %occStatus.inc が必要です。
システム定義のエラー・コードのリストは、"InterSystems IRIS エラー・リファレンス" の “一般的なエラー・メッセージ” を参照してください。
FormatMessage マクロを使用すると、同じマクロ呼び出し内で、メッセージ・ディクショナリからテキストを取得し、メッセージ引数をテキストに置き換えることができます。これは %StringOpens in a new tab を返します。
引数 | 説明 |
---|---|
language | RFC1766Opens in a new tab 言語コード。Web アプリケーション内では、%response.Language を指定して既定のロケールを使用できます。 |
domain | メッセージ・ドメイン。Web アプリケーション内では、%response.Domain を指定できます。 |
id | メッセージ ID。 |
default | language、domain、および id で識別されるメッセージが見つからない場合に使用する文字列。 |
arg1、arg2 など | メッセージ引数の置換テキスト。これらはすべてオプションなので、メッセージに引数がない場合でも $$$FormatMessage を使用できます。 |
メッセージ・ディクショナリの詳細は、"InterSystems Business Intelligence の実装" の “ローカライズの実行” を参照してください。
このマクロには %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 マクロは、指定の %Status コード (sc) のエラー・コード値を返します。 このマクロには %occStatus.inc が必要です。
GETERRORMESSAGE マクロは、指定の %Status コード (sc) から、エラー・メッセージ値の、num で指定されている部分を返します。 例えば、num=1 は SQLCODE エラー番号を返し、num=2 はエラー・メッセージ・テキストを返します。このマクロには %occStatus.inc が必要です。
ISERR マクロは、指定の %Status コード (sc) がエラー・コードの場合に True を返します。それ以外は False を返します。このマクロには %occStatus.inc が必要です。
ISOK マクロは、指定の %Status コード (sc) が正常に完了した場合に True を返します。それ以外は False を返します。このマクロには %occStatus.inc が必要です。
OK マクロは、正常終了を表す %Status コードを作成します。このマクロには %occStatus.inc が必要です。
Text マクロは、ローカライズに使用されます。Text マクロは、コンパイル時に新しいメッセージを生成し、実行時にはそのメッセージを取得するコードを生成します。 このマクロには %occMessages.inc が必要です。
TextHTML マクロは、ローカライズに使用されます。Text マクロと同じ処理を実行し、その結果に HTML エスケープを適用します。その後、結果の文字列を返します。 このマクロには %occMessages.inc が必要です。
TextJS マクロは、ローカライズに使用されます。Text マクロと同じ処理を実行し、その結果に JavaScript エスケープを適用します。その後、結果の文字列を返します。 このマクロには %occMessages.inc が必要です。
ThrowOnError マクロは、指定された %Status コード (sc) を評価します。sc がエラー状態を示す場合、ThrowOnError は THROW 操作を実行して、%Exception.StatusExceptionOpens in a new tab タイプの例外を例外ハンドラにスローします。このマクロには %occStatus.inc が必要です。詳細は、このドキュメントの “エラー処理” の章の "TRY-CATCH メカニズム" を参照してください。
THROWONERROR マクロは式 (expr) を評価します。その式の値は %Status コードと見なされ、マクロは sc として渡された変数内に %StatusOpens in a new tab コードを格納します。%StatusOpens in a new tab がエラーの場合、THROWONERROR は THROW 処理を実行し、%Exception.StatusExceptionOpens in a new tab タイプの例外を例外ハンドラへスローします。このマクロには %occStatus.inc が必要です。
ThrowSQLCODE マクロは、指定された SQLCODE とメッセージを使用し、THROW 処理を実行して、%Exception.SQLOpens in a new tab タイプの例外を例外ハンドラへスローします。このマクロには %occStatus.inc が必要です。詳細は、このドキュメントの “エラー処理” の章の "TRY-CATCH メカニズム" を参照してください。
ThrowSQLIfError マクロは、指定された SQLCODE とメッセージを使用し、THROW 処理を実行して、%Exception.SQLOpens in a new tab タイプの例外を例外ハンドラへスローします。このマクロは、SQLCODE が 0 未満 (エラーを示す負の数字) の場合に、この例外をスローします。このマクロには %occStatus.inc が必要です。詳細は、このドキュメントの “エラー処理” の章の "TRY-CATCH メカニズム" を参照してください。
ThrowStatus マクロは、指定された %Status コード (sc) を使用し、THROW 処理を実行して、%Exception.StatusExceptionOpens in a new tab タイプの例外を例外ハンドラへスローします。このマクロには %occStatus.inc が必要です。詳細は、このドキュメントの “エラー処理” の章の "TRY-CATCH メカニズム" を参照してください。
マクロが拡張される条件
プリプロセッサ指示文は、MAC バージョンのコードに含まれています。MAC コードをコンパイルすると、ObjectScript コンパイラによって事前処理が実行され、INT (中間の読み取り可能な ObjectScript) コードおよび OBJ (実行可能オブジェクト) コードが生成されます。
プリプロセッサは、ObjectScript パーサによって埋め込み SQL が処理される前にマクロを拡張します。プリプロセッサは、埋め込みまたは遅延いずれかのコンパイル・モードの埋め込み SQL をサポートしています。プリプロセッサは、ダイナミック SQL でのマクロ拡張は行いません。
ObjectScript パーサは、プリプロセッサ指示文を解析する前に、複数の行から成るコメントを削除します。そのため、/* と */ で囲んで記述した複数行コメントの中で指定したマクロ・プリプロセッサ指示文は、実行されません。
以下のグローバルは MAC コード情報を返します。これらのグローバルとその添え字を表示するには、ZWRITE を使用します。
-
^rINDEX(routinename,"MAC") には、MAC コードが変更後に最後に保存された日時を表すタイムスタンプと、この MAC コード・ファイルの文字数が含まれます。文字数には、コメントおよび空白行が含まれます。MAC コードが最後に保存された日時を表すタイムスタンプ、コンパイルされた日時を表すタイムスタンプ、および使用された #include ファイルに関する情報は、.INT ファイルの ^ROUTINE グローバルに記録されます。.INT コードの詳細は、"ZLOAD" コマンドを参照してください。
-
^rMAC(routinename) には、MAC ルーチンの各コード行の添え字ノードが含まれます。また、^rMAC(routinename,0,0) には行数、^rMAC(routinename,0) には MAC ルーチンが最後に保存された日時のタイムスタンプ、^rMAC(routinename,0,"SIZE") には文字数がそれぞれ含まれます。
-
^rMACSAVE(routinename) には、MAC ルーチンの履歴が含まれます。ここには、保存された過去の MAC ルーチン・バージョン 5 つ分の ^rMAC(routinename) と同じ情報が含まれます。現在の MAC バージョンに関する情報は含まれません。