リスト6−1 ダミー関数 |
1 win_bell() 2 { 3 fprintf( stderr, "win_bell() is not supported\n" ); 4 } 5 6 Notify_value notify_next_event_func() 7 { 8 fprintf( stderr, "notify_next_event_func() is not supported\n" ); 9 } 10 11 pw_batch() 12 { 13 fprintf( stderr, "pw_batch() is not supported\n" ); 14 } 15 16 Event *canvas_window_event() 17 { 18 fprintf( stderr, "canvas_window_event() is not supported\n" ); 19 } |
ダミー関数は、とりあえず関数がないとリンクできず、デバッグを進められないときなどに必ず 必要になります。プログラムが大きくなると、全ての関数を用意してからテストということはなく、 開発の初期には、ダミー関数の山でしょう。だから、用意するダミー関数の数が何十個にもなるこ とは珍しくありません。そのようなとき、リスト1のように、関数名以外は全く同じ関数を繰り返 して書くことは退屈な、ばかばかしい作業です。
当然、マクロの出番です。マクロは、短い文字列の置換だけでなく、行末を\(¥)で継続する ことで、何行にもわたる長いマクロが定義できます。
ここでは、マクロの引数にダミー関数名を与えることで、
MakeDummyFunction(win_bell)と書くだけでダミー関数が1つ作れるようにします。マクロの置換は、単なる文字列の置換で、C 言語の文法に従って置換する訳ではありませんから、文字列中にも仮引数と一致する文字列がある と置換されます。これで、多数のダミー関数があっても、リスト6−2のようにすればあっという 間に作れるでしょう。
「せこい」と言われようが、こういう技巧も心得ていないと開発に余分な時間がかかります。
リスト6−2 マクロで大量生産したダミー関数 | |
1 #define MakeDummyFunction( type, fn ) \ 2 type fn() \ 3 { \ 4 fprintf( stderr, #fn "() is not supported\n" ); \ 5 } 6 7 MakeDummyFunction( int, win_bell ) 8 MakeDummyFunction( Notify_value, notify_next_event_func ) 9 MakeDummyFunction( int, pw_batch ) 10 MakeDummyFunction( Event *, canvas_window_event ) |
リスト6−3 # 行のあるソース |
1 caddr_t menu_get( menu_obj, op ) 2 IN myMenu_object *menu_obj; 3 IN Menu_attribute op; 4 { 5 va_list ap; 6 caddr_t value = NULL; 7 8 #ifdef DEBUG 9 printf( " >>> enter menu_get\n" ); 10 #endif 11 12 if( ! menu_obj ) { 13 printf( " menu_get() .. menu_object is NULL\n" ); 14 goto ending; 15 } 16 17 switch( TYPE_OF_MENU_OBJECT( menu_obj ) ) { 18 case MENU_OBJECT_MENU: 19 value = smenu_get_menu( (myMenu*)menu_obj, op ); 20 break; 21 case MENU_OBJECT_MENU_ITEM: 22 value = smenu_get_item( (myMenu_item*)menu_obj, op ); 23 break; 24 } 25 ending: 26 27 #ifdef DEBUG 28 printf( " <<< exit menu_get\n" ); 29 #endif 30 return value; 31 } |
また、#ifdefと#endifで囲むのが面倒だし、どうせすぐに消すからと、printfまたはfprintfな どで直接書いてしまうことも多いでしょう。
こういうとき、ヘッダーファイル中に、
#ifdef DEBUG #define DebugPrint( message ) fprintf( stderr, message ) #define DebugPrint2( f, a, b ) fprintf( stderr, f, a, b ) #else #define DebugPrint( message ) #define DebugPrint2( f, a, b ) #endifと書いておけば、#ifdef,#endifがなくなり、はるかに読みやすくなります。単純なメッセージは DebugPrintを、データを伴うものはDebugPrint2を使ってみてください。
#ifdefと#endifがいっぱい出てくると、読みづらくてたまりません。これは、C言語特有の字下 げの効果を破滅させるからです。長いプログラム中に、#ifdefと#endifが頻繁に出てくるのは、た とえデバッグ中でもいやなものです。上の方法では、デバッグライトが字下げを妨害しません。こ の効果はリスト6−4に出ています。プログラムが長くなると、もっともっと差が歴然としますが、 それは想像してください。
リスト6−4 # 行を取り除いたソース |
1 caddr_t menu_get( menu_obj, op ) 2 IN myMenu_object *menu_obj; 3 IN Menu_attribute op; 4 { 5 va_list ap; 6 caddr_t value = NULL; 7 8 DebugPrintf( " >>> enter menu_get\n" ); 9 10 if( ! menu_obj ) { 11 DebugPrint( " menu_get() .. menu_object is NULL\n" ); 12 goto ending; 13 } 14 15 switch( TYPE_OF_MENU_OBJECT( menu_obj ) ) { 16 case MENU_OBJECT_MENU: 17 value = smenu_get_menu( (myMenu*)menu_obj, op ); 18 break; 19 case MENU_OBJECT_MENU_ITEM: 20 value = smenu_get_item( (myMenu_item*)menu_obj, op ); 21 break; 22 } 23 24 ending: 25 DebugPrint( " <<< exit menu_get\n" ); 26 return value; 27 } |
#define IN #define OUT #define INOUTと定義しているため、プリプロセッサでマクロ展開されると無くなってしまい、何も書かなかった のと同じ、つまりコメントと同様になります。コメントは、/* と */ に囲まれているので、邪魔 くさい感じがします。C言語の予約語にみえるようにするためには、in, out, inout と小文字に することもできます。
何かに変換するだけがマクロの使い道ではありません。「無」への置換はいろいろ応用範囲が広 いので覚えておいてください。