Cでのエラーハンドリング方法
かなり久しぶりの更新です。
仕事が忙しかったですし、そのあとはイベント続きや体調を崩したりで色々時間がなかったので...。
結局今年はプライベートで進めているプロジェクトをまだリリース出来ておらず、辛いです。
さて、この記事を読んでつらつらどうやっていたかを書こうかと思います。
Cのエラーハンドリングと例外設計、例外処理のメモ - 百日半狂乱
最近はCはさほど触らないのですが、これは難しい問題ですよね。
そももそも昔からCのコードのかなりの部分がエラーハンドリングのコードになるということは言われていて、コード量が増える原因の1つになってますね。
ラッパー関数
まずはラッパー関数を作成する方法です。
私がよく使うのはmalloc系のラッパーのxmallocです。
これは昔からよく使われるテクニックです。
void *xmalloc(size_t size) { void *tmp; tmp = malloc(size); if (tmp == NULL) { fprintf("malloc(3) error"); exit(1); } //例えば0クリアもしておく。callocは使いづらいので... memset(tmp, 0, size); return tmp; }
状況にもよりますが、mallocに失敗するとexitしたり、さらに成功の場合はmemsetで0クリアをする場合もあります。
組み込みのファームですとexitするのはまずいので、こういうのはやりませんが。そもそも組み込みだとmalloc自体使えないこともありますけどね。
xreallocも作ります。
reallocの場合失敗するとNULLを返しますが、元のポインタ領域はそのままなので初心者だと変数をそのまま使うバグを作ることはよくありますね。
例えばこのように実装します。
void *xrealloc(void *ptr, size_t size) { void *ret; ret = realloc(ptr, size); if (ret == NULL) { // 例えばバグ回避のためにptrを返す return ptr; //別の方法だと、ptrを開放しておいてNULLを返す free(ptr); return NULL; } return ret; }
いくつか実装は考えられますが、このようにしておくとバグが防ぎやすくなります。
ラッパー関数は単にエラー処理の単純化だけでなく、よくセットで行う処理を同時に行ったりバグを防ぐ事を目的の場合に役に立ちます。
goto
次に悪名高きgotoです。Cの教科書だとよく機能としてはあるけどバグの原因なので本によっては使い方を書かないというのを見たこともあります(どの本かまでは覚えてませんが...)。
例えばこういうのが悪い使い方として紹介されていたりします。
TOP: //なんか処理 if (x == hoge) { goto TOP; }
言語に関係なく、基本的に処理は上から下へという前提があるので(ループとかもあるけど...)、いくらなんでもこういう方法を取ることはないでしょうが、下手な人が使うとバグの原因になり兼ねないのはわかります。
ただ、エラー処理においては非常に優秀でぜひ覚えるべきテクニックでしょう。
char *hoge() { int ret; char *tmp; tmp = xmalloc(1024); //なんか処理 ret = func0(tmp); if (ret < 0) { goto ERROR; } //なんか処理2 ret = func1(tmp); if (ret < 0) { goto ERROR; } //この後も処理は続く... //成功時 return tmp; ERROR: // エラー時 free(tmp); return NULL; }
エラーの場合、その場でreturnを行う方法もありますが上記のように失敗時にfreeが必要となるとエラー時に毎回freeを書く必要があります。
gotoを使うだけで、エラー処理が1つになり、また関数の出口も1箇所に集まるため処理が非常に楽になりバグが少なくなります。
プログラミングにおいて楽をするのは正義です。下手くそなエンジニアが書くとどんな言語でも無理に頑張って根性で乗り切る人がいますがそれは間違いです。
楽をしてバグを少なくしましょう。そもそも楽をするためにコンピュータを使っているのに、めんどくさくしてバグを増やすなんてもってのほかです。
setjmpなどのテクニックもありますが、これは使いドコロが難しいですね。
今では古くなったサイトですがせめてこのあたりの知識くらいは身につけましょう。
Super Technique 講座〜目次
(コ)の業界のオキテ
Cプログラミング診断室