はかせのラボ

私の頭の中を書いていく雑記ブログです

C++ タスクシステム⑤ ~エラー&エラー~

あいさつ

どうも、はかせです。
前回ハッカソン話をして英気を養ったので今回からは
また開発やっていきますよ。

いざ本プロジェクトへ

まず前回まででコルーチンがあらかた出来たので
それを本プロジェクトへもっていきます。
ファイルをそのまま持ってってもいいんですが
今回はコピペでいきます。

なぜかというとですね、
私はいつも何か試したいことが出来たときすぐ試せるよう
テストプロジェクトは常時開いています。
なんでファイル移動ができません。

閉じればいい話ですがコピペも大して労力的に変わらないので
それでいいやという感じです。

で、丸コピして実行しようとしたらですねこんなのが出ました。
f:id:hakase0274:20181223222047p:plain

???
await_readyが見つからない?
一体何を言っているんでしょう?

await_readyなら
f:id:hakase0274:20181223222259p:plain
ご覧の通りちゃんといますよ?

遂に私のVisualStudio壊れたか・・・?

コンパイラオプション

色々ネットでググったらですね
コンパイラオプションなるものを見つけました。
私のVisualStudioが壊れたわけではないようで一安心です。

コンパイル時にコンパイラがどう解釈するかの設定が出来るようです。
これの設定をしていなかったためawait_readyが見つけられなかったようです。
awaitを使うためには/awaitのオプションをつける必要があるみたいです。
付けたら直りました。

テストプロジェクトにもついていたようです。
あのプロジェクトはもうかれこれ1年前ぐらいから作って
書いたり消したりを繰り返しているので
そのどっかでつけたんでしょうね。

すっかり記憶から抜け落ちていました。
これでコルーチン移植の完了です。

使う前に簡単にデリゲートを

タスクシステムを作るためにコルーチンをつくりました。
タスクに必要なものはざっくり言うと
①処理を保持し実行する
②処理結果を報告

この2つです。

②は作ったコルーチンを使って実現できます。
①はそのまま書いてもいいですが
タスクシステムなのでタスククラスを作ってそいつに管理させたいです。
そこで登場するのがデリゲートです。

デリゲートとは簡単に言うとメソッドを変数に格納し操作することです。
こうすることで変数と同じ要領でメソッドを管理できます。
今回作った簡単なデリゲート管理クラスです。

namespace MyDirectX
{
	class Task
	{
	public:
		Task() {};
		~Task() {};
		//デリゲート登録
		void SetAction(std::function<void()> action) { mAction = action; }
		//デリゲート実行
		void Run() { if(mAction != nullptr) mAction(); }
		//デリゲート実行 一度実行したらデリゲートを解除する
		void RunOnce()
		{
			Run();
			Reset();
		}
		//デリゲート解除
		void Reset() { mAction = nullptr; }
		//デリゲートがあるか
		bool HasAction() { return mAction != nullptr; }
	private:
		std::function<void()> mAction;
	};
}

std::functionがデリゲートを保持する型です。
テンプレートで保持する処理の戻り値を指定します。
今回は渡した処理を実行してほしいだけなのでvoidにしています。

今回作ったタスククラスは
・デリゲートの登録
・デリゲートの実行
・デリゲートの解除
・デリゲートの有無を返す

という単純な作りにしてあります。

これで①も実現できました。
あとは合わせるだけです。

合わせてみよう

前述のタスククラスにコルーチンを実行するだけのメソッドを渡し実行してみます。

コードです。

mTask = std::make_unique<Task>();
mTask->SetAction([&]()
{
	auto coroutine = PreInstantiateCoroutineAsync();
	while (!coroutine.IsCompleted()){}
});
	
mTask->RunOnce();

コルーチンの実行し終了を待っています。

では実行結果を見てみましょう。
f:id:hakase0274:20181223225215p:plain



( ゚д゚)ポカーン・・・・・・・・・・・・

実行さえできない・・・・だと・・・・・?

このエラーはなんだ?

このエラー自体は見覚えあります。

プログラム中で配列の範囲外みたいな
未定義の領域にアクセスしようとしたりすると
出てくるやつだったと思います。

確かにコルーチンの根っこはイテレータを使っているので
どっかおかしくなれば配列の範囲外にアクセスしようとしても
おかしくはありません。

ただコルーチン自体は動くことをテストプロジェクトでも確認してますし、
本プロジェクトでもしました。
さっき書いたタスククラスも同じくです。

これが単体テストはパスするが
結合テストで落ちるっていうやつか・・・!

エラー原因

エラーの原因はこいつでした。

//生成が終わるまで待機
while (!PreInstantiateCoroutineAsync().IsCompleted()){}

初期化処理が完了していなかったら困るので
最後に完了を待っていたんですが、
こいつが悪かったようです。
(そもそも渡したタスク自体が待っていたので
改めて待つ必要もありませんでした)

コルーチンを再起動はしていないので
この結果範囲外に出てしまっていたようです。

あとがき

今回はタスクシステムのエラー祭りでした。
とりあえず実行できるようになったのでよかったです。
ただまだ私の思い描くタスクシステムにはなっていません。

タスクを管理するクラスもないですし、
タスクが全て同期して動いているので
処理結果も扱いやすさも変わっていません。

もう少しタスクシステムについて理解を深めて
設計を練り直す必要がありそうです。

11月から初めてもう12月が終わります。
もうすでに2か月ほど経ちそうですが、
どうやらこのプロジェクトとはまだ付き合うことになりそうです。

それでは今回はこの辺でノシ