APIエラーのパターン

UCIDを使ってAPIトレーサを生成するにあたって、エラーのハイライト機能を付けたいと考えた。APIのエラー表現にはいくつかの類型が有り、これらをシステムとしてサポートすることで、UCID記述自体はコンパクトにすることができる。
(今のところ、UCIDはC++ APIを表現することができないため、C++例外を扱うことができない。)

エラーコードが存在しない

そもそも、エラーコードが存在しないAPIがそれなりの数存在する。代表的なものとしてはmalloc()ファミリがある。mallocは失敗するとNULLを返すが、何故失敗したのかを示すようなエラーは特に規定されていない。
また、安全に失敗しない関数もここに含まれる。C標準ライブラリ関数の殆どは安全に失敗しないため、エラーコードも存在しない。

発生したエラーを別途取得する(errno, GetError)

OSインターフェースはerrnoスタイルのエラー表現を使うことが多いように見える。つまり、APIが失敗すると失敗を示す値を返し、別途エラーコードを取得することになる。
POSIXのsystem interfaceのエラーはerrno変数へのアクセスで取得できる。errno変数は実際にはスレッドローカルなerrnoのアドレスを求める関数に展開されることが多く、スレッド毎に異なるインスタンスを持つ。
C言語浮動小数点サポートと、C標準ライブラリの数学関数、文字列関数はOSインターフェースでないがerrnoを使用する。
Win32の場合は、GetLastError APIによってほぼ同様の機能を提供している。ただし、Win32の場合は成功時にエラーをクリアするのに対して、errnoは決して(システムとしては)クリアされないという違いがある。

If the function is not documented to set the last-error code, the value returned by this function is simply the most recent last-error code to have been set; some functions set the last-error code to 0 on success and others do not.

The value of errno should only be examined when it is indicated to be valid by a function's return value. No function in this volume of POSIX.1-2008 shall set errno to zero.

一般的なライブラリAPIもこのスタイルのエラー表現を行うものは多い。例えばSDLにはSDL_GetError関数が有る( http://wiki.libsdl.org/SDL_GetError )。
この手の手法は普及しているが、問題も有る:

  • エラーの発生した呼び出しと、取得できるエラーコードの対応関係が曖昧になる
  • エラーコード読み出しのオーバーヘッド

OpenGLはglGetErrorによって最後に発生したエラーを取得することができるが、パフォーマンスクリティカルな場所での使用は使用しないことが良しとされている(これはGLにおけるGet関連全般に言える...)。これらのオーバーヘッドがあるため、APIトレースにおいてエラーを記録するかどうかは絶妙な問題となる。

戻り値でエラーコードを返す

WindowsにおけるCOMスタイルのAPIは、エラーをHRESULT( http://en.wikipedia.org/wiki/HRESULT )で通知する。HRESULTはエラーコードの他にファシリティコードや失敗/成功フラグ等を持つ。
他のライブラリでは、例えばlibusb1がこの形式のエラーを提供する。

Most libusb functions return 0 on success or one of these codes on failure. You can call libusb_error_name() to retrieve a string representation of an error code or libusb_strerror() to get an end-user suitable description of an error code.

この方式の問題点は、APIがオブジェクトを返せない点と言える。つまり、Bool値等エラー以外のものを返すAPIでは直接利用できない。このような特殊ケースを許すとAPIが覚えづらくなる。libusbの場合、データ転送とデバイス列挙はlibusbのエラーコードを返すが、構造体へのアクセスAPIは構造体の値そのものを返す。