[C++] C++20: ์ฝ”๋ฃจํ‹ด

์ฝ”๋ฃจํ‹ด์€ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ๋„์ž…๋œ ์‹ ๊ธฐ๋Šฅ์ด๋‹ค.

๋‹ค๋ฅธ ์–ธ์–ด๋“ค์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋ฃจํ‹ด๊ณผ ๋น„๊ตํ•˜๋ฉด ์‹ ํƒ์Šค๊ฐ€ ์ƒ๋‹นํžˆ ๋ณต์žกํ•œ ๊ฒƒ ๊ฐ™๋‹ค.
์ฐธ์œผ๋กœ C++๋‹ต๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.




ํƒ€์ž… ์ •์˜

๋จผ์ € ์ฝ”๋ฃจํ‹ด์— ์‚ฌ์šฉํ•  ์ฝ”๋ฃจํ‹ด์šฉ ํƒ€์ž…์„ ์ •์˜ํ•œ๋‹ค.

์ฝ”๋ฃจํ‹ด ํƒ€์ž…์€ ๋ฌธ๋ฒ•์— ๋”ฐ๋ฅด๋ฉด promise๋ผ๋Š” ๋‚ด๋ถ€ ํƒ€์ž…์„ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.
์ž์ž˜ํ•œ ์„ธ๋ถ€์‚ฌํ•ญ์„ ๋‹ค ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด coroutine_handler๋ผ๋Š” ๋…€์„์„ ์ƒ์†๋ฐ›์•„๋„ ์ข‹๋‹ค.

๊ทธ๋Ÿผ ์ด๋Ÿฐ ์‹์œผ๋กœ ๋œ๋‹ค.

ํ”„๋กœ๋ฏธ์Šค ํƒ€์ž…์˜ ๊ทœ๊ฒฉ์— ๋Œ€ํ•ด์„œ๋Š” ๋ฐ”๋กœ ๋‹ค์Œ ๋ฌธ๋‹จ์—์„œ ๋‹ค๋ฃฌ๋‹ค.




ํ”„๋กœ๋ฏธ์Šค ์ •์˜

์ด๊ฒŒ ์ข€ ๋ณต์žกํ•˜๋‹ค.
๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ด 4๊ฐ€์ง€์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

ํŠน๋ณ„ํ•˜๊ฒŒ ์ปค์Šคํ…€์• ์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด ์—†๋‹ค๋ฉด, ์ผ๋‹จ ์ด๋Ÿฐ์‹์œผ๋กœ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

struct promise {
	coroutine get_return_object()
	{
		return coroutine{ coroutine::from_promise(*this) };
	}

	std::suspend_always initial_suspend() noexcept 
	{
		return std::suspend_always{}; 
	}

	std::suspend_always final_suspend() noexcept 
	{ 
		return std::suspend_always{};
	}

	std::suspend_never return_void() {
		return std::suspend_never{};
	}

	void unhandled_exception() { std::puts("์˜ˆ์™ธ ๋ฐœ์ƒ"); }
};

C++์˜ ์ฝ”๋ฃจํ‹ด์€ ํ•˜๋‚˜์˜ ์ •ํ•ด์ง„ ํ˜•ํƒœ๊ฐ€ ์žˆ๋Š”๊ฑด ์•„๋‹ˆ๊ณ , ์ด 3๊ฐ€์ง€์˜ ํ˜•ํƒœ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.
์‚ฌ์‹ค ์œ„์—์„œ ์ •์˜ํ•œ ์ฝ”๋ฃจํ‹ด ํƒ€์ž…์„ ์ฝ”๋ฃจํ‹ด์ด๋ผ๊ณ  ํ•˜์ง€๋Š” ์•Š๊ณ , co_yield, co_await, co_return ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์„ ์‚ฌ์šฉํ•œ ํ•จ์ˆ˜๋ฅผ ์ฝ”๋ฃจํ‹ด์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

ํ•˜๋‚˜์”ฉ ๋‹ค๋ค„๋ณด๊ฒ ๋‹ค.



co_yield ๊ตฌ๋ฌธ

co_yield ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•ด์„œ ์ ์ง„์ ์ธ ์ƒํƒœ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฒ•์„ ๊ฐ„๋žตํžˆ ๋‹ค๋ค„๋ณด๊ฒ ๋‹ค.

