きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

scope_exitをmoveできるようにしたmovable_scope_exitを実装

scope_exitでは、その変数を定義したスコープを抜けたときの処理を記述できるように実装しましたが、抜けた時に処理をしないようにする、あるいは処理を延期できるようにすることを目標にmovable_scope_exitを実装しました。ポイントはAnyクラスの実装とほぼそっくりであることです。すなわち内部でポインタとしてtemplateの抽象クラスを保持し、必要なときにインターフェースとしてexecuteを呼び出す、というものです。キャプチャのラムダ式もオーバーヘッドなく使用できます。ただし仮想関数テーブル分および抽象クラス分のオーバーヘッドは避けられません。大したことはないと思いますが……

使用例

{
	movable_scope_exit mse;
	{
		auto se = make_movable_scope_exit(
			[]() {
			printf("out\n");
		});
		mse = std::move(se);
		printf("1\n");
	}
	printf("2\n");
}
printf("3\n");

実行結果は、

1
2
out
3

実装例

class scope_exit_place_holder {
public:
	virtual ~scope_exit_place_holder() { }
	virtual void execute() { }
};

template <typename Func>
class scope_exit_holder : public scope_exit_place_holder {
public:
	scope_exit_holder(Func func) : func(func) { }
	virtual void execute() { func(); }
	Func func;
};

class no_scope_exit { };

class movable_scope_exit {
public:
	template <typename Func>
	movable_scope_exit(Func func) : func(std::make_unique<scope_exit_holder<Func>>(func)) { }
	movable_scope_exit(movable_scope_exit const& it) = delete;
	movable_scope_exit(movable_scope_exit&& it) : func(std::move(it.func)) {}
	movable_scope_exit(no_scope_exit const& it) : func(nullptr) {}
	movable_scope_exit() : func() { }
	movable_scope_exit& operator=(movable_scope_exit const& it) = delete;
	movable_scope_exit& operator=(no_scope_exit const& it) {
		func = nullptr;
		return *this;
	}
	movable_scope_exit& operator=(movable_scope_exit&& it) {
		func = std::move(it.func);
		return *this;
	}

	virtual ~movable_scope_exit() {
		if (func) {
			func->execute();
		}
	}

	std::unique_ptr<scope_exit_place_holder> func;
};

template <typename Func>
static movable_scope_exit make_movable_scope_exit(Func func) {
	return movable_scope_exit(func);
}