InterSystems コールアウト・ライブラリの作成
InterSystems コールアウト・ライブラリは、カスタム・コールアウト関数、および InterSystems IRIS がそれらを使用できるようにする使用可能化コードを含む共有ライブラリです。この章では、コールアウト・ライブラリを作成して実行時にアクセスする方法を説明します。
以下の項目について説明します。
-
コールアウト・ライブラリの概要 — コールアウト・ライブラリの作成およびアクセス方法を説明します。
-
ZFEntry リンク・オプション — 関数引数が渡される方法を決定するリンク・オプションの詳細を説明します。
-
互換性のある言語とコンパイラ — C 以外の言語でコールアウト・ライブラリを作成する方法を説明します。
-
コールアウト・ライブラリ Runup 関数と Rundown 関数 — コールアウト・ライブラリがロードまたはアンロードされるときに自動的に実行されるように設定できる 2 つのオプション関数について説明します。
-
トラブルシューティングとエラー処理 — 避けるべきコーディング方法の一覧を示し、UNIX® シグナル処理のエラーを扱うための特別な関数について説明します。
このドキュメントでは、共有ライブラリという用語は、動的にリンクされたファイル (Windows の DLL ファイル、または UNIX® および関連オペレーティング・システムの SO ファイル) を示します。コールアウト・ライブラリは、$ZF コールアウト・インタフェースへのフックを含む共有ライブラリであり、多様な $ZF 関数が実行時にこれをロードし、アクセスできるようにします。
コールアウト・ライブラリの概要
ObjectScript コードからコールアウト・ライブラリにアクセスするには複数の方法がありますが、一般的には、ライブラリ名、関数名、および必要な引数を指定するのが主な方法です (“コールアウト・ライブラリ関数の呼び出し” を参照してください)。例えば、以下のコードでは、簡単なコールアウト・ライブラリ関数を呼び出します。
以下の ObjectScript コードは、ターミナルで実行されます。これは、simplecallout.dll という名前のコールアウト・ライブラリをロードし、2 つの整数引数を加算して合計を返す AddInt と言う名前のライブラリ関数を呼び出します。
USER> set sum = $ZF(-3,"simplecallout.dll","AddInt",2,2)
USER> write "The sum is ",sum,!
The sum is 4
この例では、$ZF(-3) を使用します。これは、単一のコールアウト・ライブラリ関数を呼び出す最も簡単な方法です。その他のオプションは、“コールアウト・ライブラリ関数の呼び出し” を参照してください。
simplecallout.dll コールアウト・ライブラリの複雑さは、それを呼び出すコードとあまり変わりません。すべてのコールアウト・ライブラリで必要とされる以下の 3 つの要素を含みます。
-
iris-cdzf.h コールアウト・ヘッダ・ファイルを含めるときに提供される標準コード。
-
パラメータが正しく指定された 1 つ以上の関数。
-
ZFEntry テーブル のマクロ・コード。これは、ライブラリがロードされる際に InterSystems IRIS がコールアウト関数を見つけるために使用するメカニズムを生成します (詳細は、“ZFEntry テーブルの作成” を参照してください)。
simplecallout.dll コールアウト・ライブラリを生成するためにコンパイルされたコードは以下のとおりです。
#define ZF_DLL /* Required for all Callout code. */
#include <iris-cdzf.h> /* Required for all Callout code. */
int AddTwoIntegers(int a, int b, int *outsum) {
*outsum = a+b; /* set value to be returned by the $ZF function call */
return IRIS_SUCCESS; /* set the exit status code */
}
ZFBEGIN
ZFENTRY("AddInt","iiP",AddTwoIntegers)
ZFEND
-
最初の 2 行では、ZF_DLL を定義し、iris-cdzf.h ファイルを含める必要があります。これらの 2 行は必ず含める必要があります。
-
AddTwoIntegers() 関数が次に定義されます。これには、以下の機能があります。
-
2 つの入力パラメータである整数 a と b、および 1 つの出力パラメータである整数ポインタ *outsum。
-
出力パラメータ *outsum に値を割り当てる文。これは、$ZF(-3) への呼び出しによって返される値です。
-
return 文は、関数出力値を返しません。代わりに、$ZF 呼び出しが正常に行われた場合に InterSystems IRIS によって受け取られる終了ステータス・コードを指定します。この関数が失敗した場合、InterSystems IRIS はシステムによって生成された終了ステータス・コードを受け取ります。
-
-
最後の 3 行は、InterSystems IRIS がコールアウト・ライブラリ関数を見つけるために使用する ZFEntry テーブルを生成するマクロ呼び出しです。この例では、単一のエントリのみです。ここで、
-
"AddInt" は、$ZF 呼び出し内の関数を識別するために使用される文字列です。
-
"iiP" は、2 つの入力値および出力値のデータ型を指定する文字列です。
-
AddTwoIntegers は、C 関数のエントリ・ポイント名です。
-
ZFEntry テーブルは、共有ライブラリをロードし、$ZF コールアウト・インタフェースによりアクセス可能にするメカニズムです (“ZFEntry テーブルの作成” を参照)。ZFENTRY 宣言は、C 関数と ObjectScript $ZF 呼び出し間のインタフェースを指定します。この例では、このインタフェースは、次のように動作します。
-
C 関数宣言 は、以下の 3 つのパラメータを指定します。
int AddTwoIntegers(int a, int b, int *outsum)
パラメータ a および b は入力であり、outsum は、出力値を受け取ります。AddTwoIntegers の返り値は、終了ステータス・コードであり、出力値ではありません。
-
ZFENTRY マクロ は、InterSystems IRIS 内での関数の識別方法、およびパラメータが渡される方法を定義します。
ZFENTRY("AddInt","iiP",AddTwoIntegers)
"AddInt" は、$ZF 呼び出し内の C 関数 AddTwoIntegers を指定するために使用されるライブラリ関数識別子です。リンク宣言 ("iiP") は、パラメータ a および b をリンク・タイプ i (入力のみの整数) として、outsum をリンク・タイプ P (入力および出力の両方に使用できる整数ポインタ) として宣言します。
-
$ZF(-3) 関数呼び出し は、ライブラリ名、ライブラリ関数識別子、および入力パラメータを指定し、出力パラメータの値を返します。
set sum = $ZF(-3,"simplecallout.dll","AddInt",2,2)
パラメータ a および b は、最後の 2 つの引数によって指定されます。3 番目のパラメータ outsum は出力にのみ使用されるため、引数は必要ありません。outsum の値は、$ZF(-3) 呼び出しが返されたときに sum に割り当てられます。
ZFEntry テーブルの作成
すべてのコールアウト・ライブラリは、ZFEntry テーブルを定義する必要があり、これにより InterSystems IRIS がコールアウト関数をロードしてアクセスできるようになります (簡単な例は “コールアウト・ライブラリの概要” を参照してください)。ZFEntry テーブルは ZFBEGIN で始まり ZFEND で終わるマクロ・コードのブロックによって生成されます。これら 2 つのマクロ間で、関数を公開するごとに 1 回、ZFENTRY マクロを呼び出す必要があります。
各 ZFENTRY 呼び出しは、以下の 3 つの引数をとります。
ZFENTRY(zfname,linkage,entrypoint)
ここで、zfname は $ZF 呼び出し内で関数を指定するために使用される文字列、linkage は引数を渡す方法を指定する文字列、entrypoint は C 関数のエントリ・ポイント名です。
コールアウト・ライブラリを作成するには、ライブラリ関数を見つけるための内部の GetZFTable 関数を生成するスイッチである #define ZF_DLL 指示文をコードに含める必要があります。コールアウト・ライブラリがロードされると、InterSystems IRIS はこの関数を呼び出して後続のライブラリ関数名の検索用にライブラリを初期化します。
ZFEntry テーブル内のエントリの位置が重要となる場合があります。$ZF(-5) インタフェースおよび $ZF(-6) インタフェース (“コールアウト・ライブラリ関数の呼び出し” で説明します) は、両方ともテーブル内でシーケンス番号 (1 で始まる) を指定することによってライブラリ関数を呼び出します。例えば、$ZF(-6) は以下の呼び出しを使用して ZFEntry テーブル内の 3 番目の関数を呼び出します。
x = $ZF(-6,libID,3)
ここで libID はライブラリ識別子、3 はテーブル内の 3 番目のエントリのシーケンス番号です。
一部のコンパイラ (Microsoft Visual Studio など) は、プリコンパイル済みのヘッダをサポートします。プリコンパイル済みのヘッダを使用する場合、#define ZF_DLL 文がプリコンパイルに対して有効である必要があります。そうでない場合、結果として生成される dll により、使用時に <DYNAMIC LIBRARY LOAD> エラーが発生します。コールアウト・ライブラリにはプリコンパイル済みのヘッダを使用しないことを強くお勧めします。
ZFEntry リンク・オプション
各 ZFENTRY 文 (“ZFEntry テーブルの作成” を参照) では、関数引数が渡される方法を決定する文字列が必要です。このセクションでは、利用可能なリンク・オプションについて詳細に説明します。
-
NULL で終了する文字列を C リンク・タイプで渡す — NULL で終了する文字列のリンク・オプションについて説明します。
-
短い計算文字列を B リンク・タイプで渡す — 計算文字配列に ZARRAY 構造を使用するリンクについて説明します。
-
標準的な計算文字列を J リンク・タイプで渡す — 計算文字配列に InterSystems IRIS IRIS_EXSTR 構造を使用するリンクについて説明します。
-
従来の短い文字列の $ZF ヒープの構成 — 従来の短い文字列パラメータ渡しのメモリ割り当てを制御する InterSystems IRIS システム設定について説明します。
リンクの概要
各 ZFENTRY 文 (“ZFEntry テーブルの作成” を参照) では、引数が渡される方法を説明する文字列が必要です。例えば、"iP" は、整数と整数へのポインタの 2 つのパラメータを指定します。2 番目の文字は、2 番目の引数が入力と出力の両方に使用されることを指定するために大文字になっています。コードは、最大で 32 の実パラメータおよび仮パラメータを持つことができます。
大文字のリンク・タイプを指定する場合 (i を除くすべてのリンク・タイプで許可されている)、入力と出力の両方にこの引数を使用することができます。1 つの出力引数のみが指定されている場合は、その最終値は関数の返り値として使用されます。複数の出力引数が指定されている場合は、すべての出力引数がコンマ区切りの文字列として返されます。
出力引数は入力引数として使用する必要はありません。すべての入力引数の後に出力限定引数を指定する場合、関数は出力引数のいずれも指定することなく呼び出すことができます (例は、“コールアウト・ライブラリの概要” を参照してください)。
ObjectScript プログラマの観点からすると、パラメータは入力のみです。実パラメータの値は $ZF 呼び出しによって評価され、C ルーチン宣伝内の仮パラメータにリンクされます。C 仮パラメータへの変更は失われるか、または$ZF 返り値にコピー可能になるかのいずれかです。
ZFENTRY マクロが、仮パラメータを返り値として使用するように指定しない場合は、$ZF 呼び出しは空の文字列 ("") を返します。リンク宣言は、1 つ以上の出力パラメータを含むことができます。この場合、すべての返り値は単一のコンマ区切り文字列に変換されます。複数の返り値間に挿入されたコンマと、1 つの返り値内に存在するコンマとを区別する方法はないため、最後の返り値のみがコンマを含むようにします。
以下のテーブルは、利用可能なオプションを説明しています。
C データ型 | Input | In/Out | メモ |
---|---|---|---|
int | i または 4i | なし (P を使用) | 32 ビットの整数を指定します。i リンク・タイプは入力のみです。整数タイプを返すには、P または 4P (int *) を使用します。入力引数は数値文字列の場合があります (メモ 1 参照)。 |
int * | p または 4p | P または 4P | 32 ビットの整数へのポインタです。入力引数は数値文字列の場合があります (メモ 1 参照)。 |
_int64 | 8i | なし (8P を使用) | 64 ビットの整数を指定します。64 ビットの整数タイプを返すには、8P を使用します。入力引数は数値文字列の場合があります (メモ 1 参照)。 |
_int64 * | 8p | 8P | 64 ビットの整数へのポインタです。入力引数は数値文字列の場合があります (メモ 1 参照)。 |
double * | d | D | 入力引数は数値文字列の場合があります (メモ 1 参照)。#D を使用して double * を基数 2 形式で保持します (メモ 2 を参照)。 |
float * | f | F | 入力引数は数値文字列の場合があります (メモ 1 参照)。#F を使用して float * を基数 2 形式で保持します (メモ 2 を参照)。 |
char * | 1c または c | 1C または C | これは、一般的な C NULL 終了文字列です (メモ 3 を参照)。 |
unsigned short * | 2c または w | 2C または W | これは、C 形式の NULL 終了 UTF-16 文字列です (メモ 3 を参照)。 |
wchar t * | 4c | 4C | これは、wchar_t 要素のベクトルとして保存される C 形式の NULL 終了文字列です (メモ 3 および 4 を参照)。 |
ZARRAYP | 1b または b | 1B または B | 最大 32,767 文字の短い 8 ビット各国語文字列。 |
ZWARRAYP | 2b または s | 2B または S | 最大 32,767 文字の短い 16 ビット Unicode 文字列。 |
ZHARRAYP | 4b | 4B | wchar_t によって実装される要素内に保存される、最大 32,767 文字の短い Unicode 文字列 (メモ 4 を参照してください) |
IRIS_EXSTR | 1j または j | 1J または J | 8 ビット各国語文字で構成される、標準的な文字列 (文字列長の制限あり) |
IRIS_EXSTR | 2j または n | 2J または N | 16 ビット Unicode 文字で構成される、標準的な文字列 (文字列長の制限あり) |
IRIS_EXSTR | 4j | 4J | wchar_t 文字で構成される、標準的な文字列 (文字列長の制限あり) (メモ 4 を参照) |
-
i、p、d、f — 数値引数が指定されると、InterSystems IRIS では文字列の入力引数が許可されます。詳細は、“数値リンクの使用” を参照してください。
-
#F、#D — 数値を基数 2 浮動小数点形式で保持するには、float * で #F を使用するかまたは double * で #D を使用します。詳細は、“数値リンクの使用” を参照してください。
-
1C、2C、4C — このリンクで渡されるすべての文字列は最初の NULL 文字で切り捨てられます。詳細は、“NULL で終了する文字列を C リンク・タイプで渡す” を参照してください。
-
4B、4C、4J — wchar_t は通常 32 ビットですが、InterSystems IRIS では各 Unicode 文字を格納するのに 16 ビットのみを使用します。大きい wchar_t 値を含む出力引数は、$ZF の戻り値に割り当てるために UTF-16 に変換されます。UTF-16 を含む文字列 (サロゲート・ペア) は、$ZF 入力引数のために wchar_t に展開されます。wchar_t の真の値には、ObjectScript 関数 $WASCII() および $WCHAR() を使用してアクセスできます。
構造体と引数のプロトタイプ定義 (インターシステムズ内部定義を含む) は、インクルード・ファイル iris-cdzf.h にあります。
数値リンクの使用
数値リンク・タイプは、以下のデータ型用に提供されています。
C データ型 | Input | In/Out | メモ |
---|---|---|---|
int | i | なし (P を使用) | 32 ビットの整数を指定します。i リンク・タイプは入力のみです。整数タイプを返すには、P または 4P (int *) を使用します。 |
int * | p | P | 32 ビットの整数へのポインタです。 |
_int64 | 8i | なし (8P を使用) | 64 ビットの整数を指定します。64 ビットの整数タイプを返すには、8P を使用します。 |
_int64 * | 8p | 8P | 64 ビットの整数へのポインタです。 |
double * | d | D | #D (出力のみ) を使用して double * を基数 2 形式で返します。 |
float * | f | F | #F (出力のみ) を使用して float * を基数 2 形式で返します。 |
数値引数が指定されると、InterSystems IRIS では文字列の入力引数が許可されます。文字列が渡されると、先頭の数字が文字列から解析されて数値が派生します。先頭の数字がない場合は、値 0 が受け取られます。つまり、"2DOGS" は 2.0 として受け取られますが、"DOG" は 0.0 として受け取られます。整数の引数は切り捨てられます。例えば、"2.1DOGS" は 2 として受け取られます。詳細は、"ObjectScript の使用法" の “文字列から数値への変換” を参照してください。
出力リンクが F (float *) または D (double *) で指定されている場合、返す数値は内部基数 10 の数の形式に変換されます。数値を基数 2 形式で保持するには、float * で #F を使用するかまたは double * で #D を使用します。
入力引数では # 接頭語は許可されていません。変換 (精度が若干失われる原因となる場合があります) を回避するには、関数を呼び出す ObjectScript コード内で $DOUBLE を使用して入力値を作成する必要があり、対応する入力リンクは小文字の f または d として指定する必要があります。
InterSystems IRIS は、標準の IEEE 形式の 64 ビット浮動小数点を作成する $DOUBLE 関数をサポートします。これらの数は、精度を失うことなく外部関数と InterSystems IRIS 間で渡すことができます (外部関数が 64 ビットの double ではなく 32 ビットの float を使用する場合を除く)。出力では、IEEE 形式の使用は、接頭語 # を F または D の引数タイプへ追加すると、指定できます。例えば、"i#D" は整数の入力引数 1 つと、64 ビット浮動小数点出力引数 1 つに引数リストを指定します。
NULL で終了する文字列を C リンク・タイプで渡す
このリンク・タイプは、InterSystems IRIS が NULL ($CHAR(0)) 文字を含む文字列を送信しないことがわかっている場合にのみ使用します。このデータ型を使用する際は、C 関数は、文字列が長い場合でも、InterSystems IRIS によって渡される文字列を最初の NULL 文字で切り捨てます。例えば、文字列 "ABC"_$CHAR(0)_"DEF" は、"ABC" に切り捨てられます。
C データ型 | Input | In/Out | メモ |
---|---|---|---|
char * | 1c または c | 1C または C | これは、一般的な C NULL 終了文字列です。 |
unsigned short * | 2c または w | 2C または W | これは、C 形式の NULL 終了 UTF-16 文字列です。 |
wchar t * | 4c | 4C | これは、wchar_t 要素のベクトルとして保存される C 形式の NULL 終了文字列です。 |
数値文字列を返すために 3 つすべてのリンク・タイプを使用する短いコールアウト・ライブラリを以下に示します。
以下の 3 つの関数それぞれは、ランダム整数を生成して最大 6 桁の数値文字列に変換し、C リンクを使用してその文字列を返します。
#define ZF_DLL // Required when creating a Callout library.
#include <iris-cdzf.h>
#include <stdio.h>
#include <wchar.h> // Required for 16-bit and 32-bit strings
int get_sample(char* retval) { // 8-bit, null-terminated
sprintf(retval,"%d",(rand()%1000000));
return ZF_SUCCESS;
}
int get_sample_W(unsigned short* retval) { // 16-bit, null-terminated
swprintf(retval,6,L"%d",(rand()%1000000));
return ZF_SUCCESS;
}
int get_sample_H(wchar_t* retval) { // 32-bit, null-terminated
swprintf(retval,6,L"%d",(rand()%1000000));
return ZF_SUCCESS;
}
ZFBEGIN
ZFENTRY("GetSample","1C",get_sample)
ZFENTRY("GetSampleW","2C",get_sample_W)
ZFENTRY("GetSampleH","4C",get_sample_H)
ZFEND
短い計算文字列を B リンク・タイプで渡す
iris-cdzf.h コールアウト・ヘッダ・ファイルは、短い文字列 (インターシステムズの従来の文字列型) を表す計算文字列構造 ZARRAY、ZWARRAY、および ZHARRAY を定義します。これらの構造は、文字要素の配列 (それぞれ、8 ビット、16 ビット Unicode、または 32 ビット wchar t) および配列内の要素の数を指定する short 整数 (最大値は 32,768) を含みます。以下はその例です。
typedef struct zarray {
unsigned short len;
unsigned char data[1]; /* 1 is a dummy value */
} *ZARRAYP;
以下はその説明です。
-
len — 配列の長さが格納されます。
-
data — 文字データを格納する配列です。要素タイプは、ZARRAY は unsigned char、ZWARRAY は unsigned short、ZHARRAY は wchar_t です。
B リンクは、3 つの配列構造に対応して、ポインタ型 ZARRAYP、ZWARRAYP、および ZHARRAYP を指定します。返される配列の最大サイズは 32,767 文字です。
C データ型 | Input | In/Out | メモ |
---|---|---|---|
ZARRAYP | 1b または b | 1B または B | 8 ビットの最大 32,767 文字を含む短い各国語文字列。 |
ZWARRAYP | 2b または s | 2B または S | 16 ビットの最大 32,767 文字を含む短い Unicode 文字列。 |
ZHARRAYP | 4b | 4B | 最大 32,767 の wchar_t 文字を含む短い Unicode 文字列。 |
引数の最大合計長さは、1 文字あたりのバイト数に依存します (“$ZF ヒープの構成” を参照)。
数値文字列を返すために 3 つすべてのリンク・タイプを使用するコールアウト・ライブラリを以下に示します。
以下の 3 つの関数それぞれは、ランダム整数を生成して最大 6 桁の数値文字列に変換し、B リンクを使用して文字列を返します。
#define ZF_DLL // Required when creating a Callout library.
#include <iris-cdzf.h>
#include <stdio.h>
#include <wchar.h> // Required for 16-bit and 16-bit characters
int get_sample_Z(ZARRAYP retval) { // 8-bit, counted
unsigned char numstr[6];
sprintf(numstr,"%d",(rand()%1000000));
retval->len = strlen(numstr);
memcpy(retval->data,numstr,retval->len);
return ZF_SUCCESS;
}
int get_sample_ZW(ZWARRAYP retval) { // 16-bit, counted
unsigned short numstr[6];
swprintf(numstr,6,L"%d",(rand()%1000000));
retval->len = wcslen(numstr);
memcpy(retval->data,numstr,(retval->len*sizeof(unsigned short)));
return ZF_SUCCESS;
}
int get_sample_ZH(ZHARRAYP retval) { // 32-bit, counted
wchar_t numstr[6];
swprintf(numstr,6,L"%d",(rand()%1000000));
retval->len = wcslen(numstr);
memcpy(retval->data,numstr,(retval->len*sizeof(wchar_t)));
return ZF_SUCCESS;
}
ZFBEGIN
ZFENTRY("GetSampleZ","1B",get_sample_Z)
ZFENTRY("GetSampleZW","2B",get_sample_ZW)
ZFENTRY("GetSampleZH","4B",get_sample_ZH)
ZFEND
複数の値を含む出力引数文字列ではコンマが区切り文字として使用されます。コンマは計算文字列配列の一部となるため、引数リストの 最後 にこれらの配列を宣言し、呼び出しごとに 1 つの配列を使用します。
標準的な計算文字列を J リンク・タイプで渡す
iris-callin.h ヘッダ・ファイルは、標準的な InterSystems IRIS 文字列を表す計算文字列構造 IRIS_EXSTR を定義します。この構造は、文字要素の配列 (8 ビット、16 ビット Unicode、または 32 ビット wchar t) および配列内の要素の数を指定する int 値 (文字列長の制限あり) を含みます。
typedef struct {
unsigned int len; /* length of string */
union {
Callin_char_t *ch; /* text of the 8-bit string */
unsigned short *wch; /* text of the 16-bit string */
wchar_t *lch; /* text of the 32-bit string */
/* OR unsigned short *lch if 32-bit characters are not enabled */
} str;
} IRIS_EXSTR, *IRIS_EXSTRP;
C データ型 | Input | In/Out | メモ |
---|---|---|---|
IRIS_EXSTR | 1j または j | 1J または J | 8 ビット各国語文字の標準的な文字列 |
IRIS_EXSTR | 2j または n | 2J または N | 16 ビット Unicode 文字の標準的な文字列 |
IRIS_EXSTR | 4j | 4J | 32 ビット wchar_t 文字の標準的な文字列 |
IRIS_EXSTR データ構造は、コールイン API から関数で操作します (低レベルの InterSystems 関数呼び出しのライブラリ。詳細は、"コールイン API の使用法" の “コールイン関数リファレンス” を参照してください。名前はよく似ていますが、コールイン API と $ZF コールアウト・インタフェースはまったく別の製品です)。
IRIS_EXSTR のインスタンスを作成および破棄するには、以下の関数を使用します。
-
IrisExStrNew[W][H] — 文字列に対して要求されたストレージの容量を割り当て、IRIS_EXSTR 構造に、構造の長さおよび構造の値フィールドへのポインタを埋め込みます。
-
IrisExStrKill — IRIS_EXSTR 文字列に関連付けられているストレージを解放します。
数値文字列を返すために 3 つすべてのリンク・タイプを使用するコールアウト・ライブラリを以下に示します。
以下の 3 つの関数それぞれは、ランダム整数を生成して最大 6 桁の数値文字列に変換し、J リンクを使用して文字列を返します。
#define ZF_DLL // Required when creating a Callout library.
#include <iris-cdzf.h>
#include <stdio.h>
#include <wchar.h>
#include <iris-callin.h>
int get_sample_L(IRIS_EXSTRP retval) { // 8-bit characters
Callin_char_t numstr[6];
size_t len = 0;
sprintf(numstr,"%d",(rand()%1000000));
len = strlen(numstr);
IRISEXSTRKILL(retval);
if (!IRISEXSTRNEW(retval,len)) {return ZF_FAILURE;}
memcpy(retval->str.ch,numstr,len); // copy to retval->str.ch
return ZF_SUCCESS;
}
int get_sample_LW(IRIS_EXSTRP retval) { // 16-bit characters
unsigned short numstr[6];
size_t len = 0;
swprintf(numstr,6,L"%d",(rand()%1000000));
len = wcslen(numstr);
IRISEXSTRKILL(retval);
if (!IRISEXSTRNEW(retval,len)) {return ZF_FAILURE;}
memcpy(retval->str.wch,numstr,(len*sizeof(unsigned short))); // copy to retval->str.wch
return ZF_SUCCESS;
}
int get_sample_LH(IRIS_EXSTRP retval) { // 32-bit characters
wchar_t numstr[6];
size_t len = 0;
swprintf(numstr,6,L"%d",(rand()%1000000));
len = wcslen(numstr);
IRISEXSTRKILL(retval);
if (!IRISEXSTRNEW(retval,len)) {return ZF_FAILURE;}
memcpy(retval->str.lch,numstr,(len*sizeof(wchar_t))); // copy to retval->str.lch
return ZF_SUCCESS;
}
ZFBEGIN
ZFENTRY("GetSampleL","1J",get_sample_L)
ZFENTRY("GetSampleLW","2J",get_sample_LW)
ZFENTRY("GetSampleLH","4J",get_sample_LH)
ZFEND
前述の例では、IRISEXSTRKILL(retval) は、メモリから入力引数を削除するために常に呼び出されています。これは、引数が出力に使用されない場合でも常に実行する必要があります。実行しない場合は、メモリ・リークが発生します。
従来の短い文字列の $ZF ヒープの構成
このセクションは、従来の短い文字列 (“短い計算文字列を B リンク・タイプで渡す” を参照してください) にのみ適用されます。標準的な InterSystems IRIS 文字列 (“標準的な計算文字列を J リンク・タイプで渡す” を参照してください) は、独自のスタックを使用します。
$ZF ヒープは、すべての $ZF の短い文字列入力および出力パラメータに割り当てられる仮想メモリ領域です。これは、以下の InterSystems IRIS システム設定によって制御されます。
-
ZFString は、1 つの文字列パラメータに対して使用が許可される文字数です。このために実際に必要なバイト数は、使用している文字が 8 ビット文字か、16 ビット Unicode 文字か、UNIX® 上の 32 ビット文字かによって異なります。この設定で許可されている範囲は、0 から 32767 文字です。既定は、0 であり、最大値を使用することを示します。
-
ZFSize は、InterSystems IRIS がすべての $ZF 入力および出力パラメータに割り当てる合計バイト数です。この設定で許可されている範囲は、0 から 270336 バイトです。0 (既定の設定) は、InterSystems IRIS が ZFString の値に基づいて適切な値を計算することを示します。
以下のように ZFSize (合計バイト数) を ZFString (文字列あたりの最大文字数) に基づいて計算します。
ZFSize = (<bytes per character> * ZFString) + 2050
例えば、ZFString の値が既定の 32767 文字だとします。
-
Unicode 16 ビット文字を使用すると、ZFSize の適切な値は (2 * 32767 + 2050) = 67584 バイトになります。
-
UNIX® 32 ビット文字を使用すると、ZFSize の適切な値は (4 * 32767 + 2050) = 133118 バイトになります。
これらの設定は、以下のいずれかの場所で変更できます。
-
構成パラメータ・ファイル ("構成パラメータ・ファイル・リファレンス" の “[config]” セクションの “zfheap” を参照してください)。
-
管理ポータル ("追加構成設定リファレンス" の “詳細メモリ設定” にある ZFSize および ZFString のエントリを参照してください)。
互換性のある言語とコンパイラ
$ZF コールアウト・インタフェースを使用して、外部言語で関数を記述し、それらを ObjectScript から呼び出すことができます。コールアウト・ライブラリは、通常は C で記述されますが、ご使用の C コンパイラで認識される呼び出し規則を使用するその他のコンパイル言語で記述される場合もあります。互換性に関する 2 つの問題が発生します。1 つ目は、コンパイラは C と互換性があるアプリケーション・バイナリ・インタフェース (ABI) を使用する必要があることです。2 つ目は、コンパイラは InterSystems IRIS と互換性がないランタイム・ライブラリ機能に依存しないコードを生成する必要があることです。
インターシステムズは、すべてのプラットフォーム上で InterSystems IRIS を生成するために使用するのと同じ C コンパイラの使用をサポートします。
プラットフォーム | コンパイラ |
---|---|
IBM AIX | AIX 用 IBM XL C |
Mac OS X (Darwin) | Xcode |
Microsoft Windows | Microsoft Visual Studio |
Linux (系列すべて) | GNU プロジェクト GCC C |
ほとんどのプラットフォームは、標準化されたアプリケーション・バイナリ・インタフェース (ABI) を備え、ほとんどのコンパイラに互換性があります。Intel x86-32 および x86-64 プラットフォームは主な例外であり、これらのプラットフォームには、複数の呼び出し規則が存在します。これらのプラットフォームでの呼び出し規則については、(https://en.wikipedia.org/wiki/X86_calling_conventionsOpens in a new tab) を参照してください。
多くの C コンパイラでは、外部ルーチンに対して異なる呼び出し規則を宣言することを許可しています。適切な呼び出し規則を宣言する C ラッパ・ルーチンを記述することによって、別の言語で記述されたルーチンを呼び出すことができます。
コールアウト・ライブラリの Runup 関数と Rundown 関数
InterSystems コールアウト・ライブラリは、共有オブジェクトがロードされたとき (runup) またはアンロードされたとき (rundown) に呼び出されるカスタム内部関数を含むことができます。どちらのケースも引数は渡されません。関数は以下のように使用されます。
-
ZFInit — コールアウト・ライブラリが $ZF(-3)、$ZF(-4,1)、または $ZF(-6) によって最初にロードされたときに呼び出されます。この関数からの返りコードは、エラーがないことを示すゼロ、もしくはいくつかの障害の存在を示すゼロ以外の値になります。呼び出しが正常に実行されると、ZFUnload rundown 関数のアドレスが保存されます。
-
ZFUnload — コールアウト・ライブラリが $ZF(-3) への呼び出しによってアンロードまたは置換されたとき、あるいは $ZF(-4,2) または $ZF(-4,4) によってアンロードされたときに呼び出されます。プロセスの停止では呼び出されません。エラーが rundown 関数実行中に発生した場合、それ以降、コールアウト・ライブラリのアンロードを許可するための呼び出しは無効となります。ZFUnload からの返り値は現在無視されています。
コールアウト・ライブラリを構築する際、リンクを確立するプロシージャの間に、ZFInit と ZFUnload 記号を明示的にエクスポートする必要がある場合があります。
トラブルシューティングとエラー処理
このセクションでは、以下の項目について説明します。
-
ワースト・プラクティス — 深刻な問題を引き起こす可能性のあるプラクティスをいくつか示します。
-
UNIX® シグナル処理のエラーの処理 — プロセスがシグナルを受信したときに生じる可能性のある失敗したシステム呼び出しから回復するのに役立ついくつかの関数について説明します。
ワースト・プラクティス
ほぼすべてのルーチンは $ZF コールアウト・インタフェースで呼び出すことができますが、$ZF コールアウト・インタフェースは数学関数で使用するのが最適です。また、InterSystems IRIS I/O でうまく処理されない外部デバイスへのインタフェース、または InterSystems IRIS インタフェースが存在しないシステム・サービスの場合にも効果的に使用することができます。
以下の動作によって重大な問題が発生する可能性があります。
-
自身に属していないメモリへのアクセス
メモリ・アクセス違反は、InterSystems IRIS によって処理され、InterSystems IRIS 内のバグとして扱われます。
-
トラップによって処理されるその他のエラーの発生
トラップによって処理されるエラー (ほとんどのプラットフォーム上でのゼロによる除算など) も InterSystems IRIS 内のバグとして扱われます。
-
プロセスの優先度の変更
InterSystems IRIS は、InterSystems IRIS を実行する他のプロセスと対話する必要があります。優先度を下げると、上げるのと同様に問題が発生する場合があります。例えば、プロセスが CPU を解放する直前にスピン・ロックで保護されたリソースを取得することを考えてみましょう。優先度が低すぎると、高い優先度のその他のプロセスがリソースを奪い合い、事実上、スピン・ロックを解除できるようにするプロセスの実行を妨げます。
-
中断のマスク
インタロックを実装するために中断を短時間マスクする場合がありますが、中断をマスクしたままにしないように十分注意する必要があります。
-
削除できないリソースを作成または開く
ファイルを開いて malloc を使用してメモリを割り当てることは問題ありません。それらのリソースはプロセスの終了時に閉じられるかまたは解放されます。2 番目のスレッドを作成すると、2 番目のスレッドが InterSystems IRIS プロセスの終了前に正常に終了することを保証できないため、2 番目のスレッドは作成しないでください。
-
opaque 以外のオブジェクトを ObjectScript 以外のコードで返す
コード内のメモリのブロックを malloc して、それを読み取るために $VIEW(address,−3,size) を使用できると考えないでください。また、ObjectScript 以外のコードに malloc ブロックを戻さないでください。コードは opaque ハンドルを返し、後で opaque ハンドルを受け取った時に使用する前に有効であることを確認する必要があります。
-
プロセスの終了
exit だけを呼び出すことはしないでください。常に ZF_SUCCESS または ZF_FAILURE のいずれかと共に返します (これらの値の実装は InterSystems IRIS プラットフォーム間で異なることに留意してください)。
-
exec のいずれかのバージョンの呼び出しによる終了
子プロセス内で fork してから exec を呼び出すことはできますが、必ず親は InterSystems IRIS に返り、子プロセスは InterSystems IRIS に返らないようにしてください。
-
プロセスでのエラー処理の振る舞いの変更
Windows とは異なり、UNIX® システムでは、現在の内部フレームのローカル・エラー処理のみを確立できます。
UNIX® シグナル処理のエラーの処理
UNIX® および関連するオペレーティング・システムで実行中の場合、システム呼び出しの中には、open、read、write、close、ioctl、および pause などのシグナルをプロセスが受け取ると、失敗するものがあります。関数がこれらのシステム呼び出しを使用する場合、実際のエラー、Ctrl-C、再起動を必要とする呼び出しと区別できるように、コードを記述する必要があります。
以下の関数により、非同期のイベントの確認や $ZF でのアラーム・ハンドラの設定ができます。関数の宣言は、iris-cdzf.h に組み込まれます。
int sigrtclr(); — 再試行フラグを消去します。sigrtchk() を使用する前に一度呼び出す必要があります。
int dzfalarm(); — 新規 SIGALRM ハンドラを構築します。
$ZF にエントリする際に、前のハンドラが自動的に保存されます。終了すると、自動的にリストアされます。ユーザ・プログラムで、他のシグナル処理を変更しないでください。
int sigrtchk(); — 非同期イベントをチェックします。これは、open、close、read、write、ioctl、または pause のいずれかのシステム呼び出しが失敗した場合や、プロセスがシグナルを受け取ったときに失敗する呼び出しがある場合に、必ず呼び出す必要があります。ユーザの次の動作を示すコードを返します。
-
-1 — シグナルではありません。入出力エラーを調べてください。errno 変数の内容を参照してください。
-
0 — 他のシグナルです。割り込みが生じた時点から処理を再開してください。
-
1 — SIGINT/SIGTERM。SIGTERM "return 0" で $ZF を終了します。これらのシグナルは適切にトラップされます。
一部のデバイスの制御に使用される一般的な $ZF 関数は、以下の擬似コードと同様のロジックを使用します。
if ((fd = open(DEV_NAME, DEV_MODE)) < 0) {
Set some flags
Call zferror
return 0;
}
プロセスがシグナルを受け取ると、open システム呼び出しは失敗する可能性があります。通常、これはエラーではないため、処理を再開できます。しかし、シグナルによっては他の処理が必要な場合もあります。すべての可能性を考慮するために、以下の C コードを使用することを検討してください。
sigrtclr();
while (TRUE) {
if (sigrtchk() == 1) return 1 or 0;
if ((fd = open(DEV_NAME, DEV_MODE)) < 0) {
switch (sigrtchk()) {
case -1:
/* This is probably a real device error */
Set some flags
Call zferror
return 0;
case 0:
/* A innocuous signal was received. Restart. */
continue;
case 1:
/* Someone is trying to terminate the job. */
Do cleanup work
return 1 or 0;
}
}
else break;
}
/*
Code to handle the normal situation:
open() system call succeeded
*/
dzfalarm() を呼び出して新しい SIGALRM ハンドラを確立する場合を除き、エラー処理コードでシグナルの処理を変更しないでください。