co_yield๋Š” ๋‹ค๋ฅธ ์–ธ์–ด์˜ ์ฝ”๋ฃจํ‹ด ์‹ ํƒ์Šค์™€ ๋™์ผํ•˜๊ฒŒ, ํ•จ์ˆ˜ ๋‚ด์—์„œ ์ƒํƒœ๋ฅผ ๋งŒ๋“ค๊ณ  ์ž„์‹œ๋กœ ๋ฆฌํ„ด์„ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
๊ทธ๋ž˜์„œ ์ฝ”๋ฃจํ‹ด์„ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๋ฉด ๊ทธ๋•Œ ์ž„์‹œ๋กœ ๋ฆฌํ„ดํ–ˆ๋˜ ์ง€์ ์œผ๋กœ ๋Œ์•„๊ฐ€์„œ ๋กœ์ง์„ ๋งˆ์ € ์ฒ˜๋ฆฌํ•œ๋‹ค.

์ด๋ฅผ ์ด์šฉํ•˜๋ฉด iterator ๋“ฑ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์ ์ ˆํ•˜๋‹ค.

๋‚ด ๊ฒฝ์šฐ์—๋Š” ๊ฐ„๋‹จํ•œ ์นด์šดํ„ฐ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋ณด์•˜๋‹ค.

// ์ฝ”๋ฃจํ‹ด ๊ฐ์ฒด
template <class T>
struct coroutine: std::coroutine_handle<promise>{
	struct promise {
		coroutine get_return_object()
		{
			return coroutine(std::coroutine_handle<promise>::from_promise(*this));
		}

		std::suspend_always initial_suspend() noexcept
		{
			return {};
		}

		std::suspend_always final_suspend() noexcept
		{
			return {};
		}

		void return_void() {
		}

		void unhandled_exception() { std::puts("์˜ˆ์™ธ ๋ฐœ์ƒ"); }

		T value;

		std::suspend_always yield_value(T from) {
			this->value = from; // caching the result in promise
			return {};
		}
	};

	using promise_type = struct promise;
	std::coroutine_handle<promise> handle;

	coroutine(std::coroutine_handle<promise> handle) {
		this->handle = handle;
	}

	T operator()() {
		handle(); // resume
		auto result = handle.promise();
		return result.value;
	}
};

์ฝ”๋“œ ํ˜•ํƒœ๊ฐ€ ์ข€ ๋‹ฌ๋ผ์กŒ๋‹ค.

ํ”„๋กœ๋ฏธ์Šค ํƒ€์ž…์—๋Š” yield_value๋ผ๋Š” ํ•จ์ˆ˜์™€ ๋ฉค๋ฒ„๋ณ€์ˆ˜ T value;๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋Š”๋ฐ, ์™ธ๋ถ€์—์„œ co_yield๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ทธ ๊ฐ’์ด yield_value๋ผ๋Š” ํ•จ์ˆ˜๋กœ ๋„˜์–ด์˜ค๊ฒŒ ๋œ๋‹ค.

๊ทธ๋ž˜์„œ ๊ทธ๊ฑธ ์ €์žฅํ•ด๋†“๊ณ , coroutine์—์„œ ํŽ‘ํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ทธ๊ฑธ ๋‹ค์‹œ ๊บผ๋‚ด์ฃผ๋Š” ํ˜•ํƒœ๊ฐ€ ๋˜์—ˆ๋‹ค.

์ด์ œ ๋ฐ‘์ค€๋น„๋Š” ๋˜์—ˆ์œผ๋‹ˆ, ํ•จ์ˆ˜๋งŒ ์ •์˜ํ•˜๋ฉด ๋œ๋‹ค.

coroutine<int> new_counter() {
	int count = 0;

	while (true) {
		count++;
		co_yield count; // ๋ฐ˜ํ™˜ํ•œ ํ›„, ๋‹ค์‹œ ์‹คํ–‰๋˜๋ฉด ๋‹ค์‹œ ๋Œ์•„์˜ด
	}
}

๋ณ€์ˆ˜๋ฅผ ํ•˜๋‚˜ ๊น”๊ณ  ๋ฌดํ•œ๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ ๊ณ„์† ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ–ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๋‹ค.

๊ทธ๋Ÿผ ์ด์ œ ์‚ฌ์šฉ์ž๋‹จ์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ ‡๋‹ค.




co_return ๊ตฌ๋ฌธ

co_return์€ ์ฝ”๋ฃจํ‹ด์„ ์ข…๋ฃŒ์‹œํ‚ค๋Š” ๋ช…๋ น์ด๋‹ค.
co_yield๊ฐ€ ์ž„์‹œ๋กœ ๋ฐ˜ํ™˜๋งŒ ํ•ด์„œ ๋‹ค์‹œ ๋Œ์•„์˜ฌ ์ˆ˜ ์žˆ์—ˆ๋‹ค๋ฉด, co_return์€ ์•„์˜ˆ ์ข…๋ฃŒ๋ฅผ ์‹œ์ผœ๋ฒ„๋ฆฐ๋‹ค.

