はじめに

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

前提

以下のようなコードがあるとする.

#include <iostream>
typedef enum {START,  STOP} COMMAND;

void check (COMMAND command) {
  std::cout << (int) command << " is called" << std::endl;
}

int main (int argc, char *argv[])
{
  check (START);
  check (STOP);
  return 0;
}

出力結果は以下のようになる.

0 is called
1 is called

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

start is called
stop is called

方法

switch を使う

一番単純な方法は, switch をつかって, 表示を分岐する

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;
    }
  }
};

関数ポインタ配列を使う

別の定石は, 関数ポインタ配列をつかう.

#include <iostream>

typedef enum {START,  STOP} COMMAND;

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] ();
}

lambda をつかう

関数テーブルに関数を登録するために, 関数を作成する必要があるけれども, 一行なので, 関数を作成するのは面倒.

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

#include <iostream>
#include <functional>

typedef enum {START,  STOP} COMMAND;

static std::function<void ()> handle_tbl[2] = {
  [] (){ 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;
}

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

Special Thanks

この記事によると, switch 型と関数テーブル型では, スピードは変わらないとか.

委譲をつかう方法もある (Strategy Pattern)

C++11 の方法.