NULL - みる会図書館


検索対象: 月刊 C MAGAZINE 1993年5月号
24件見つかりました。

1. 月刊 C MAGAZINE 1993年5月号

学 五ロ ルトの処理を示すボタンは黒い線て、囲まれ ます。このデフォルトのボタンはマウスて、 クリックする代わりにリターンキーて、代用 て、きます。安全な動作をデフォルトのボタ たとえば工デイタ ンに設定してください て、文書を変更して Quit しようとすると , Fi g. 3 のようなダイアログを表示します。変更 したデータを失わないためにはセープする 必要があります。そこて、デフォルトボタン は Yes ( セープする ) になっています。 なお , ボ、タン , ラジオボタン , チェック ポックスなどは , コントロールと呼ばれ , コントロールマネージャによって管理され ます。 ーダイアログの表示 ダイアログを表示するためには NewDiaI og あるいは GetNewDialog を使います。 Ne wDialog はリソースを使わないて、 , アプリケ ーションの中から直接ダイアログを生成し ます。これは表示する座標を指定したり , ダイアログアイテムのリストを作成しなけ ればならなかったりて、 , かなり繁雑な処理 となります。よって , 今回は使用しません。 GetNewDialog は Fig. 4 のようにプロトタ イプ宣言されています。この関数 ( 正確には システムコール ) は , 指定されたダイアログ のリソース ( リソースタイプは ' DLOG ' ) を読 み込んて、ダイアログの内部データを初期化 します。このときにダイアログレコードの アドレスや NULL を dStorage に与えます。 NULL を渡した場合は , ツールポックスが アプリケーションのヒープ領域にダイアロ グレコードを作成します。 behind はダイア ログをウインドウのリストのどの位置に挿 入するのかを指定します。一番手前の位置 に挿入するには , (void * ) ー 1 と指定します。リターンされる値はダイア ログレコードへのポインタて、す。ダイアロ グの作成に失敗すると NULL を返します。 GetNewDialog< ダイアログが作成される と同時に表示されるようにリソースを設定 することがて、きます。アプリケーションが Fig. 2 代表的なダイアログアイテム 〇ラジオボタン 1 〇ラジオボ・タン 2 〇ラジオボタン 5 ロチェックポックス 1 ロチェックポックス 2 スタティックテキスト 偏隼可能子キュト Fig. 3 テフォルトボタンの例 Save changes before closing ? Yes Cancel NO Fig. 4 GetNewDialog と DisposDialog DiaIogPtr GetNewDiaIog(short dlgRsrcID, Ptr dStorage, WindowPtr behind) ; dIgRsrcID ダイアログのリソース ( タイプは ' DLOG ' ) の ID ダイアログレコードのアドレス dStorage NULL をあたえるとツールポックスが作成する どのウインドウの下に表示するかを指定する behind ー 1 のときは一番上に表示する ダイアログレコードのアドレス リターン void DisposDiaIog(DialogPtr theDialog); theD i a log 解放するダイアログレコードのアドレス ダイアログの生成と解放 #define DialogID 1000 / * 'DLOG' リソースの * / DialogPtr dptr; dptr = GetNevDiaIog(DialogID, NULL, (void *)-1); ShovWindow(dptr) : / * ここでダイアログの処理をする * / HideWindow(dptr) : DisposDiaIog(dptr); List 1 C 言語雑学講座 119

2. 月刊 C MAGAZINE 1993年5月号