๋งŒ์•ฝ ์œ„ ์นด์šดํ„ฐ ์ฝ”๋“œ์—์„œ ์ˆซ์ž๊ฐ€ 3์— ๋„๋‹ฌํ–ˆ์„๋•Œ ์ข…๋ฃŒ๋ฅผ ์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿผ ์ฝ”๋ฃจํ‹ด์ด done ์ƒํƒœ๊ฐ€ ๋˜๋Š”๋ฐ, ์ด ์ƒํƒœ์—์„œ resume์„ ์‹œ๋„ํ•˜๋ฉด ์˜ˆ์™ธ๋„ ์•„๋‹ˆ๊ณ  ํ”„๋กœ๊ทธ๋žจ์ด ํ„ฐ์ง„๋‹ค.
์ดํ•ด๋Š” ์•ˆ๋˜์ง€๋งŒ ๊ทธ๋ ‡๋‹ค.

๊ทธ๋ž˜์„œ ์ถ”๊ฐ€์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๊ณ 

๋Œ๋ฆฌ๋ฉด

์ด๋ ‡๊ฒŒ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค.




co_await ๊ตฌ๋ฌธ

์ด๊ฑด ๋‹ค๋ฅธ ์–ธ์–ด์—์„œ ์‚ฌ์šฉํ•˜๋Š” async/await ์‹ ํƒ์Šค์™€ ํก์‚ฌํ•˜๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฝ”๋ฃจํ‹ด์ด ์ผ๋‹จ ์‹คํ–‰๋˜๊ฒŒ ๋ƒ…๋‘๋‹ค๊ฐ€, co_await๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ˜ธ์ถœ์ž๋Š” ํ”ผํ˜ธ์ถœ์ž(์ฝ”๋ฃจํ‹ด)๊ฐ€ ๋๋‚˜๊ธฐ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.


๊ทธ๋‹ค์Œ์—๋Š” awaitableํ•œ ํƒ€์ž…์„ ์ •์˜ํ•œ๋‹ค.
์‚ฌ์‹ค ์œ„์—์„œ ์“ด suspend_never์™€ suspend_always ๋“ฑ๋„ ์ด์™€ ๊ฐ™์€ ๊ตฌ์กฐ์˜ awaitable ๊ตฌํ˜„์ผ ๋ฟ์ด๋‹ค.

await_ready๋Š” ํ˜„์žฌ ์ฝ”๋ฃจํ‹ด์ด ์™„์ „ํžˆ ์ข…๋ฃŒ๋ผ์„œ co_await๋ฅผ ๋ฐ›์•„๋“ค์ผ ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ์ฒดํฌํ•˜๋Š” ๋ฉ”์„œ๋“œ๋‹ค.
๋๋‚ฌ์„๋•Œ true๋ฅผ ๋˜์ง€๋ฉด ๋œ๋‹ค.

await_suspend๋Š” co_await๋ฅผ ์™„๋ฃŒํ•  ์ˆ˜ ์—†์„๋•Œ(await_ready๊ฐ€ false์ผ๋•Œ), await_resume์€ co_await๋ฅผ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ์„ ๋•Œ(await_ready๊ฐ€ true์ผ๋•Œ) ํ˜ธ์ถœ๋œ๋‹ค.
์—ฌ๊ธฐ์„œ๋Š” ๊ตณ์ด ์‚ฌ์šฉํ• ์ผ์ด ์—†์–ด์„œ ๋น„์›Œ๋’€๋‹ค.

๊ทธ๋‹ค์Œ์— ์Šค๋ ˆ๋“œ ์Šคํฐ์€ ์ด๋Ÿฐ์‹์œผ๋กœ ๊ตฌ์„ฑํ–ˆ๊ณ 

์ด๋ ‡๊ฒŒ ์Šค๋ ˆ๋“œ๋ฅผ ์ด 2๊ฐœ ๋„์› ๋‹ค,

์ด 2๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์˜ฌ๋ฆฌ์ง€๋งŒ, co_await์œผ๋กœ ํ•ธ๋“ค๋ง์„ ํ•ด์„œ ์ฒซ๋ฒˆ์งธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜์–ด await๋œ ํ›„์— ๋‘๋ฒˆ์งธ ์Šค๋ ˆ๋“œ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํ–ˆ๋‹ค.

๊ทธ๋Ÿผ ์ด๋ ‡๊ฒŒ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค.



์ฐธ์กฐ
https://en.cppreference.com/w/cpp/language/coroutines
https://kukuta.tistory.com/222
https://kukuta.tistory.com/224