% LISP ABORT
nptのドキュメントです。
参照元:ANSI Common Lisp npt
前へ:関数の登録
次へ:脱出の操作
LISP ABORTとは、システムエラーによる強制終了のことです。
C言語のexit関数を終了コード1で呼び出し、プロセスを終了させます。
もし手動でabortを起こしたい場合は、次の関数を実行します。
lisp_abort();
実行結果は次のようになります。
$ ./a.out
**************
LISP ABORT
**************
$ echo $?
1
$
実行例のように、LISP ABORTという文言が出力された場合は、
プロセスが強制終了されたことを意味します。
確認している通り、そのときの終了コードは1です。
終了時にメッセージを出力したい場合は下記の関数を使用します。
void lisp_abortf(const char *fmt, ...);
void lisp_abort8(const void *fmt, ...);
void lisp_abort16(const void *fmt, ...);
void lisp_abort32(const void *fmt, ...);
名前に8, 16, 32が付いている関数は、lisp_string8_関数でメッセージを作成します。
formatを経由するため、Common Lispが動作している必要があります。
lisp_abortfは、引数にprintfの書式を受け取ります。
formatを経由しないため、Common Lispが動作している必要はありません。
ただしprintfの引数にLispのオブジェクトを指定することはできません
(できたとしても%pくらいだと思います)。
lisp_abortの挙動はハンドラーを登録することで変更できます。
例えば、下記のハンドラーを設定してみます。
void test_handler(void)
{
printf("Hello Handler.\n");
}
ハンドラーの登録はlisp_set_abort_handler関数にて行います。
例えば次のようになります。
lisp_set_abort_handler(test_handler);
lisp_abort();
実行結果を下記に示します。
$ ./a.out
Hello Handler.
**************
LISP ABORT
**************
$
ハンドラーが起動されたあと、LISP ABORTが発生して
プロセスが強制終了したことが分かります。
ハンドラーを登録したとしても、
そのまま終了した場合はLISP ABORTが実行されます。
LISP ABORTの捕捉LISP ABORTは、setjmpを使うことでプロセスを終了させることなく
処理を継続させることができます。
下記の命令を用いることで、捕捉と継続ができます。
lisp_set_abort_setjmp_handlerLisp_abort_BeginLisp_abort_Endlisp_set_abort_setjmp_handlerは、補足用のハンドラーを設定します。
Lisp_abort_BeginとLisp_abort_Endは、捕捉処理を囲むためのマクロです。
LISP ABORTを補足する例を下記に示します。
int main(void)
{
lisp_set_abort_setjmp_handler();
Lisp_abort_Begin {
printf("Start\n");
lisp_abort();
printf("End\n");
}
Lisp_abort_End;
printf("Return\n");
return 0;
}
実行結果は下記のとおりです。
$ ./a.out
Start
Return
$
実行結果ではStartとReturnが表示されており、
Endが表示されていないのがわかります。
lisp_abort関数によりabortがが発生して、
処理がLisp_abort_Endまでジャンプしたためです。
上記の方法だと、処理が普通に終わったものなのか、
あるいはLISP ABORTが発生したものなのか区別がつきません。
そこで、次のように変更します。
void main_call(void)
{
printf("Start\n");
lisp_abort();
printf("End\n");
}
int main(void)
{
int finish;
lisp_set_abort_setjmp_handler();
finish = 0;
Lisp_abort_Begin {
main_call();
finish = 1;
}
Lisp_abort_End;
if (finish == 0)
printf("Lisp Abort\n");
return 0;
}
変数finishを用意し、Lisp_abort_Beginの最後で
値が変更されていたら正常終了したとみなします。
実行結果を下記に示します。
$ ./a.out
Start
Lisp Abort
$
LISP ABORTのハンドラーはsetjmpが使われますが、
C++でコンパイルした場合はtry/catchに変更されます。
変更する理由はsetjmpであればデストラクタが起動しないためです。
例として下記の文をC++で実行してみます。
class destruct
{
public:
destruct() { printf("Constructor\n"); };
~destruct() { printf("Destructor\n"); };
};
void main_call(void)
{
destruct x;
printf("Start\n");
lisp_abort();
printf("End\n");
}
int main(void)
{
int finish;
lisp_set_abort_setjmp_handler();
finish = 0;
Lisp_abort_Begin {
main_call();
finish = 1;
}
Lisp_abort_End;
if (finish == 0)
printf("Lisp Abort\n");
return 0;
}
実行例は下記のとおりです。
$ ./a.out
Constructor
Start
Destructor
Lisp Abort
$
この例ではsetjmpではなくtry/catchが使われているため、
デストラクタが起動しているのが確認できます。
このような挙動を好ましく思わない場合は、
コンパイル時にLISP_ABORT_SETJMPをdefineすることで、
C++コンパイルでもsetjmpを使用することができます。
上記の例文をsetjmpで実行した結果を下記に示します。
$ ./a.out
Constructor
Start
Lisp Abort
$
lisp_abort関数が実行されてると
そのままLisp_abort_Endへと遷移しているため、
デストラクタが無視されているのがわかります。