C++例外処理のススメ
Java をはじめとするいくつかの言語は、実行時に発生したエラーを処理する仕組みとして、例外処理機構を採用しています。
例外処理を使うと、戻り値でエラーを検知・処理する手法よりも確実に行うことができる上、コードの記述も簡潔になります。
C++ もこの例外処理を採用していますが、Java のそれと比べても明らかに普及していません。
その理由としては、
- 前身である C から、従来のエラー処理手法 (戻り値で検出) が尾を引いている。
- Java の Exception のような、標準の例外型が存在しない。
- 例外の補足・処理が強制されない。(Java では throws キーワードを使用してこれを強制する。)
などが考えられます。
これらのうち、1. 2. を解決すべく、Java に負けじと C++ 版 Exception を作成してみました。
※本エントリ内のファイルの公開を終了しました。
これを使えば、従来よりも簡単かつ便利に例外処理を行うことができます。
Exception を用いたコーディングの例を以下に示します。
(ファイル hoge.txt を piyo.txt にコピーするプログラムです。)
#include "narita/Exception.h" #define BUF_SIZE 256 typedef const char* LPCSTR; void CopyFile(LPCSTR lpszFilenameSrc, LPCSTR lpszFilenameDst); FILE* OpenFile(LPCSTR lpszFilename, LPCSTR lpszMode); //エントリポイント int main(){ try { CopyFile("hoge.txt", "piyo.txt"); } catch (Exception& e){ e.Print(stderr); } return 0; } //ファイルのコピー void CopyFile(LPCSTR lpszFilenameSrc, LPCSTR lpszFilenameDst){ FILE* fpSrc =NULL; FILE* fpDst =NULL; try { fpSrc =OpenFile(lpszFilenameSrc, "rb"); fpDst =OpenFile(lpszFilenameDst, "wb"); for (;;){ char acBuf[BUF_SIZE]; int nRead =fread(acBuf, 1, BUF_SIZE, fpSrc); if (!nRead){ if (::ferror(fpSrc)) throw Exception::create(__FILE__, __LINE__, "Read error occured."); else break; } int nWrite =fread(acBuf, 1, nRead, fpDst); if (nWrite < nRead){ throw Exception::create(__FILE__, __LINE__, "Write error occured."); } } //for (;;) fclose(fpSrc); fclose(fpDst); return; } catch (Exception& e){ if (fpSrc) fclose(fpSrc); if (fpDst) fclose(fpDst); throw e.Relay(__FILE__, __LINE__); } } //ファイルを開く FILE* OpenFile(LPCSTR lpszFilename, LPCSTR lpszMode){ FILE* fp =fopen(lpszFilename, lpszMode); if (!fp){ throw Exception::createf(__FILE__, __LINE__, "File '%s' cannot be opened.", lpszFilename); } return fp; } |
ファイル hoge.txt が開けない場合、実行結果は次のようになります。
File 'hoge.txt' cannot be opened. /home/narita/test.cpp(70) /home/narita/test.cpp(61) |
このように、エラーの詳細をメッセージ (文字列) として関数の呼び出し元に通知することができます。
また、例外の発生位置およびスローされてきた経路 (スタック履歴) を参照することも可能です。
これらの機能は、デバッグの際に大きな助けとなること間違いなし。
皆様もぜひご活用ください。
担当: 成田 (例外スロア)