C++によるRedux

# はじめに

C++でOpenGLのプログラミングを行っていた。
Guiのスレッドが1つあり、裏にワーカースレッドがたくさんあるとして、
描画とどう同期すればいいんだろう。
これにユーザーの入力が加わるとボタンUIはちゃんと動くのだろうか。
そうしたことを考えているとふとJavascriptで似たようなことをやってたのを思い出した。
**React+Redux**だ。
C++でも同じことをやればいいんでないか。
しかし、いかんせんC++は素人なので突っ込みあったらよろしく。

# Action

**Redux**がどういうものかは上の検索ボックスから各自調べてもらうとして……
まずは**Action**だ。
状態は**Store**が管理するが、これを変更するのは**Action**の発行によってのみ行われる。
それはユーザー操作であれ、ワーカースレッドであれ変わらない。

ActionはC++らしくenumで定義するのが良いだろう。
ここでは2つのアクションを定義している。
シンプルなint型を更新したいというアクションと、
巨大なクラス(BigClass)があってそれを更新したいというアクションだ。

```c++
// Actionの定義
enum class ActionType
{
	UPDATE_INT,
	UPDATE_BIGCLASS
};
struct Action {
	ActionType at;
	int i;
	void* z;
};
const Action action_update_int(const int new_int) {
	Action ac;
	ac.at = ActionType::UPDATE_INT;
	ac.i = new_int;
	return ac;
}
const Action action_update_bigclass(const BigClass &new_bigclass) {
	Action ac;
	ac.at = ActionType::UPDATE_BIGCLASS;
	BigClass *z = new BigClass; // メモリの動的確保
	*z = new_bigclass; // ここでクラスの代入演算子が呼ばれる
	ac.z = static_cast<void*>(z);
	return ac;
}
```

どんなクラスであれとりあえずvoid*に突っ込めば送ることができる。
またデータの依存をここで切り離すため、データのディープコピーを行う。
これで更新要求をActionというカプセルに詰め込めた。

# Store

状態を管理するStoreはというと、ここはActionの受け口となる。
全体がマルチスレッドになっているため、
キューを使って、挿入と削除にミューテックスを利用することで排他制御する。
Stateという構造体が先程のintとBigClassのデータを持っているが、
これをどうやって更新しているのかというと。

```c++
#include <thread>
#include <queue>
// 状態を表現する構造体
struct State {
	int i;
	BigClass *p;

	State();
	~State();
	State(const State &a); // コピーコンストラクタ
	State &operator=(const State &a);
};
// Stateを管理するクラス
std::mutex store_mtx; // Storeのキューにアクセスするためのミューテックス
class Store {
private:
	std::queue<Action> action_que; // Actionを格納するキュー
	State state;
public:
	Store();
	~Store();
	void add_action_que(Action ac); // キューにactionを追加する
	void exe_action_que(); // キューのアクションを実行する
	void update_store(const State &nextState); // stateを更新する
	State provider() const; // 現在のstateから描写に必要な情報を取り出す
};
Store::Store() {
	state.p = malloc(sizeof(BigClass));
}
Store::~Store() {
	free(static_cast<BigClass*>(state.p));
}
// キューにactionを追加する
void Store::add_action_que(Action ac) {
	std::lock_guard<std::mutex> lock(store_mtx);
	action_que.push(ac);
}
// Actionを実行する
void Store::exe_action_que() {
	if (action_que.empty()) { // キューが空なら何もしない
		return;
	}
	// キューが空でなければ1つActionを実行する
	store_mtx.lock(); // storeのミューテックスを取得する
	Action ac = action_que.front();
	action_que.pop();
	store_mtx.unlock(); // ミューテックスを解放する
	State nextState = reducer(ac, state); // reducerにdispatchする
	update_store(nextState); // stateを更新する
}
// stateを変更できる唯一のメソッドとすること
void Store::update_store(const State &nextState) {
	state.i = nextState.i;
	state.p = nextState.p;
}
// 現在のstateから描写に必要な情報を取り出す
State Store::provider() const {
	// 最新のstateを取り出し必要があれば加工してrender()に渡す
	return &state; // ここではただの参照を渡す
}
```

ここの2文だ。
State nextState = reducer(ac, state); // reducerにdispatchする
update_store(nextState); // stateを更新する
reducerというものが新しいStateを返してきてupdate_storeはそれをただコピーしているだけ。
ここのコピーはただのポインタなので高速です。

# Reducer

ではreducerとは何か。
const Actionとconst Stateを受け取り、Stateを返す関数である。
これはStoreのメンバ関数でないことに注目してほしい。
いわゆる副作用のない純粋関数というやつだ。

```c++
// Actionを受けて新しいStateを作成する (Stateは直接変更しない)
const State reducer(const Action &action, const State &state) {
	State nextState = state; // stateをnextStateにコピーする
	// 各Actionに対応した処理を行う
	switch (action.at) {
		case ActionType::UPDATE_INT: {
			nextState.i= action.i;
			break;
		}
		case ActionType::UPDATE_BIGCLASS: {
			nextState.p = static_cast<BigClass*>(action.z); // ポインタの付け替えでmoveする
			delete state.p; // 以前確保したメモリを解放する
		}
		default: {
			break;
		}
	}
	return nextState; // 新しいstateを返す
}
```

最初のコピーはポインタをコピーするだけのコピーコンストラクタを作成してください。
これも全てのパスがポインタコピーになるので高速です。
ムーブコンストラクタをしっかり書けば複雑なムーブにも対応できるだろう。
ではBigClassを扱うコストはどこで支払われたのか？
それはAction発行時にBigClassのコピーが行われた時である。

# Render

描画の関数もStoreのメンバ関数ではない。
Providerから受け取った情報に対して描画を行う。
Gui側がシングルスレッドならば、描画中にstateが更新されないので
コピーではなくて参照渡しでよい。

```c++
// providerから受け取ったStateを用いて描写を行う
void render(const State &state) {
	// ここに描写コードを書く
}
```