liboctave を使っていると,例えば要素数の異なる列ベクトルを足そうとしたときに
fatal: operator +: nonconformant arguments (op1 len: 2, op2 len: 5)
というエラーが発生し,プログラムが正常終了する.正常終了とは exit(1) による終了で,
- コアファイル (core) がダンプされないのでバックトレースできない
- このためどの関数が operator+ を呼び出してエラーを発生させたのかわからない
という問題があり,デバッグしにくい.そこで, liboctave のエラーハンドラを自分で定義して,エラーが発生したときの振舞いを自分で決められるようにしよう.
liboctave のエラーハンドラを設定するには, octave/lo-error.h で定義されている set_liboctave_error_handler という関数を使う.この関数の宣言は
typedef void (*liboctave_error_handler) (const char *, ...);
CRUFT_API extern void set_liboctave_error_handler (liboctave_error_handler f);
となっていて,自分で定義した関数を f に指定することで, liboctave のエラーハンドラを自分で定義できる.
デフォルトのエラーハンドラは(おそらく)libcruft/misc/lo-error.cで定義されている liboctave_fatal 関数のようで,この定義をソースから抜粋すると,
00048 static void
00049 verror (const char *name, const char *fmt, va_list args)
00050 {
00051 if (name)
00052 fprintf (stderr, "%s: ", name);
00053
00054 vfprintf (stderr, fmt, args);
00055 fprintf (stderr, "\n");
00056 fflush (stderr);
00057 }
00086 void
00087 liboctave_fatal (const char *fmt, ...)
00088 {
00089 va_list args;
00090 va_start (args, fmt);
00091 verror ("fatal", fmt, args);
00092 va_end (args);
00093
00094 exit (1);
00095 }
となっている. va_list, va_start, va_end は「可変引数リスト」を扱うための型と関数群 (cstdarg で定義されている), verror は lo-error.c で定義されている関数だ. liboctave_fatal を自分で書き直して, set_liboctave_error_handler で指定しよう.
具体的には,lexit: デバッグを効率化する終了関数で紹介した lexit 終了関数を使って,バックトレースが表示されるようにする:
#include <libhoge.h> // lexit が定義されているヘッダ
#include <cstdlib>
#include <cstdarg>
#include <iostream>
#include <octave/config.h>
#include <octave/dColVector.h>
namespace some_namespace // 適当な名前空間 (lexit と同じ)
{
static void verror (const char *name, const char *fmt, va_list args)
{
if (name)
fprintf (stderr, "%s: ", name);
vfprintf (stderr, fmt, args);
fprintf (stderr, "\n");
fflush (stderr);
}
void liboctave_error (const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
verror ("liboctave fatal", fmt, args);
va_end (args);
lexit(btfail); // バックトレースを出力し,終了
}
}; // end of some_namespace
テストしてみよう:
using namespace std;
using namespace some_namespace;
int main(int argc, char**argv)
{
set_liboctave_error_handler (&liboctave_error);
ColumnVector x(2,1.0), y(0);
cout<<(x+y)<<endl;
return 0;
}
このプログラムでは x と y の要素数が異なるから, x+y の演算に失敗する.このときプログラムは,
liboctave fatal: operator +: nonconformant arguments (op1 len: 2, op2 len: 0) ------ the program is terminated at octave-errorhandler.cpp:36: within the function: void some_namespace::liboctave_error(const char*, ...) backtrace: ./a.out(_ZN12some_namespace7_exitlv7__lexitENS0_10TExitLevelEiPKcS3_+0x611)[0x804aec1] ./a.out(_ZN12some_namespace7_exitlv6_lexitENS0_10TExitLevelEiPKcS3_+0x36)[0x804a694] ./a.out(_ZN12some_namespace15liboctave_errorEPKcz+0x4a)[0x8049fdd] /usr/lib/octave-3.0.1/liboctave.so(_Z19gripe_nonconformantPKcii+0x38)[0xb7afb0b8] /usr/lib/octave-3.0.1/liboctave.so(_ZplIdE6MArrayIT_ERKS2_S4_+0x116)[0xb7a86766] ./a.out(_ZplRK12ColumnVectorS1_+0x26)[0x804a634] ./a.out(main+0x64)[0x8049e64] /lib/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb74c6455] ./a.out(_ZNSt8ios_base4InitD1Ev+0x3d)[0x8049d11]
というエラーを出力する.バックトレースを調べれば,どの関数でエラーが発生したのかが分かり,デバッグがしやすくなるだろう.もちろん, lexit(btfail) の代わりに lexit(abort) を使ってコアファイルをダンプさせることで,より深いデバッグも可能だ.