List 7 1 18 : 120 : 121 : 122 : 1 2 3 : 124 : 125 : 126 : 128 : 129 : 130 : 131 : 132 : 133 : 134 : } 135 : 137 : 138 : 139 : 140 : 141 : 142 : 143 : } 1 4 4 : 146 : 147 : 148 : 149 : 150 : 1 5 1 : 1 5 2 : 153 : 154 : 1 5 5 : 156 . 159 : { 160 : 1 6 1 : 162 : 163 : 164 : 165 : 166 : 167 : } 168 : 169 : 170 : { 171 : 172 : 173 : 174 : 175 : 176 : } 178 : 179 : 180 : 181 : 182 : 183 : 184 : 185 : 186 : 188 : 189 : 190 : 191 192 : 194 : { 195 : 196 : } 197 : 198 : / * 199 : 200 : 201 : 203 : 204 : 205 : 206 : 207 : 208 : 209 : 210 : 21 い return (funcp-t)st3; case Exponent: return (funcp-t)NULL; return (funcp-t)st4; statiC funcp-t st4(charKind-t kind) switch (kind) { case S ign: return (funcp-t)st5; case D i g i t : return (funcp-t)st6; return (funcp-t)NUL し : 136 : static funcp-t st5(charKind-t kind) switch (kind) { case D i g i t : return (funcp-t)st6; return (funcp-t)NULL; 145 : static funcp-t st6(charKind-t kind) svitch (kind) { case EOS : return (funcp-t)ok; case White: return (funcp-t)st7; case D i g i t : return (funcp-t)st6; return (funcp-t)NULL; 158 : static funcp-t st7(charKind-t kind) svitch (kind) { case return (funcp-t)ok; case Whi te: return (funcp-t)st7; return (funcp-t)NULL; static funcp-t st8(charKind-t kind) svitch (kind) { case D i g i t : return (funcp-t)st9; return (funcp-t)NU しし static funcp-t st9(charKind-t kind) svitch (kind) { case Eos: return (funcp-t)ok; case White: return (funcp-t)st7; case Digit: return (funcp-t)st9; case Exponent: return (funcp-t)st4; return (funcp-t)NULL; 193 : static funcp-t ok(charKind-t kind) return (funcp-t)NULL; * 状態推移解釈ルーチン static int return 1 : return 0 : if ()t = (funcp-t (*)(charKind-t))NUL い st = (funcp-t (*)(charKind-t))st(kinds[*nptr]) : for ()t = s0; st ! = 0k; + + nptr) { funcp-t (*st)(charKind-t) : 202 : stateMachine(charKind-t *kinds, funcp-t s0(charKind-t), const char *nptr) ロ 技法 めには , もっと広いクラスを受理て、きるモ デルが必要になる。そのような場合には , 状態推移図をネストさせるという対策を施 すことがて、きるのだが , 本稿て、はこれ以上 は触れないことにする。 状態推移モデルの明白な弱点として , ソ ースを見ただけて、 , どのような処理を行お うとしているのかが一見て、は判然としない 点が指摘て、きる。 List 1 や List 3 て、あれば , ソースを注意深く読めば , だいたい何を意 図しているのかが理解て、きよう。 ところが , List 5 や List 6 て、はそうはいかないだろう。 したがって , このアプローチを用いる場合 には , 必ずドキュメントに状態推移図をつ けておくことが重要て、ある。それをしない と , このコードはもっとも保守がめんどう なコードということになりかねない 最後に ,List 5 や List 6 に見られるアプロ ーチを用いると , 処理速度的には不利にな る場合もあることも記憶しておいてほしい 状態推移部分は , 一種のインタブリタを形 成しているからて、ある。もっとも , 多くの アプリケーションて、はこのペナルティは許 容範囲内て、ある。 以上見てきたのは , ソフトウェア全体の 設計の話て、はなく , 単にひとつの関数の話 にしかすぎない。しかも処理的には比較的 簡単だと思われる浮動小数点数文字列のチ ェックプログラムなのて、ある。それなのに たったこれだけの関数にかぎっても , 実は さまざまなアプローチが可能て、ある。そし て , それぞれのアプローチには長所短所が 存在しており , 「こういった状況の下て、は , この方法が優れている」というようなことは いえても , 常にどれかがいちばん優れてい るとはいえないのが実状て、ある。 この多用性がプログラム設計の難しさの 原因にもなっているのだが , 裏を返せばデ ザイナーの腕の見せどころて、もある。した がって , よいソフトウェアを設計するには , いろいろなアプローチ法を知っておく必要 がある。 良きデザイナーになるためには日々の胼 特集 C プログラム が重要て、ある。 特集 c プログラム設計技法 57

3. 月刊 C MAGAZINE 1993年5月号

表示と消去をコントロールするときには , ShowWindow (theDiaIog) ・ HideWindow (theDiaIog) ・ のようにします。これらは , ウインドウマ ネージャのルーチンて、すが , ダイアログも ウインドウの一種て、すからこれらを使用す ることがて、きます。 アプリケーションがダイアログを必要と しなくなったとき , ダイアログレコードと して使用していたメモリを解放してやる必 要があります。これを実行するのが Dispos Dialog と CloseDialog て、す。どちらもダイア ログレコードへのポインタを受け取って , ダイアログが使用していたデータ領域を解 放します。このふたつのルーチンの違いは , ダイアログレコードそのものを解放するか どうかて、す。つまり GetNewDialog や New Dialog の dStorage として NULL を渡して作 成したときには DisposDiaIog を , NULL 以 外を渡したときには CIoseDiaIog を呼び出し ます。これらの呼び出しは , 間違えないよ うに十分に注意してください ダイアログの生成と解放の例を List 1 に示 します。 ーダイアログアイテムの処理 ダイアログアイテムはリソースタイプ 'D ITL' ( ダイアログアイテムリスト ) のリソー スとして定義します。各アイテムは定義の 順に 1 から番号が割り当てられていきます。 アプリケーションて、はこのアイテム番号を 使ってダイアログの処理を実行します。 ResEdit て、 Fig. 2 のダイアログアイテムの 番号を示したものが Fig. 5 て、す。この図て、は 各アイテムの領域が四角て、示され , 右肩に アイテム番号が表示されています。 こて、 注意してほしいのは , OK ボタンと Cance17É タンに 1 と 2 を与えている点て、す。ここて、 1 は デフォルトボタンて、す。そして 2 はキャンセ ルボタンとして使用されることが多いのて、 , ok と cancel という名前て、それぞれ 1 と 2 がす て、に定義されています。 ' DLOG' リソースにはそのダイアログが使 用するアイテムリスト ' DITL ' の ID が格納さ 120 C MAGAZINE 1993 5 Fig. 5 アイテムの番号 ロ DITL = 1000 作 om sample: 0 ラジオボタン 1 5 〇ラジオボタン 2 4 〇ラジオボタン 3 5 チェックポックス 1 ロチェックポックス 2 7 スタティックテキスト 集可能テキスト 8 9 ー亠 Fig. 6 ModaIDiaIog void M0daIDiaIog(ProcPtr fiIterProc, short *i temHit) : filterProc ダイアログの処理中に特別なイベント処理をする場合 , 処理関数へのポインタをあたえる 通常は NULL にする マウスクリックやキー入力などで選択されたダイアログ アイテムの番号が格納される itemHit ダイアログの処理 short itemHit; do { List 2 M0daIDiaIog(NUL し &itemHit); svitch (itemHit) { case Ok: / * 0K ボタンの処理 * / break, case cancel. / * Cance17t< タンの処理 * / break : / * その他の処理 * / } ⅶⅱ e ( i temH i t ! = ok & & i temH i t ! = cance I) :

4. 月刊 C MAGAZINE 1993年5月号

技法 ム グ ロ 集プ 特 O [ 開始 ] 指数文字 ( E または e ) の前には仮数部 , ある 動小数点数の定義を行ってみる。なお , いは数字の並びがきていなければならない こて、定義した構文は ANSIC ライプラリの a したがって , 3 , 4 番目も誤りてある。最後 tof などが許す構文に制限を加えたものて、あ に指数文字の後には ( 省略可能な符号に引き る。 C がソースの中に記すことを許す「浮動 続いて ) 数字の並びがなければならない。だ 定数 (floating-constant) 」の構文とは , サフ から 5 番目も間違いて、ある。 ィックスを許さない一方て、符号を許す点な こまて、考えると , そろそろプロ どが違っているのて、注意。 さて , グラムを作れる人も出てくるかもしれない このように定義することて、 , 初めて誤解 どのような処理を思いつかれるて、あろうか。 の余地なく設計を行うことがて、きる。たと 以前の check int の構造をベースに改造を行 えば , 次のそれぞれが「正しい」のかどうか うという方針て設計を行うと , てきあがる は , このような定義に則して考えると明白 のは List 2 のようなプログラムかもしれな List 2 においては , 関数の大筋において . e10 check int と同じ構造になっている。すなわ e10 ち , 構造を疑似コードて、大ざっぱに表せば , 十 e10 次のようになるだろう。 1 . 2e [ 準備 ] これらはすべて「正しくない」 ちなみに ・数字読み込みカウントを 0 にする。 のて、ある。理由は , 次のとおりて、ある。小 ・小数点はまだ読み込んて、いないものとす 数点の前後の少なくとも一方には必ず数字 の並びがきていなければならない。したが ・指数部の読み込み中て、はないものとする。 って , 1 番目と 2 番目は間違っている。また , check doub 厄の実装 ( 第 2 案 ) ・文字列の先頭の空白類文字を読み飛ばす。 ・先頭に符号があれば , 読み飛ばす。 ・着目文字が , 数字 , 小数点 , 指数文字て、 あるあいだ , 以下の処理を行う。 数字て、あれば , 数字読み込みカウント を増やす。 小数点て、あれば , すて、に小数点を読み込んて、いれば 指数部読み込み中て、あれば工ラー さもなけば , 小数点を読み込んだ ことを記憶する。 指数文字て、あれば , すて、に指数部読み込み中て、あれば 数字読み込みカウントが 0 ならば工 フ 次の文字が符号て、あればそれを読 み飛ばす。 数字読み込みカウントを 0 にする。 指数部読み込み中て、あることを記 0 になる。 3 LiSt LiSt if ((nptr = check-digits(nptr)) return 0 : if (* 叩 tr = 十十 nptr; vhile (isdigit(*nptr)) 十十 nptr; 39 : 40 : 49 : 53 : 55 : 56 : 57 : 58 : 60 : 66 : 67 : $ifdef TEST 68 : i n t 69 : main(int argc, char **argv) if (argc く 2 ) return 0 ; ⅶⅱ e (* + + argv ! = NULL printf( ” XS = XdYn ” return 0 : 77 : $endif ニ NULL) 1 : $include く ctype. h> 2 : $include く stdio. h> 3 : * 1 個以上の数字を読み飛ばす。 5 : 6 : * 読み飛ばしに成功すれば、次の文字へのポインタを返す。 7 : * 失敗したら N 乢しを返す。 9 : static const char * 10 : check-digits(const char *nptr) if (!isdigit(*nptr)) return N 乢い do { 十 + nptr; } vhile (isdigit(* 叩 tr)); return nptr; 20 : / * 21 : * 浮動小数点数文字列のチェックを行う 23 : i n t 24 : check-double(const char *nptr) / * 空白があれば、それを読み飛ばす * / 26 : vhile (isspace(*nptr)) 27 : 十十 nptr; 29 : / * 符号があれば読み飛ばす * / 30 : if (*nptr = Ⅱ *nptr : 32 : 十十 nptr; 33 : / * 仮数部をチェックする * / 34 : if (*nptr ニ 35 : if ((nptr : check-digits( + + nptr)) 36 : 37 : return 0 : } else { 38 : / * 指数部があれば、それをチェックする * / i f (*nptr = 十十 nptr; if (*nptr : 十十 nptr, if ((nptr = check_digits(nptr)) = NULL) return 0 : / * 空白があれば、それを読み飛ばす * / vhile (isspace(*nptr)) 十十 nptr; / * 文字列の終わりに達したかチェックする * / if (*nptr = return 1 : return 0 : 'e' Ⅱ *nptr = Ⅱ *nptr = / * 最低 1 つは数字があるはず * / check-double(*argv) ) : *argv, : NULL) 特集 C プログラム設計技法 49

5. 月刊 C MAGAZINE 1993年5月号

タを取得 , 設定します。 それては以下て、各アイテムの処理の方法 を見ていきましよう (List 3 ) 。 ーチ = ックボックス チェックポックスは , ひとつひとつがオ ンやオフの状態を持ちます。ューザがチェ ックポックスをクリックすると , そのとき の状態がオンならばオフに , オフならばオ ンにしなければなりません。要するに 0 と 1 の反転て、す。これは 1 との排他的論理和を使 うことて、実現て、きます。具体的には List 3 の setCheckBox(67—77 行目 ) のようにしま す。 ラジオボタン 次にラジオボタンの処理を説明しましよ う。ラジオボタンはいくつかのボタンがひ とつのグループを形成します。その中て、た だひとつのボ、タンだけが選択された状態に ならなければなりません。つまり , Fig. 5 て、 はアイテム 3 から 5 はひとつのグループとな ります。 これらのうちのどれかひとつが選択され たときには , それまて、選択されていたアイ テムは選択されていない状態に設定し直さ なければなりません。 この処理は List 3 の setRadioButton ( 52 ~ 64 行目 ) のようにします。この関数はパラ メータにもらったアイテム番号のラジオポ タンを選択された状態 ( 1 ) にします。グルー プ内のそれ以外のラジオボ、タンは選択され ていない状態 ( 0 ) にします。 スタティックテキスト スタティックテキストはマウスクリック などのイベントを受け取らないように disab led になっています。また , 今回のプログラ ムて、は使用していませんが , テキストの中 に , , , が現れると , これらは関数 ParamText て、設定された文字列に置き換え られます。 122 C MAGAZINE 1993 5 List 3 Handle iHandIe; Rect iRect; &iType, &iHandle, &iRect); GetDItem(theDiaIog, Radi0ButtonlID, theltem : = RadioButtonIID); SetCtlValue((Contr01HandIe) iHandle, &iType, &iHandIe, &iRect) : GetDItem(theDiaIog, Radi0Button21D, theltem = = RadioButton21D) : SetCtlValue((ControlHandIe) iHandle, &iType, &iHandIe, &iRect) : GetDItem(theDiaIog, Radi0Button31D, theltem = RadioButton31D) : SetCtlValue((Contr01HandIe) iHandle, 55 : 58 : 62 : 63 : 64 : } 66 : 67 : void setCheckBox(DiaIogPtr theDialog, short theltem) short iType; Handle iHandIe; 70 : Rect iRect; short value; &iRect); GetDItem(theDiaIog, theltem, &iType, &iHandle, 74 : = GetCtlVaIue((Contr01HandIe)iHandle); 75 : value SetCtlValue((Contr01Handle) iHandle, value 76 : 79 : 80 : void doModaIDiaIog() DiaIogPtr dptr; 82 : short itemHit, Handle iHandIe; Rect iRect; 85 : short iType; 86 : dptr ニ GetNevDiaIog(DiaIogID, NULL, (void *)-l); 88 : 89 : GetDItem(dptr, UserItemID, &iType, &iHandle, &iRect); 90 : &iRect); SetDItem(dptr, UserItemID, iType, (void *)drawUserItem, SelIText(dptr, EditTextID, 0 , 32767 ) : 92 : setRadi0Button(dptr, Radi0ButtonIID); 93 : setCheckBox(dptr, CheckBox21D) : 95 : ShowWindow(dptr) ; 96 : 97 : M0daIDiaIog(NULL, &i temHit) : 98 : switch (itemHit) { 99 : case RadioButtonlID: 100 : setRadioButton(dptr, RadioButtonlID); 101 : 102 : break, case RadioButton21D: 103 : RadioButton21D); setRadi0Button(dptr, 10 4 : break; 105 : case RadioButton31D: 106 : RadioButton31D); setRadi0Button(dptr, 107 : break; 108 : case CheckBoxlID: 109 : setCheckBox(dptr, CheckBox11D) : 110 : break; 111 : case CheckBox21D: 112 : setCheckBox(dptr, CheckBox21D) : 113 : break; } while (itemHit ! = 0k & & itemHit ! = cancel); 1 1 6 : DisposDiaIog(dptr) : 119 : 120 : 121 : void handleAppleCh0ice(short item) 122 : { Str255 accName; 123 : int accNum; 124 : 125 : svi tch (item) { 126 : case AboutItemID: 127 : N0teAIert(AboutAlertID, NULL) : 128 : break, 129 : default: 130 : GetItem(AppIeMenuH, item, accName) : 131 :

6. 月刊 C MAGAZINE 1993年5月号

リ The ◆ List 1 たことになります。まだ Windows の API て、 ある CreateWindow( ) は呼ばれていないの て , 実際のウインドウはオープンしていま せん。あくまて、も , OWL の管理するデータ 構造の初期化が行われたにすぎません。て、 すから , TWindow の派生クラスのコンスト ラクタて、は , ウインドウハンドルて、ある HW indow メンバを参照する API は使えません。 こまて、の初期化が終わると MyApp : lnitlnstance ( ) は , MyApp : : MakeW indow ( ) を呼び出します。 MakeWindow ( ) は , 引数に与えられたインスタンスの Cr eate() を呼び出します。この MyWin: :C reate ( ) が , 実際に Windows API を呼び出 して画面上のウインドウを作成します。ウ インドウが作成されたら ( まだ画面上て、は可 視状態て、はありません ),Create() は MyW : SetupWindow() を呼び出します。 ln . Set 叩 Window ( ) は , MyWin の基本クラ スて、ある TWindowObject クラスの SetupW ind 。 w ( ) を呼び出します。このメンバ関数 は , 子供のウインドウが存在すればその Cr eate() を呼び出し , さらにその SetupWind ow ( ) を呼びます。 このようにして , ウインドウの階層がい くら深くとも , 再帰的に初期化および Setu p が行われるのて、す。 この一連の流れを List 2 に記述しておきま したのて、 , 参考にしてください さて , このように見ていくと , ウインド ウの大きさを確定するにはどうやら SetupW indow( ) メンバが適任のようて、す。 HeXM ain クラスて、 SetupWindow ( ) をオーバライ ドし , その中て、 MoveWindow( ) することに しましよう (SetupWindow て、は , 最初に TW indow : : SetupWindow ( ) を呼ぶことを忘 れないようにしましよう ) 。 ↑ー 00 ・ 0 ん せ ま 足 ー切」カ一 ーし Z ー ーし ーし ↑ー 0 ・ 0 1 人 0 ーし -4 ・ LO ー 8 00 0 1 ーっ 0 -4 ・ LO っ行ー 8 0 リ 0 ー 1 90 ワ 0 -4 ・ - ト 00 行ー 8 0 リ 0 1 より 0 CO ・ L-0 00 ー -0 LO LO LO L.O L.O っ :D れ 0 れ 0 CD CD 00 ーーーー行ーーーーー 8 8 8 8 8 8 8 8 ワ朝ワ 3 ワり乙ワ 90 ワワワ朝り 0 ワワ 0 り 0 り 0 ワ朝ワ 0 ワ 0 り 0 っ 0 り 0 っ朝ワ 3 り 0 ワ朝 93 ワ】り 0 ワ 3 ワ】り 0 ワ 0 りワ 0 ワ 0 EnableMenuItem(msg. WParam, CM-FILEINFORMATION, MF-BYCOMMAND ー (*fname ? MF-ENABLED . MF-GRAYED)) ; ニ CCM_FIRST + CM_ABOUT] virtual void CMAbout(RTMessage) GetM0duIe()- 〉 ExecDialog(new TDialog(this, "about")); void InitApplication() BWCCGetVersion() ; / / 確実に膸 CC. DLL をロードするため Att 「メンバのデフォルト値 ゞ structa CLASSTY PE TW i nd owAttt%{• ON ぃ WS.SYSMENUO WS$I THICK DWO RD style : PRAME,R WS MlNlMlZEBOX,o WS 笋 DWORD ExStyleV MIXIMIZEBOX の ORe ExStyle : 拡張スタイル。。デフォルトは 0 。 i{int% W,A••lt さ , Y : ウインドウの表示夸れる場所。左上の を LPSTR Menu : 点を指すいデブオルトは”が CW ー 。 in い d ; USEDEFAULTT, Windows のデフォル。。 、 LPSTR Param : トを選択することを示す気 W , H:- ウインドウの幅と高さ言デフォルト Style : ウインドウスタイルを格納する。 OWL 。。 t€CreateWindowEx()APl でウネン は , W が CW USEDEFAULT0 W が CW ~ USEDEFAULT の場合 , Windows は H ウを作成するときに使われるしコンス を無視する。一 ドラクタの ,parent 引数が NULL で MDIO ・ウインドウでない場合 ,üWS ー OVER Menu : NUL し擎駄。。, →は = : NULL LAPPEDWINDOW が設定されるよこれ を , は , WS OVERLAPPED, WS CAPTI 、一 Param : NULL& てことも , ソースコードを書き換えること 出てきましたが , もともとのアイディアは , ソースコード中のコーディング ( ハードコー なく可能になるわけて、す。リソースにはメ ディングとて、もいうのかな ) て、変更するの ニューだけて、なく , 文字列やダイアログテ は , 作成中および作成後の保守ともに面倒 ンプレート , ビットマップ , アイコンなど なのて , コードと分離て、きるデータは別に を持たせることがて、きます。 作成しておいて , リンク時にひとつの EXE リソースは , テキストエデイタて、記述し にまとめようということだと思います。メ てリソースコンノヾイラてコンノヾイルする ともて、きます。て、すが , ビジュアルなエレ ニューを英語から日本語に書き換えるなん ニュー作成 メニューは , リソースの中に入れておく ことがてきます。 唐突にリソースなどという新しい単語が Try The C 十十 101

7. 月刊 C MAGAZINE 1993年5月号

HeX プログラム List 作る いきなりソース 細部の記述を行う前に , プログラムの骨 格を作るのが常套手段て、す。 Windows70 ロ グラミングの場合 , プログラムの骨格とは ューザインタフェイスとも考えられます。 ー隹がいったのか忘れてしまいましたが , ひとつの警句があります。 まずユーザインタフェイスを作れ。さ もないと , データ処理部分の設計を 1 か らやりなおさなければならなくなるで あろう けだし名言て、はありませんか。 ということて、 , 本来ならば段階的に詳し くしていきたいところて、す。てすが , 何ぶ ん誌面はかぎられていますから , 完成した リストのみを示し , その要素要素を少しす つ解説していくことにしましよう (List 1 ) 。 1 : # i ncl ude く s td i 0. h> 2 : # i ncl ude く 0 ⅵ . h > 3 : # i ncl ude く d i r. h 〉 4 : # i ncl ude く s t r i ng. h > 5 : # i ncl ude く j s tr i ng. h > 6 : # i ncl ude く a Ⅱ oc. h> 7 : # i ncl ude く i 0. h > 8 : #include く 0 ⅵ rc. h 〉 9 : # i ncl ude く f ⅱ ed i a g. h 〉 10 : #pragma hdrstop 11 : #include く bwcc. h> 12 : #include ” res. h ” 13 : ” HeX ” 14 : #define PROGNAME 16 : inline char* strtail(char* p) return p 十 strlen(p); 20 : 21 : class WaitCursor { 22 : HCURSOR cursor; 23 : p ub ⅱ c : WaitCursor() 24 : 25 : 26 : cursor Wai tCursor() 28 . 29 : SetCursor(cursor) : 30 : 32 : } : 33 : 34 : #define WIDTH 74 35 : #define HEIGHT 10 36 : : pu b ⅱ c 物 i ndov { 37 : cl ass D i sp 38 : protected: char huge* buffer; 39 : 40 : long len; long yrange; 42 : int cxchar, 43 : int cychar; 4 4 : pub ⅱ c : Disp(PTWindows0bject parent, LPSTR name, PTModule module) 45 : TWindow(parent, name, module) 47 : Attr. Style ニ WS_CHILD ー WS_VISIBLE ー WS_BORDER ー WS_VSCROLL; 48 : (char huge*)farmalloc(l) : buffer ー / / farrealloc() に備えて 49 : 50 : len Scroller = nev TScroller(this, 0 , 0 , 0 , の : / / 仮に 52 : ScrolIer- 〉 AutoOrg = FALSE; 53 : 54 : protected: void GetTextInfo() 55 : 56 : HDC dc = GetDC(HWindow) : SeIectObject(dc, OEM-FIXED-FONT) : 58 : 59 : TEXTMETRIC tm, GetTextMetrics(dc, &tm) ; 60 : ReIeaseDC(HWindov, (c) : t 田 . tmAveCharWidth; 62 : cxchar tm. tmHeight 十 t 田 . tmExternalLeading + 2 ; 63 : cychar 64 : 65 : pub ⅱ c : int neededWidth() 66 : 67 : return cxchar * WIDTH + GetSystemMetrics(SM_CXVSCROLL) ; 68 : 69 : int neededHeight() 70 : return cychar * HEIGHT; int readlt(char* fname) 75 : WaitCursor wait; FILE* fp ー fopen(fname, if ()p = NULL) / / ファイルがない return FALSE; setvbuf(fp, NUL し _IOFBF, BUFSIZ * 16 ) : 80 : filelength(fileno(fp)); len (char huge*) farrealloc(buffer, len + 1 ) : buffer ー 82 : ニ NULL) { if (buffer 83 : : SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC-WAIT))) : インドウのオープン一 今回の HeX て、は , ウインドウのリサイズは行わない という方針て、す。これは , コンストラクタ て、 Attr というメンバ変数を設定することに より実現て、きます。 Attr は , OWL がウイン ドウを Create するときに参照する構造体 て、 , ウインドウのスタイルや大きさ , メニ ューなどの情報を格納て、きます 0TWindow クラスのコンストラクタは , 適当なデフォ ルト値を Attr に設定しています。よって , TWindow クラスの派生クラスて、は , 変更し たいものだけ設定すれば OK て、す。 HeX の場合 , Attr. StyIe & = WS THICKFRAME ; Attr. StyIe ー =WS BORDER ; Attr. StyIe & = WS MAXIMMIZEBOX ; によって , WS THICKFRAME をやめて W S BORDER( 細い実線の枠 ) にし , さらに最 大化ボタンとシステムメニューの最大化を 98 C MAGAZINE 1993 5

8. 月刊 C MAGAZINE 1993年5月号

Li st 7 えなければならない部分がきっちりと分か れているからて、ある。 さて , 状態推移モデルの実装は List 4 に見 られる , 掟破りの goto 多用型と , List 5 や L ist 6 て、使ったような表参照型の , いわば状 態推移行列インタブリタ型と , この 2 種類し かあり得ないのかというと , もちろんその ようなことはない。同じモデルに立脚しな がらも , 異なる実装というのはまだほかに もある。 たとえば , ひとつの関数の中て、 goto を使 って制御を移す代わりに , 各状態にひとつ ずっ関数を割り当てて , それぞれの関数は , 入力として与えられた文字の種類に応じて , 「次に呼び出すべき関数へのポインタ」を返 すように作ることがて、きる。 List 7 が , このアイデアに基づいてコーデ イングしたものて、ある。関数へのポインタ を扱うところて、 , C 言語の型システム上の破 綻から , キャストを多用せざるを得ず , し かもかなり難解なキャストもあるのが難点 て、ある ( 型システムの破綻とは何かについて は , 残念ながらここて、触れている余裕はな い ) 。しかし , とにかく goto は出てこないの て、 , goto 有害論者の目をごまかすには , のようなコーディングがあることを知って おくのも悪くないだろう。 List 7 が List 6 のアプローチに比べてメリ ットがあるとすれば , それは , 各ステート がそれぞれ独立した関数になっているため に , デバッグコードを挿入するなど , 特定 のステートて、だけ , 何か特別な処理をする ことが容易にて、きるということて、ある。 状態推移モデルは , その実装をどう行う かはさておき , 文字列の解析処理にかぎら ず , 一般にイベントが離散的に発生し , そ の発生順序に従って何らかの処理を行わな ければならないような場合には有効なアプ ローチてあるといえる。それはもっと巨大 なソフトウェア全体の状態推移かもしれな いし , もっとミクロな処理て、あるかもしれ ただし , 先に述べたように , 正規文法て、 表現不可能な場合には , それを解釈するた 56 C MAGAZINE 1993 5 static funcp_t ok(charKind_t) : 68 : { 24 : 28 : 30 : 32 : 33 : 36 : 37 : 38 : 39 : 40 : 43 : 45 : 49 : 50 : 52 : 53 : 56 : 57 : 58 : 59 : 62 : 63 : 65 : 66 : 67 : 69 : 75 : 77 : 84 : 85 : 86 : 88 : 89 : 90 : 92 : 95 : 96 : 97 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 105 : 106 : 107 : 108 : } 109 : 112 : 115 : 116 : kinds[i] = 0ther; ・ i く UCHAR-MAX + 1 : + + i ) for (i kindsC'%0'] = Eos; 34 : static V0id initKinds(charKind-t *kinds) * 文字種配列初期化関数 kinds[(unsigned char)*s + + ] = k ind; vhile ()s ! = ' \ 0 つ 25 : static V0id setKinds(charKind-t *kinds, char *s. charKind_t kind) setKinds(kinds, setKinds(kinds, setKinds(kinds, setKinds(kinds, setKinds()i nds, " 0123456789 ” , Digit); ”十一 Sign); P0int); ” eE", Exponent); ” %t%f%n ” , White) : * 各ステートに対する状態推移処理関数 typedef void *(*funcp-t) (charKind-t); funcp-t st0(charKind-t ki nd) static funcp-t st10(charKind-t) : static funcp-t st9(charKind-t): static funcp_t st6(charKind-t); static funcp-t st5(charKind-t); static funcp-t st4(charKind-t); static funcp-t st3(charKind-t) ; static funcp-t st0(charKind-t); 64 : static funcp-t stll(charKind-t); 61 : static funcp-t st8(charKind-t); 60 : static funcp-t st7(charKind-t); 55 : static funcp-t st2(charKind-t); 54 : static funcp-t stl(charKind-t); static return (funcp-t)st0; case Whi te: svitch (kind) { 93 : static funcp-t st2(charKind_t kind) return (funcp-t)NULL; return (funcp-t)st8; case Point: return (funcp-t)st2; case D i g i t : svitch (kind) { 82 : static funcp-t stl(charKind_t kind) return (funcp-t)NULL; return (funcp-t)st8; case Point: return (funcp-t)st2; case D i g i t : return (funcp-t)stl; case S ign ・ return (funcp-t)NULL; return (funcp-t)st4; case Exponent: return (funcp-t)st3; case Point: return (funcp-t)st2; case Digit: return (funcp-t)st7; case Whi te: return (funcp-t)ok; case EOS : svitch (kind) { 110 : static funcp-t st3(charKind-t kind) case D i g i t : return (funcp-t)st7; case Whi te: return (funcp-t)ok; case EOS : svitch (kind) {

9. 月刊 C MAGAZINE 1993年5月号

学 五ロ まれていなければなりません。しかし , 残 念ながらツールボ、ツクスは , この処理をし てくれません。つまりアプリケーションが アウトラインを描画しなければならないの て、す。この目的のためにユーザアイテムを 利用します。 ューザアイテムは , アプリケーションが 何て、も必要なものをダイアログアイテムと して使用て、きるように用意した特別なアイ テムて、す。ューザアイテムの場合 , GetDIt em, SetDItem のパラメータ iHandle はアイ テムを描画するルーチンのアドレスて、 , 描 画が必要なときはいって、もツールポックス から呼び出されます。この描画ルーチンは pascal 関数として定義されなければならない 点に注意してください。ツールボ、ツクスて、 は , この関数を PascaI て、記述されているも のと仮定しているからて、す。普通の C の関数 を指定すると , まず , 間違いなく暴走しま す。 これを使ってアウトラインを描くには次 のようにします。まず , ダイアログアイテ ムリストの中にユーザアイテムを追加しま す。このアイテムはデフォルトボタンとび ったり重なるように , 同じ座標を指定しま す。また , マウスクリックに反応しないよ うに disabled にしておきます。 アプリケーションがダイアログを表示す る前に , ユーザアイテムの描画ルーチンを 設定します。このルーチンは自分自身の座 標を取得し , その領域のわすかに外側に黒 いアウトラインを描きます。このアウトラ イベント処理が必要なときに処理関数への り , ウインドウにアイコンを描いたりする インの描画の具体的な方法は List 3 の draw ポインタを渡します。通常は NULL にして ために , アラート表示のための特別なルー UserItem(39—49 行目 ) を参照してくださ チンが用意されていることて、す。このルー おきます。 AIert はウインドウ内に ' DITL ' に用意さ チを Fig. 8 に示します : また , アラートを 表すリソースタイプは , ALRT' て、す。 ' D れたアイテムしか描きませんが , StopAler アラート t, CautionAlert, NoteAlert は Fig. 9 に示 LOG' と同じくアラートリソースの中にも対 したアイコンをウインドウ内の座標 ( 10 , 応するダイアログアイテムリストの ID が格 アラートはアプリケーションが実行中に 20 , 42 , 52 ) に描きます。したがって , これ 納されています。 発生したエラーを報告したり , 実行すると ら三つのルーチンを呼び出すときには , 使 Fig. 8 に示した関数は , 第レヾラメータに データを失うような危険な処理をユーザが 渡された ID の 'ALRT' リソースを使ってダ 用するアラートのダイアログアイテムをア 選択したときに , 本当に実行してよいのか イコンの領域に重ならないように注意して イアログを表示します。第 2 パラメータの fi 警告を与えるために使います。 配置しなければなりません。 IterProc は ModalDialog の第 1パラメータと 実は , アラートもダイアログの一種て、す。 スタティックテキストのところて、説明し 同じものて , ダイアログの処理中に特別な ダイアログと異なるのは警告音を発生した C 言語雑学講座 125 Fig. 10 ParamText を使用するアラート ロ DITL = 129 from Sample2. proj. Error = ℃ . つん Pa 「 amText とアラートの組み合わせ #define ErrorAlertID 129 ParamText("%p-43" "YpFile not found. StopAlert(ErrorAlertID, NULL) : List 4 Fig. 11 List 3 の実行結果 Error = ー 45. File not found. 0

10. 月刊 C MAGAZINE 1993年5月号

ープロクラミンク熨場 Dr. 望洋の - スタックを確保・初期化する関数 スタックに対する各種の操作 List LiSt int StackAIIoc(Stack *s, int max) 3 : s->ptr 4 : 5 : s—>max return ( ー 1 ) : 8 : 9 : 1 : / * - ースタックにデータをブッシュ 2 : int StackPush(Stack *s int x) if (s->ptr > = s->max) return(-l); 5 : s->stk[s- 〉 ptr 十 + ] return ( の ; 10 : / * ーーースタックからデータをポップーーー * / int StackPop(Stack *s int *x) は (s->Ptr ←の 13 : return(-l); return ( の : 1 9 : / * ーースタックを空にする 20 : void StackClear(Stack *s) 22 : 23 : } 25 : / * ーーースタックを解放する一一 * / 26 : void StackFree(Stack *s) if ()- 〉 stk ! ニ NULL) 28 : free()- 〉 stk) : 29 : 30 : } if ((s->stk ー calloc(max, sizeof(int))) = NULL) { X : s—>max max; return ( の : Fig. 2 スタックの構造 s->ptr max 個 Pt 「 末尾のデータ 先頭のテータ することに注意していただきたい。これに 予防的プログラミング よって , メモリが確保て、きなかった場合は , List 3 の 4 , 13 行目に注目していただきた 配列の大きさが 0 て、あることが記憶され , 以 降の操作て、不正に配列領域 stk にアクセスす い ① if (s->ptr > = s->max) ることはて、きなくなる。 さて , 実際にスタックに要素を積む操作 ② if (s->ptr く = の をブッシュする ( push ) といい , 取り出す操 List 3 に示した , スタック操作の関数のみ 作をポップする ( p 叩 ) という。 を正しく使用して , スタック操作を行って いれば , ptr は 0 以上かっ max 以下て、あること Stack 型に対してブッシュする関数 Stack が保証される。したがって , これらは , Push やポップする関数 StackP 叩などを Lis ① if ()- >ptr s- > max) こて、 Stack 構造体のメンバ pt t 3 に示す。 ② if ()- >ptr r は , 現在積まれている最後のデータの次の ックと関数呼び出し のように記述してもよいはずて、ある。 場所 ( = 配列の添え字 ) を指すようにしてい しかし , プログラムのバグなど何らかの る ( すなわち現在積まれているデータの個数 理由によって ptr の内容が破壊されたとき 実際に「スタック』を使用したことがあ を格納する ) 。 に , ptr は 0 以上 max 以下の値を持っとはかぎ もちろん最後の位置を指すようにしても るだろうか ? との問に対して , 前回の例 らなくなる。ここて、示したような記述を行 のように , 具体的なプログラムを作成した よいが , Stack 型を扱うすべての関数に対し うことにより , そのような場合ても正しく ことのある人を除けば , 「 No ! 」と答えるのて て一貫性を保っ必要がある。 なお , スタックの利用を終了した後は , 動作する関数となる。 はないだろうか。 このようなプログラミングの方法を予防 ところが , 読者の皆さんは , 無意識のう 必ず StackFree を呼び出して , メモリの解放 的プログラミングという。 を正しく行わなければならない ちにスタックを使用しているのて、ある。 【演習】以下の関数を作成せよ。 ( 1 ) スタックの大きさを返す関数 int StackSize(Stack * s) ; ②スタックに積まれているデータ数を 返す関数 int StackNo(Stack * s) ; ( 3 ) スタックが空てあるかどうかを返す 関数 int StacklsEmpty(Stack * s) : D 「 . 望洋のプログラミング道場 113