[C++] C++20協程例子之惰性計算(附時序圖)

C++20協程 惰性計算流程圖

  C++20協程的標準目前爲止似乎還未確定, 現在按VS2019(10.0.18362.0)的具體實現做例子, 下面給出一個惰性計算的例子.
  注意目前開啓C++協程需要打開選項 /await.
  爲方便理解起見, 下面將promise對象稱爲協程守護器, awaiter對象稱爲同步器.

源碼

所需頭文件如下

#include <iostream>
#include <experimental/coroutine>

同步器

class HandleDeliver {
	HandleDeliver() = delete;
	HandleDeliver(const HandleDeliver&) = delete;
	HandleDeliver& operator= (const HandleDeliver&) = delete;
	HandleDeliver& operator= (HandleDeliver &&) = delete;

private:
	std::experimental::coroutine_handle<>& phandle;
public:
	HandleDeliver(std::experimental::coroutine_handle<>& phandle) noexcept :
		phandle(phandle) {}
	HandleDeliver(HandleDeliver &&self) noexcept :
		phandle(self.phandle) {}
	bool await_ready() const noexcept {
		return false;
	}
	void await_suspend(std::experimental::coroutine_handle<> handle) noexcept {
		phandle = handle;
	}
	void await_resume() const noexcept {}
};

惰性計算類

class Lazy {
	Lazy() = delete;
	Lazy(const Lazy&) = delete;
	Lazy& operator= (const Lazy&) = delete;
	Lazy& operator= (Lazy &&) = delete;

public:
	class promise_type {
		... // 見下文守護器
	};

private:
	class iterator_type {
		... // 見下文迭代器
	};

private:
	promise_type & promise;
	bool is_overed;
	int retv;

protected:
	bool _next() {
		if(is_overed) {
			return false;
		}
		promise.resume();
		// promise.rethrow_if_has_exception();
		if(promise.overed()) {
			retv = promise.value();
			promise.destroy();
			is_overed = true;
			return false;
		}
		return true;
	}
	int _get() const noexcept {
		if(is_overed) {
			return -1;
		} else {
			return promise.value();
		}
	}

public:
	explicit Lazy(promise_type& promise) noexcept :
		promise(promise),
		is_overed(false),
		retv() {
	}
	Lazy(Lazy &&self) noexcept :
		promise(self.promise),
		is_overed(self.is_overed) {
	}
	iterator_type begin() noexcept {
		return iterator_type(*this);
	}
	iterator_type end() noexcept {
		return iterator_type(*this);
	}
	int get_return() noexcept {
		if(is_overed) {
			return retv;
		} else {
			return -1;
		}
	}
};

協程守護器

	class promise_type {
		promise_type(const promise_type&) = delete;
		promise_type(promise_type &&) = delete;
		promise_type& operator= (const promise_type&) = delete;
		promise_type& operator= (promise_type &&) = delete;

	private:
		std::experimental::coroutine_handle<> handle;
		int v;
		bool is_overed;

	public:
		std::experimental::suspend_never initial_suspend() noexcept {
			return std::experimental::suspend_never();
		}
		promise_type() noexcept : v(-1), is_overed(false) {}
		Lazy get_return_object() noexcept {
			return Lazy(*this);
		}
		HandleDeliver yield_value(int v) noexcept {
			this->v = v;
			return HandleDeliver(handle);
		}
		void return_value(int v) noexcept {
			this->v = v;
		}
		HandleDeliver final_suspend() noexcept {
			is_overed = true;
			return HandleDeliver(handle);
		}
		void unhandled_exception() {
			try {
				std::rethrow_exception(std::current_exception());
			} catch(std::exception& e) {
				std::cout << "exception: " << e.what() << std::endl;
			} catch(...) {
				std::cout << "unknow exception" << std::endl;
			}
		}

	public:
		void resume() const noexcept {
			handle.resume();
		}
		bool overed() const noexcept {
			return is_overed;
		}
		int value() const noexcept {
			return v;
		}
		void destroy() noexcept {
			handle.destroy();
		}
	};

協程迭代器

	class iterator {
		iterator_type() = delete;
		iterator_type(const iterator_type&) = delete;
		iterator_type& operator= (const iterator_type&) = delete;
		iterator_type& operator= (iterator_type &&) = delete;

	private:
		Lazy &z;

	public:
		explicit iterator_type(Lazy& z) noexcept : z(z) {}
		iterator_type(iterator_type &&self) noexcept : z(self.z) {}
		iterator_type& operator++ () noexcept {
			z._next();
			return *this;
		}
		bool operator!= (iterator_type const& other) const noexcept {
			return !z.is_overed;
		}
		int operator* () const noexcept {
			return z._get();
		}
	};

實例

Lazy f() {
	for(int i = 0; i < 5; ++i) {
		co_yield i;
	}
	co_return 10;
}

int main() {
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	{
		Lazy result = f();
		for(int i : result) {
			std::cout << ">" << i << std::endl;
		}
		std::cout << result.get_return() << std::endl;
	}

	_CrtDumpMemoryLeaks();
	return 0;
}

輸出

>0
>1
>2
>3
>4
10

若給每個核心函數調用加上字符串輸出, 則如下:

00E893D8 Lazy::promise_type::initial_suspend
00E893D8 Lazy::promise_type::promise_type
00E893D8 Lazy::promise_type::get_return_object
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>0
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>1
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>2
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>3
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::yield_value
00E893F8 HandleDeliver::HandleDeliver
00E893F8 HandleDeliver::await_ready
00E893F8 HandleDeliver::await_suspend
>4
00E893F8 HandleDeliver::await_resume
00E893F8 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::return_value
00E893D8 Lazy::promise_type::final_suspend
00E89408 HandleDeliver::HandleDeliver
00E89408 HandleDeliver::await_ready
00E89408 HandleDeliver::await_suspend
00E89408 HandleDeliver::~HandleDeliver
00E893D8 Lazy::promise_type::~promise_type
10

時序圖

在這裏插入圖片描述
其中, 紅框的函數是語法上要求必須存在的函數.
C++20標準中要求守護器的初始化在initial_suspend之前. 但VS的實際實現卻顛倒過來了. initial_suspend被調用時, 守護器竟然尚未初始化!(具體見上文實例輸出部分)

附加說明

  協程拋出異常後, 由協程守護器unhandled_exception函數接收異常. 若需將異常拋出給主線程, 則該異常需爲由throw new創建. 否則在協程棧上創建的異常拋出協程後會立即失效.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章