コードを短く書く努力をするよ(VC++編)

さて、C# や VB でコードを短くするには限界があります。
という具体例を出しましょう。

まずは、先ほどの VB のコードを再掲します。

Public Sub InitStatusBar()
 SETSTATUS(Button1, "先頭のボタンです")
 SETSTATUS(Button2, "真ん中のボタンです")
 SETSTATUS(Button3, "一番舌のボタンです")
 SETSTATUS(ComboBox1, "項目を選択します")
 SETSTATUS(TextBox1, "名前を入力します")
 SETSTATUS(TextBox2, "年齢を入力します")
 SETSTATUS(TextBox3, "住所を入力します")
End Sub

Private _statusdic As New Dictionary(Of Control, String)
''' <summary>
''' フォーカス時のメッセージを設定
''' </summary>
''' <param name="c"></param>
''' <param name="msg"></param>
''' <remarks></remarks>
Public Sub SETSTATUS(ByVal c As Control, ByVal msg As String)
 _statusdic.Add(c, msg)
 AddHandler c.MouseEnter, AddressOf COM_MouseEnter   'MouseEnter
 AddHandler c.MouseLeave, AddressOf COM_MouseLeave   'MoueeLeave
End Sub

Public Sub ChangeFocus(ByVal sender As Object, ByVal b As Boolean)
 If b = False Then
 ToolStripStatusLabel1.Text = ""
 Else
 ToolStripStatusLabel1.Text = _statusdic(CType(sender, Control))
 End If
End Sub

Private Sub COM_MouseEnter(ByVal sender As System.Object, ByVal e As System.EventArgs)
 ChangeFocus(sender, True)
End Sub
Private Sub COM_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs)
 ChangeFocus(sender, False)
End Sub

これでも十分短いですよね。しかも Dictonary を使っているから効率が良さそうです。
ですが、Dictonary を使っているために、イベントがワンクッション遅くなっています。大抵のイベントはこれでいいのですが、高速化が問題になるときは、この【ワンクッション】が致命的になります。

じゃあ、ってんで VB や C# の場合は冗長パターンに書き直す(あるいは、スクリプトを使ってコードを自動生成する)ことになるんですが、C++ を使うとこの両方の条件を満たすことができます。

#define SETEVENT( cn ) \
this->cn->MouseLeave += gcnew System::EventHandler(this, &Form1::cn##_MouseLeave); \
this->cn->MouseEnter += gcnew System::EventHandler(this, &Form1::cn##_MouseEnter);

#define SETSTATUS( cn, msg ) \
private: System::Void cn##_MouseEnter(System::Object^  sender, System::EventArgs^  e) {    \
 this->ToolStripStatusLabel1->Text = msg;    \
} \
private: System::Void cn##_MouseLeave(System::Object^  sender, System::EventArgs^  e) { \
 this->ToolStripStatusLabel1->Text = "";    \
}

private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
 // ステータスバーのイベントを設定
 SETEVENT( Button1 );
 SETEVENT( Button2 );
 SETEVENT( Button3 );
 SETEVENT( ComboBox1 );
 SETEVENT( TextBox1 );
 SETEVENT( TextBox2 );
 SETEVENT( TextBox3 );
}

/// ステータスバーの設定
SETSTATUS( Button1, "先頭のボタンです");
SETSTATUS( Button2, "真ん中のボタンです");
SETSTATUS( Button3, "一番下のボタンです");
SETSTATUS( ComboBox1 , "項目を選択します");
SETSTATUS( TextBox1, "名前を入力します");
SETSTATUS( TextBox2, "年齢を入力します");
SETSTATUS( TextBox3, "住所を入力します");

既にマジックなコードになっていますが、見かけ上は分かりやすいですよね。
保守という点では、SETEVENT と SETSTATUS でマクロが2つに分かれていますが、先の VB のコードのように

・一時的な List や Dictonary を使わない(メモリ効率)
・イベント呼び出しにワンクッション置かない(高速化)

という条件が満たされています。

ここでトリッキーな使い方をしているのが #define のところで、ボタン名などの名前からイベント名を作成しています。イベント名を個別に作成するので、見掛け上は 1 行のコードですが、実際は複数行書かれています。
C/C++ の場合は、割とこのようなコードを書くのが通常のパターンなのですが、残念ながら VB や C# の場合は、このパターンが使えないんですよね。

カテゴリー: 開発, 設計, C++ パーマリンク

コードを短く書く努力をするよ(VC++編) への6件のフィードバック

  1. masuda のコメント:

    ちなみに、この手のマクロはコーディング時の副作用(特にコンパイルができないとか)が大きいので、使い終わったら、
    #undef SETEVENT
    とやって、消しておくとモアベターです。

  2. wkoichi のコメント:

    この例なら、次のコードでいけそうな気がしますが、
    やっぱりC++じゃないとダメなケースってありますか?

    Public Sub SETSTATUS(ByVal c As Control, ByVal msg As String)
    AddHandler c.MouseEnter, Sub(sender, e) ToolStripStatusLabel1.Text = msg
    AddHandler c.MouseLeave, Sub(sender, e) ToolStripStatusLabel1.Text = “”
    End Sub

    • masuda のコメント:

      おおッ!!! Thanks です。
      VB 2010 ならラムダ式でOKですね。
      実は、いまの開発が VB 2005 なものでラムダ式が使えないんですよね…ってなオチだったり。

  3. masuda のコメント:

    そういえば、ラムダ式内で使われている msg って、どうやって参照しているんでしょうね?と不思議に思ったり。
    SETSTATUS 関数で渡される引数だから、単なる変数の参照だと他の呼び出しのときに違っちゃうし。埋め込みなのかな?
    # msg の内容を変えても、期待通り動くことは確認済み

  4. まるこ11 のコメント:

    コードを短くする技法ためになります。
    ―追伸―
    一番初めのVBのコード内にある、「STATMSG」クラスは使用していないから削除してもいいのでは?

    • masuda のコメント:

      あ、修正前の実コードのゴミですね。STATMSG は削除しておきましょう。
      メソッドを短く ≒ 1画面で収まる、ってのが理想的かなと思って実践中。

コメントは停止中です。