前回雑記で書いたthreadクラスの実装を晒す


実装を見る前にもう一度threadクラスの使い方を見ておきます。

#include <iostream>
#include "thread.hpp" // これから実装を見ていくthreadクラスのヘッダファイル

void foo() {
  std::cout << "fooを実行しています" << std::endl;
}

int main() {
  thread t(foo); // スレッドを生成しfoo関数を実行する
  t.join();      // スレッドの実行が終了するのを待つ
  return 0;
}

ではまず本体であるthreadクラスの実装部分です。
# 最初は最低限の実装としたので引数無しの関数しか実行できないようになっています。
# 順を追って実行したい関数に引数があるバージョンを追加していきます。
注1)ここではインクルードガードの記述は省略してます。
注2)namespaceの記述も省略してます。

// thread.hpp
#include <memory>     // std::auto_ptr
#include <process.h>  // ::_beginthreadex
#include "thread_base.hpp"
#include "threadholder0.hpp"

class thread {
  HANDLE hThread_;        // スレッドのハンドル
  unsigned int threadId_; // スレッドのID

private:
   // _beginthreadexに渡す関数
  static unsigned int __stdcall workerThread(void* arg)
  {
    std::auto_ptr<thread_base> p(reinterpret_cast<thread_base*>(arg));  // ポインタをauto_ptrに渡して自動的に開放
    p->run();   // threadクラスに渡された関数を実行
    return 0;
  }

public:
  // 引数0個の関数を実行するためのコンストラクタ
  template <typename Func>
  thread(Func func)
    : hThread_(0)
    , threadId_(0)
  {
    thread_base* p = new threadholder0<Func>(func);    // 引数0個の関数ポインタを保持するクラスを生成
    hThread_ = reinterpret_cast<HANDLE>(::_beginthreadex(0, 0, &thread::workerThread, p, 0, &threadId_));
  }

  ~thread()
  {
    ::CloseHandle(hThread_);
  }

  // スレッドの実行を待つ関数
  int join()
  {
    return static_cast<int>(::WaitForSingleObject(hThread_, INFINITE));
  }
};

threadクラスのコンストラクタの引数に実行したい関数のポインタまたは、関数オブジェクトを渡します。
渡された関数ポインタまたは、関数オブジェクトであるfuncはthreadholder0クラスに保持します。
その後_beginthreadexを使用してスレッドを生成します。
workerThread関数では基底クラスであるthread_baseを通してthreadholder0の仮想関数runを呼び出しています。


それでは基底クラスであるthread_baseクラスを見てみます。

// thread_base.hpp

struct thread_base {
  virtual ~thread_base() {}
  virtual void run() = 0; // threadクラスに渡された関数を実行
};

runが純粋仮想関数となっています。この関数をthreadholder0に実装します。


threadholder0は引数0個の関数を実行するためのクラスです。
実装は以下。

// threadholder0.hpp
#include "thread_base.hpp"

template <typename Func>
class threadholder0 : public thread_base
{
  Func function_; // 関数ポインタを保持

public:
  threadholder0(Func func)
    : function_(func) 
  {}

  void run()
  {
    function_();  // threadクラスに渡された関数を実行
  }
};

threadholder0クラスはthread_baseを基底クラスにしています。
threadクラスのworkerThread関数内で実行しているp->run()は、このthreadholder0クラスのrunを実行しています。
これでthreadクラスのコンストラクタに渡した関数ポインタをthreadholder0のメンバー変数function_に保持し、
実行していることになります。


今回は引数が0個の関数を実行するためのthreadクラスの実装を見ました。
次回はこのthreadクラスに引数が1つある関数を実行するための機能を追加します。


追記:
ソースコードの色のつけ方がわかったw
このままの引数をとることができない状態のthread君でもboostのbindさえあれば引数あり関数を実行できます。

void test(int x, int y) {
  std::cout << x + y << std::endl;
}

int x = 1, y = 2;
thread t(boost::bind(&test, x, y));

boostってすごいですよね〜。まさにヘンタイですw(ほめ言葉的な意味で
とりあえずboost::bindが無いことを前提に引数追加バージョンを作っていきます。
エラー処理も追加していきます。