% 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_handler
Lisp_abort_Begin
Lisp_abort_End
lisp_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
へと遷移しているため、
デストラクタが無視されているのがわかります。