クラスのメンバ関数をSDLのSDL_CreateThread関数で扱うためのメモ書き。ちょっと流用すれば他のライブラリでも使えるかも?
staticメンバ渡せば一発なんだろうけど。派生することを考えるとなんか色々面倒くさそうなんで、普通のメンバ関数を呼び出すようにした。
基底クラスの用意
マルチスレッドで動かすクラスの基底クラスでは、至るところで派生クラスの型がわからないと使えないので、テンプレートクラスで作成。
template <typename T> class Thread{
private:
struct ThreadParameter{
T *instance;
void *parameter;
};
SDL_Thread *thread;
/**
* スレッド関数呼び出し
* @param[in] param スレッド情報
* @return function()の戻り値
*/
static int callThread(void *param){
ThreadParameter *tp = pointer_cast<ThreadParameter *>(param);
return pointer_cast<T *>(tp->instance)->function(tp->parameter);
}
public:
/**
* スレッド実行関数
* @param[in] param 引数
* @return 不明
*/
virtual int function(void *param) = 0;
/**
* スレッド実行関数
* @param[in] p 派生クラスのポインタ
* @param[in] param スレッドに渡す引数
*/
void run(T *p,void *param){
ThreadParameter tp = {p,param};
this->thread = SDL_CreateThread(&Thread<T>::callThread,&tp);
}
};
1.privateにSDL_CreateThreadから呼んでもらうメンバ関数をstaticで宣言。その関数への引数として、派生クラスのポインタとスレッドへの引数をまとめた構造体を渡す。
2.callThread関数では、渡されたポインタから派生クラスのインスタンスのポインタを取り出して、マルチスレッドで実行したいメンバ関数を呼び出す。
派生クラス
class Add:public Thread<Add>{
public:
virtual int function(void *p);
};
int Add::function(void *p){
FILE *file = fopen(pointer_cast<char *>(p),"w");
fprintf(file,"start = %d\n",SDL_GetTicks());
for(int i=0;i<2000;i++){
fprintf(file,"i = %d\n",i);
}
fprintf(file,"end = %d\n",SDL_GetTicks());
fclose(file);
return 0;
}
マルチスレッドで使用する関数をオーバーライドするだけ。
これが呼び出されると、プログラムが起動してからのミリ秒を記録したあと、2000回ほどループして、終了時間を記録してスレッド終了する仕組み。
実行結果は
スレッドID | 開始時刻 | 終了時刻 | |
1 | 515 | 536 | |
2 | 515 | 536 |
だから、マルチスレッドで動いてる。はず…
ちなみにpointer_castはこんな関数
/**
* ポインターキャスト
* @param[in] p キャストするポインター
* @return キャスト結果
*/
template <typename T> inline T pointer_cast(void *p){return reinterpret_cast<T>(p);}