11 Apr 2015, 06:52

C 言語/C++ における イベントハンドラの定石 (switch/ 関数ポインタ配列/lambda)

はじめに

C/C++ における イベントハンドラの書き方について,定石を整理してみた.

[toc]

前提

<div class="outline-text-3" id="text-unnumbered-2">
  <p>
    以下のようなコードがあるとする.
  </p>

  <p>
    [sourcecode language=&#8221;cpp&#8221; title=&#8221;&#8221; ]<br /> #include <iostream><br /> typedef enum {START, STOP} COMMAND;
  </p>

  <p>
    void check (COMMAND command) {<br /> std::cout << (int) command << " is called" << std::endl; } int main (int argc, char *argv[]) { check (START); check (STOP); return 0; } [/sourcecode] 

    <p>
      出力結果は以下のようになる.
    </p>

    <p>
      [sourcecode language=&#8221;text&#8221; title=&#8221;&#8221; ]<br /> 0 is called<br /> 1 is called<br /> [/sourcecode]
    </p>

    <p>
      この出力結果は以下のようにしたい.
    </p>

    <p>
      [sourcecode language=&#8221;text&#8221; title=&#8221;&#8221; ]<br /> start is called<br /> stop is called<br /> [/sourcecode]
    </p></div> </div> </div> 

    <div id="outline-container-unnumbered-3" class="outline-2">
      <h2 id="unnumbered-3">
        方法
      </h2>

      <div class="outline-text-2" id="text-unnumbered-3">
      </div>

      <div id="outline-container-unnumbered-4" class="outline-3">
        <h3 id="unnumbered-4">
          switch を使う
        </h3>

        <div class="outline-text-3" id="text-unnumbered-4">
          <p>
            一番単純な方法は, switch をつかって, 表示を分岐する
          </p>

          <p>
            [sourcecode language=&#8221;cpp&#8221; title=&#8221;&#8221; ]<br /> void onStart () { std::cout << "start is called" << std::endl; } void onStop () { std::cout << "stop is called" << std::endl; } void check (COMMAND command) { switch (command) { case START: onStart (); break; case STOP: onStop (); break; } } }; [/sourcecode] </div> </div> 

            <div id="outline-container-unnumbered-5" class="outline-3">
              <h3 id="unnumbered-5">
                関数ポインタ配列を使う
              </h3>

              <div class="outline-text-3" id="text-unnumbered-5">
                <p>
                  別の定石は, 関数ポインタ配列をつかう.
                </p>

                <p>
                  [sourcecode language=&#8221;cpp&#8221; title=&#8221;&#8221; ]<br /> #include <iostream>
                </p>

                <p>
                  typedef enum {START, STOP} COMMAND;
                </p>

                <p>
                  void onStart () { std::cout << "start is called" << std::endl; } void onStop () { std::cout << "stop is called" << std::endl; } typedef void (*HANDLER) (); static HANDLER handle_tbl[2] = {&onStart, &onStop}; void check (COMMAND command) { handle_tbl[command] (); } [/sourcecode] </div> </div> 

                  <div id="outline-container-unnumbered-6" class="outline-3">
                    <h3 id="unnumbered-6">
                      lambda をつかう
                    </h3>

                    <div class="outline-text-3" id="text-unnumbered-6">
                      <p>
                        関数テーブルに関数を登録するために, 関数を作成する必要があるけれども, 一行なので, 関数を作成するのは面倒.
                      </p>

                      <p>
                        そんなときは,c++11 からつかえるようになったラムダ式を利用する.
                      </p>

                      <p>
                        [sourcecode language=&#8221;cpp&#8221; title=&#8221;&#8221; ]<br /> #include <iostream><br /> #include <functional>
                      </p>

                      <p>
                        typedef enum {START, STOP} COMMAND;
                      </p>

                      <p>
                        static std::function<void ()> handle_tbl[2] = {<br /> [] (){ std::cout << "start is called" << std::endl;}, [] (){ std::cout << "stop is called" << std::endl;} }; void check (COMMAND command) { handle_tbl[command] (); } int main (int argc, char *argv[]) { check (START); check (STOP); return 0; } [/sourcecode] 

                        <p>
                          やっぱり, これからは関数型の時代だよね!
                        </p></div> </div> </div> 

                        <div id="outline-container-unnumbered-7" class="outline-2">
                          <h2 id="unnumbered-7">
                            Special Thanks
                          </h2>

                          <div class="outline-text-2" id="text-unnumbered-7">
                            <p>
                              この記事によると, switch 型と関数テーブル型では, スピードは変わらないとか.
                            </p>

                            <ul class="org-ul">
                              <li>
                                <a href="http://dixq.net/forum/viewtopic.php?f=3&t=13875">関数ポインタテーブルと switch 文 • C 言語交流フォーラム ~ mixC++ ~</a>
                              </li>
                            </ul>

                            <p>
                              委譲をつかう方法もある (Strategy Pattern)
                            </p>

                            <ul class="org-ul">
                              <li>
                                <a href="http://www7b.biglobe.ne.jp/~robe/pf/pf016.html">プログラマの友 第十六報:イベントハンドリングとデリゲート</a>
                              </li>
                            </ul>

                            <p>
                              C++11 の方法.
                            </p>

                            <ul class="org-ul">
                              <li>
                                <a href="http://qiita.com/shiro_naga/items/5967f6cd1710e7b78677">C++ &#8211; メンバー関数ポインタと配列を使って, メンバー関数を番号で指定して呼び出す方法 &#8211; Qiita</a>
                              </li>
                              <li>
                                <a href="http://kaworu.jpn.org/cpp/std::function">std::function &#8211; C++ 入門</a>
                              </li>
                            </ul>
                          </div>
                        </div>