今回のプログラムの特徴の1つは、類似パターンがいっぱい出てくることです。類似パターンは、 エディタでコピーし、ちょっと変更するとできるので、とりあえずの作成時間は短く、リストもど んどん長くなるので、作業が進んでいるように感じるでしょうが、後で「泣き」をみます。
リスト6−11 深いcase |
1 case PANEL_CHOICE_STRINGS: 2 if ( item->win_type == MY_PANEL_CHOICE ) { 3 len = -1; size = 10; 4 while ( (ctemp = va_arg( ap, char* )) != 0 ) { 5 len ++; 6 if( len == 0 ) { 7 pointer->panel_choice_strings = 8 (char**) malloc ( size 9 * sizeof(char*) ); 10 } 11 if( len+2 > size ) { 12 size = size * 2; 13 old = pointer->panel_choice_strings; 14 pointer->panel_choice_strings = 15 (char**) realloc( old,size 16 * sizeof(char*) ); 17 } 18 *((pointer->panel_choice_strings)+len) = (char*) 19 malloc( (strlen(ctemp)+1)*sizeof(char) ); 20 strcpy( *((pointer->panel_choice_strings)+len), 21 ctemp ); 22 } 23 len++; *((pointer->panel_choice_strings)+len) = NULL; 24 item->attr = (caddr_t) pointer; 25 } 26 else 27 while ( va_arg( ap, char* ) != 0 ) ; 28 break; 29 case PANEL_CHOICE_FONTS: 30 if( item->win_type == MY_PANEL_CHOICE ) { 31 len = -1; size = 10; 32 while ( ftemp = va_arg( ap, myPixfont* ) ) { 33 len++; 34 if( len == 0 ) 35 pointer->panel_choice_fonts = 36 (myPixfont**) malloc( 37 size*sizeof(myPixfont*)); 38 if( len + 2 > size ) { 39 size *= 2; 40 oldf = pointer-> 41 panel_choice_fonts; 42 pointer->panel_choice_fonts = 43 (myPixfont**) realloc( 44 oldf, size*sizeof( 45 myPixfont*)); 46 } 47 *(pointer->panel_choice_fonts+len) = 48 ftemp; 49 } 50 len++; 51 *(pointer->panel_choice_fonts+len) = NULL; 52 } 53 else 54 while ( va_arg( ap, myPixfont* ) != NULL ); 55 break; 56 case PANEL_TOGGLE_VALUE: 57 pointer = (myPanel_Item_Choice*) item->attr; 58 if( pointer->panel_toggle_val == NULL ) { 59 pointer->toggle_size = 5; 60 pointer->panel_toggle_val = (int*) 61 malloc( pointer->toggle_size* 62 sizeof(int) ); 63 pointer->toggle_index = (int*) 64 malloc( pointer->toggle_size* 65 sizeof(int) ); 66 pointer->toggle_count = 0; 67 } 68 else if ( pointer->toggle_count >= 69 pointer->toggle_size ) { 70 pointer->toggle_size += pointer->toggle_size; 71 itempa = pointer->panel_toggle_val; 72 pointer->panel_toggle_val = (int*) realloc( 73 itempa, pointer->toggle_size*sizeof(int) ); 74 itempa = pointer->toggle_index; 75 pointer->toggle_index = (int*) realloc( itempa, 76 pointer->toggle_size*sizeof(int) ); 77 } 78 itemp = va_arg( ap, int ); 79 *(pointer->toggle_index + pointer->toggle_count) = 80 itemp; 81 itemp = va_arg( ap, int ); 82 *(pointer->panel_toggle_val + pointer->toggle_count) = 83 itemp; 84 pointer->toggle_count++; 85 break; |
どんどん字下げのネストが深くなってどうしようもないプログラムを見ることがあります。必要 以上にネストが深くなるのは、if文による枝分かれで、特にエラーチェックのときに深くなるよう です。
深くならない「こつ」の一つが、if文でエラーの場合を先に処理することです。エラー処理の最 後にbreakなどを置くことで流れが途切れるので、正常時の処理は字下げが不要になります。本来の 目的の処理の前で、エラーチェックがすべて行われると、以降は正常処理だけに専念すればよいの で、楽にもなります。修正版のリスト6−13では、異常なときの可変引数の読み飛ばしをしてい ます。
3つのcaseの中をながめると、データを入れる領域を確保するためにmalloc、確保した領域内に 収まらなくなったときにreallocを使っていますが、3つとも似た使い方をしているので、共通化 が考えられます。最初の2つのcase中のif文で細工をしている領域はそれぞれ1つですが、最後の は、panel_toggle_valとtoggle_indexが指す2つの領域があります。これらをまとめて面倒をみよ うとして書いた関数がリスト6−12のunit_allocです。ここでは、領域がまったく確保されてい ないことの判定をcount==0で行っています。reallocでは、同じポインタを2回使いますが、ポイ ンタの記述が長くなってくると、プログラムがごちゃごちゃしてきます。そのため、ポインタのア ドレスを渡しています。領域のサイズは、chgsizeというフラグを設けて、真のときだけ変更され るようになっているので、複数の領域を確保するときは、最後だけTRUEで呼び出しています。もち ろん、1つしか領域を確保しないときもTRUEで呼び出します。
元の3つのreallocのときのif文の判定は同一ではありませんが、ゆとりのある方を採用するこ とで共通化しています。領域がデータ1個分余分になりますが、大したことではありません。プロ グラムがすっきりする方が優先されるべきですね。
リスト6−12 共通関数 | |
1 /********************************************************************************/ 2 /* 文字列を入れる領域を確保し、文字列をコピーする */ 3 /********************************************************************************/ 4 char *alloc_strcpy( str ) 5 IN char *str; 6 { 7 char *ptr; 8 9 ptr = malloc( strlen(str) + 1 ); 10 strcpy( ptr, str ); 11 return ptr; 12 } 13 14 /********************************************************************************/ 15 /* 1ユニット分の領域を追加確保する */ 16 /********************************************************************************/ 17 void unit_alloc( pptr, chgsize, size, count, initcount, unitcount ) 18 INOUT void **pptr; /* allocした領域 */ 19 IN int chgsize; /* sizeの値は変更されるか */ 20 INOUT int *size; /* 領域の大きさ */ 21 IN int count; /* 領域内の実データ個数 */ 22 IN int initcount; /* 初期allocのデータ個数 */ 23 IN int unitsize; /* データ1個分のバイトサイズ */ 24 { 25 int n = 0; 26 27 if( count == 0 ) { 28 n = initcount; 29 *pptr = malloc( n * unitsize ); 30 } else if( count+2 > *size ) { 31 n = *size * 2; 32 *pptr = realloc( *ptr, n * unitsize ); 33 } 34 if( chgsize && n>0 ) 35 *size = n; 36 } |
文字列を入れるためにぴったりのサイズの領域をmallocで確保し、その中に文字列をコピーする ことは、プログラム全体のあちこちで行われていました。簡単なことですが、3つもの関数 strlen, malloc, strcp を使っているので、長い同じ変数が2回現れても嫌になります。これを行 う関数を、リスト6−12にalloc_strcpyとして入れています。単純ですが、多数出現しているし、 関数にしてしまうと、ずいぶんすっきりしてきます。
2つの関数を作ったことで、ぐちゃぐちゃしていたものが、リスト6−13のように、短く、すっ きりしました。
sizeof(char)がありますが、これは1に決まっているので、省略する方がすっきりします。
リスト6−13 浅いcase | |
1 case PANEL_CHOICE_STRINGS: 2 if ( item->win_type != MY_PANEL_CHOICE ) { 3 while ( va_arg( ap, char* ) != 0 ); 4 break; 5 } 6 for( size=10,len=0; ctemp=va_arg( ap, char* ); len++ ) { 7 my_alloc( &pointer->panel_choice_strings, TRUE, &size, 8 len, 10, sizeof(char*) ); 9 pointer->panel_choice_strings[len] = alloc_strcpy(ctemp); 10 } 11 pointer->panel_choice_strings[len] = NULL; 12 item->attr = (caddr_t) pointer; 13 break; 14 case PANEL_CHOICE_FONTS: 15 if( item->win_type != MY_PANEL_CHOICE ) { 16 while ( va_arg( ap, myPixfont* ) != NULL ); 17 break; 18 } 19 for( len=0; ftemp=va_arg( ap, myPixfont* ); len++ ) { 20 unit_alloc( &pointer->panel_choice_fonts, TRUE, &size, 21 len, 10, sizeof(myPixfont*) ); 22 pointer->panel_choice_fonts[len] = ftemp; 23 } 24 pointer->panel_choice_fonts[len] = NULL; 25 break; 26 case PANEL_TOGGLE_VALUE: 27 pointer = (myPanel_Item_Choice*) item->attr; 28 initsize = 5; 29 30 unit_alloc( &pointer->panel_toggle_val, FALSE, &pointer->toggle_size, 31 pointer->toggle_count, initsize, sizeof(int) ); 32 pointer->toggle_index[pointer->toggle_count] = va_arg( ap, int ); 33 34 unit_alloc( &pointer->toggle_index, TRUE, &pointer->toggle_size, 35 pointer->toggle_count, initsize, sizeof(int) ); 36 pointer->panel_toggle_val[pointer->toggle_count] = va_arg( ap, int ); 37 38 pointer->toggle_count++; 39 break; |