2011年3月1日火曜日

C言語で書くならマクロを活用しようぜ

ひょんなことから、今C言語(正確にはOracleのPro*C)でプログラム書いてまして、この10日間で4500ステップほど書きました。
それが多いのか少ないのかわからないけど、8年ぶりにTeraTerm上のviオンリーでmakeしながら書いてるんで、この6年ぐらい営業とかマーケとかマネージャーチックなことをやっていたボクにはなかなかの達成感があるボリュームです。

書いてるのは、既存のシステムに新たに追加するマスター更新系のバッチ処理。
プログラムと言えばすっかりJavaに慣れてしまったので、「参考にしろ」と言われた数々の「お手本」の、1000ステップぐらいある「main」処理が気持ち悪くて仕方ない…。

「このwhileの閉じ括弧は、いったいどこ・・・」
「このifに対するelseってどれ・・・」みたいな。

で、ちょっとでも可読性とメンテナンス性を良くしようと意味のある単位で関数にぶった切って、なるべく処理を移譲するイメージに整えて「うむ、なかなかの出来栄え!」と思ったけど、やっぱり手続き型に慣れた目にはズラーっとダラダラ書いた方が読みやすいっぽい。んー、反省。


ただ、「え、こんなこと出来んの?」と、ボクが新人だった頃に既に使い古されていたマクロ・テクに新鮮な驚きを示した方がいて、おぉ、まさかこんなので驚かれるとは・・・と思いつつ、ひょっとしたら知って得する方もいるかもしれないので、C言語ならではの便利な記述方法を。

マクロ関数

C言語では、例えばソース内で扱う定数なんかを

#define MSG_OK 0
#define MSG_NG 9

みたいな感じで定義しますよね?
で、実際の処理の部分では以下のように書きますよね?

if( ret == MSG_OK ){
//OKの処理
}

こう書いておくと、コンパイル時のプリプロセスにおいて定義したものが展開されて、こんな風になってからコンパイルされるわけですね。

if( ret == 0 ){ //←MSG_OKが 0 に置き換えられる
//OKの処理
}


この仕組みを利用すると、関数っぽいのもマクロで定義できて、それをマクロ関数なんて言ったりします。たぶん。

よくUNIXサーバーで動いている業務系システムには、もうやたらめったらmemsetしているシーンが多々見受けられます。こんな感じ。

memset( strA, 0x00, sizeof(strA));
memset( strB, 0x00, sizeof(strB));
memset( strC, 0x00, sizeof(strC));
memset( strD, 0x00, sizeof(strD));

こういう、決まったパターンがある場合はマクロ関数がお勧めです。
ソースの先頭の方で以下のようにマクロ関数を定義します。

#define INIT_STRING(A) memset(A, 0x00, sizeof(A));

これを使うと、上記のmemsetは以下のようになります。

INIT_STRING(strA);
INIT_STRING(strB);
INIT_STRING(strC);
INIT_STRING(strD);

こうすると、見た目にもすっきりする他、よくありがちな memset( strA, 0x00, sizeof(strB)) みたいな、うっかりコピペミスによる潜在バグを防止することもできます。


また、Pro*Cの場合だと、SELECTして取ってきた値をバインド変数に突っ込む、という処理があると思いますが、恐らくインジケータ変数を見て、正常に取得できていたらバインド変数から他の変数にコピーする、なんていうこともよくやるんじゃないでしょうか?

EXEC SQL
SELECT USERID, USERNAME
INTO :UserId :ind_UserId, :UserName :ind_UserName
FROM USERTBL
WHERE USERID = 11;

if(ind_UserId == 0){
strcpy(l_uid, UserId);
}
if(ind_UserName == 0){
strcpy(l_unm, UserName);
}


もうお分かりだと思いますが、上記の strcpy 部分は、以下のようなマクロ関数を使うとちょっとスッキリします。

#define GET_DB_VALUE(A, B, C) if(A==0)strcpy(B,C);


元のソースのif~部分は以下のように書き換えることができます。

GET_DB_VALUE(ind_UserId, l_uid, UserId);
GET_DB_VALUE(ind_UserName, l_unm, UserName);


ちょっと見やすくなりましたね?ね?

こんな感じで、定型的なパターンがあって何度も同じように書かないといけない場合は、マクロ関数を使ってみてはいかがでしょうか。



って、いつの時代の話だよ!と突っ込みたくなりますが。
もっと知りたい方は、ググるとたくさん出てきますので調べてみてください。

ちなみに、何とかC言語でもJavaっぽい例外処理を書けないか、と思って調べたら、同じような悩みを抱えつつマクロとsetjmp()を使ってそれっぽいことをされている方も結構いたんですが、やっぱり呼び出しスタックの上の階層までExceptionの型を指定してthrowするような、ステキ例外機構を実現されている方はいないようでした(当たり前)。
やっぱり例外処理機構って、イケてるなぁ・・・と思いました。

#しかし、Javaなのに手続き型で書かれたプロジェクトがあったと思ったら、今度はモロに手続き型。まだまだ世の中、そんなシステムもたくさんあるんだね。

0 件のコメント: