イベントによって振る舞い(実行するアルゴリズム)変更する場合,通常はswitchが用いられる.
もしくは振る舞い部分を派生クラスでオーバーライドする手法もある.
しかし,戻り値がイベントによって異なる場合の実装は難しい.
このような場合,traitsを用いればよい.
なお,下記例は計測装置のコマンドを想定している.
まず,目的とするインターフェイス
commandの戻り値はイベント(INIT, TX)によって異なる.
そこで,下記のように定義すればよい.
関数commandの実装をしていく.コマンドはこの例では送信と受信から成る.今回は送信についてのみ実装する.
同様にtraits::sendを実装して各コマンドで特殊化すればよい.
同様に受信,受信データのパースについて実装すればよい.
なお,受信については,この装置だとASCIIかBinaryのどちらかなので,もう少し工夫しないといけない.
利点:
この手法はコマンドごとに独立しており送信→受信→解読のプロセスを一つのファイルにまとめて記述できる.
例えば,TX.hppにTX構造体(オプション)とtraitsを記述すれば,TXコマンドにおける全ての処理のみを見ることができる.
これが,CommandHandlerのようなクラスに全てが記述してあると,
あるコマンドに対してどのような処理が実行されるか脳内コンパイラでトレースする必要がある.
次に,新規コマンドの追加も容易である.
CommandHandler内部に追加する場合は,既存のコードに影響を与えない様にしなければならないが,
この場合は新しいコマンドとそのtraitsをヘッダーに書くだけでよい.
もしくは振る舞い部分を派生クラスでオーバーライドする手法もある.
しかし,戻り値がイベントによって異なる場合の実装は難しい.
このような場合,traitsを用いればよい.
なお,下記例は計測装置のコマンドを想定している.
まず,目的とするインターフェイス
struct INIT {}; struct TX { uint16_t mode; explicit TX(uint16_t option) : mode(option) {} }; bool result = command(INIT()); measurement_data data = command(TX(0x0001));
commandの戻り値はイベント(INIT, TX)によって異なる.
そこで,下記のように定義すればよい.
namespace traits { template <typename command_type> struct read {}; template <> struct read<INIT> { typedef bool result_type; }; template <> struct read<TX> { typedef measurement_data result_type; }; } // namespace traits template <typename command_type> read::result_type command(command_type& command) { }
関数commandの実装をしていく.コマンドはこの例では送信と受信から成る.今回は送信についてのみ実装する.
同様にtraits::sendを実装して各コマンドで特殊化すればよい.
// 実際の送信関数 void send(serial_port_t& serial, const char& format, ... ); namespace traits { template <typename command_type> struct send {}; template <> struct send<INIT> { static inline void exec(serial_port_t& serial, INIT& com) { return ::send(serial, "INIT:"); } }; template <> struct send<TX> { static inline void exec(serial_port_t& serial, TX& com) { return ::send(serial, "TX:%04X", com.mode); } }; } template <typename command_type> read::result_type command(serial_port_t& serial, command_type& command) { traits::send<command_type>::exec(serial, command)); auto data = traits::recv<command_type>::exec(serial, command); // not written in this page return traits::read<command_type>::exec(data, command); // not written in this page }
同様に受信,受信データのパースについて実装すればよい.
なお,受信については,この装置だとASCIIかBinaryのどちらかなので,もう少し工夫しないといけない.
利点:
この手法はコマンドごとに独立しており送信→受信→解読のプロセスを一つのファイルにまとめて記述できる.
例えば,TX.hppにTX構造体(オプション)とtraitsを記述すれば,TXコマンドにおける全ての処理のみを見ることができる.
これが,CommandHandlerのようなクラスに全てが記述してあると,
あるコマンドに対してどのような処理が実行されるか脳内コンパイラでトレースする必要がある.
次に,新規コマンドの追加も容易である.
CommandHandler内部に追加する場合は,既存のコードに影響を与えない様にしなければならないが,
この場合は新しいコマンドとそのtraitsをヘッダーに書くだけでよい.
コメントをかく