8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

std::bindとstd::functionを利用してswitch文のコピペを回避する方法

Last updated at Posted at 2014-07-15

何が起きたのか#

とある enum class の設定に基づいて、実務クラスの挙動を微妙に変える必要があり、行き当たりばったりでコードを書いていたら、いつの間にかswitch文のコピペが大量に発生していた。
ぱっと見コードが汚い…。
どうしよう (´・ω・`)

解決#

# include <iostream>
# include <functional>


// 何らかの意味があるモードがあったとします。
enum class flag_t : unsigned char
{
    mode_01,
    mode_02,
    mode_03,
};

// 関数を登録しておくためのもの
class flag_pattern_func_t
{
public:
    using func_t = std::function< void() >;

    func_t mode_01;
    func_t mode_02;
    func_t mode_03;
    func_t mode_dc; // case default:
};

// 状態を持っているクラス
class state_t
{
public:
    flag_t flag;
public:
    void pattern_call( flag_pattern_func_t &func );
};

// 状態に基づいた関数の呼び出し
void state_t::pattern_call( flag_pattern_func_t &func )
{
    switch( flag )
    {
    case flag_t::mode_01:
        if ( func.mode_01 ) return func.mode_01();
        break;
    case flag_t::mode_02:
        if ( func.mode_02 ) return func.mode_02();
        break;
    case flag_t::mode_03:
        if ( func.mode_03 ) return func.mode_03();
        break;
    default:
        break;
    }
    if( func.mode_dc ) return func.mode_dc();
}

// 何らかの仕事をしなければならないクラス
class worker_t
{
public:
   state_t state;
   
   void work_01();
   void work_02();

private:
   void work_01_mode_01() { printf("work_01_mode_01()\n"); }
   void work_01_mode_02() { printf("work_01_mode_02()\n"); }
   void work_01_mode_dc() { printf("work_01_mode_dc()\n"); }

   void work_02_mode_02() { printf("work_02_mode_02()\n"); }
   void work_02_mode_03() { printf("work_02_mode_03()\n"); }
};

// 呼び出し元から丸投げされた実務処理01
void worker_t::work_01()
{
    flag_pattern_func_t pf;
    pf.mode_01 = std::bind( &worker_t::work_01_mode_01, this );
    pf.mode_02 = std::bind( &worker_t::work_01_mode_02, this );
    pf.mode_dc = std::bind( &worker_t::work_01_mode_dc, this );
    state.pattern_call( pf );
}

// 呼び出し元から丸投げされた実務処理02
void worker_t::work_02()
{
    flag_pattern_func_t pf;
    pf.mode_02 = std::bind( &worker_t::work_02_mode_02, this );
    pf.mode_03 = std::bind( &worker_t::work_02_mode_03, this );
    state.pattern_call( pf );
}


int main()
{
   worker_t work;
   work.state.flag = flag_t::mode_01; work.work_01();
   work.state.flag = flag_t::mode_02; work.work_01();
   work.state.flag = flag_t::mode_03; work.work_01();

   work.state.flag = flag_t::mode_01; work.work_02();
   work.state.flag = flag_t::mode_02; work.work_02();
   work.state.flag = flag_t::mode_03; work.work_02();
   return 0;
}

[Wandbox]で確認。
http://melpon.org/wandbox/permlink/TeEbdN9k3EgfzXLm

scoped enum とは
http://ezoeryou.github.io/cpp-book/C++11-Syntax-and-Feature.xhtml#dcl.enum.scoped

それで?#

  1. switch文をコピペするよりはマシになった気がします。
  2. こういう方法はループの深い階層の場所では使わない気がします。
  3. 処理の浅い場所で細かい挙動を変えたい時とかに使うんだと思います。
  4. とあるモードに対する実務処理が一つだけならここまでする必要はないと思われます。
  5. 意味のある実務処理が複数存在してしまうとか、複数のソースにおんなじような switch 文が現れたら使ったほうがいいと思います。
  6. ステートマシンを使う程でもないときに利用できるかと思います。

何かもっと、より良い方法があったら教えて下さいm(_ _)m

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?