home | help
std::coroutine_traits(3)       C++ Standard Libary	std::coroutine_traits(3)

NAME
     std::coroutine_traits - std::coroutine_traits

Synopsis
	Defined in header <coroutine>
	template< class R, class... Args >  (since C++20)
	struct coroutine_traits;

	Determines  the promise type from the return type and parameter types of
     a coroutine.
	The standard library implementation provides a publicly accessible  mem-
     ber type
	promise_type  same  as	R::promise_type if the qualified-id is valid and
     denotes a
	type. Otherwise, it has no such member.

	Program-defined specializations of coroutine_traits must define  a  pub-
     licly
	accessible  nested  type  promise_type,  otherwise  the  program is ill-
     formed.

Template parameters
	R    - return type of the coroutine
	Args - parameter types of the coroutine, including the	implicit  object
     parameter if
	       the coroutine is a non-static member function

	Nested types

	Name	     Definition
	promise_type  R::promise_type if it is valid, or provided by program-de-
     fined
		     specializations

Possible implementation
	namespace detail {
	template<class, class...>
	struct coroutine_traits_base {};

	template<class R, class... Args>
	requires requires { typename R::promise_type; }
	struct coroutine_traits_base <R, Args...>
	{
	    using promise_type = R::promise_type;
	};
	}

	template<class R, class... Args>
	struct coroutine_traits : detail::coroutine_traits_base<R, Args...> {};

Notes
	If the coroutine is a non-static member function, then the first type in
     Args... is
	the type of the implicit object parameter, and the  rest  are  parameter
     types of the
	function (if any).

	If  std::coroutine_traits<R, Args...>::promise_type does not exist or is
     not a class
	type, the corresponding coroutine definition is ill-formed.

	Users may define explicit or partial specializations of coroutine_traits
     dependent
	on program-defined types to avoid modification to return types.

Example
     // Run this code

      #include <chrono>
      #include <coroutine>
      #include <exception>
      #include <future>
      #include <iostream>
      #include <thread>
      #include <type_traits>

      // A program-defined type on which  the  coroutine_traits  specializations
     below depend
      struct as_coroutine {};

      // Enable the use of std::future<T> as a coroutine type
      // by using a std::promise<T> as the promise type.
      template<typename T, typename... Args>
	  requires(!std::is_void_v<T> && !std::is_reference_v<T>)
      struct std::coroutine_traits<std::future<T>, as_coroutine, Args...>
      {
	  struct promise_type : std::promise<T>
	  {
	      std::future<T> get_return_object() noexcept
	      {
		  return this->get_future();
	      }

	      std::suspend_never initial_suspend() const noexcept { return {}; }
	      std::suspend_never final_suspend() const noexcept { return {}; }

	      void return_value(const T& value)
		  noexcept(std::is_nothrow_copy_constructible_v<T>)
	      {
		  this->set_value(value);
	      }

	      void  return_value(T&&  value)  noexcept(std::is_nothrow_move_con-
     structible_v<T>)
	      {
		  this->set_value(std::move(value));
	      }

	      void unhandled_exception() noexcept
	      {
		  this->set_exception(std::current_exception());
	      }
	  };
      };

      // Same for std::future<void>.
      template<typename... Args>
      struct std::coroutine_traits<std::future<void>, as_coroutine, Args...>
      {
	  struct promise_type : std::promise<void>
	  {
	      std::future<void> get_return_object() noexcept
	      {
		  return this->get_future();
	      }

	      std::suspend_never initial_suspend() const noexcept { return {}; }
	      std::suspend_never final_suspend() const noexcept { return {}; }

	      void return_void() noexcept
	      {
		  this->set_value();
	      }

	      void unhandled_exception() noexcept
	      {
		  this->set_exception(std::current_exception());
	      }
	  };
      };

      // Allow co_await'ing std::future<T> and std::future<void>
      // by naively spawning a new thread for each co_await.
      template<typename T>
      auto operator co_await(std::future<T> future) noexcept
	  requires(!std::is_reference_v<T>)
      {
	  struct awaiter : std::future<T>
	  {
	      bool await_ready() const noexcept
	      {
		  using namespace std::chrono_literals;
		  return this->wait_for(0s) != std::future_status::timeout;
	      }

	      void await_suspend(std::coroutine_handle<> cont) const
	      {
		  std::thread([this, cont]
		  {
		      this->wait();
		      cont();
		  }).detach();
	      }

	      T await_resume() { return this->get(); }
	  };

	  return awaiter { std::move(future) };
      }

      // Utilize the infrastructure we have established.
      std::future<int> compute(as_coroutine)
      {
	  int a = co_await std::async([] { return 6; });
	  int b = co_await std::async([] { return 7; });
	  co_return a * b;
      }

      std::future<void> fail(as_coroutine)
      {
	  throw std::runtime_error("bleah");
	  co_return;
      }

      int main()
      {
	  std::cout << compute({}).get() << '\n';

	  try
	  {
	      fail({}).get();
	  }
	  catch (const std::runtime_error& e)
	  {
	      std::cout << "error: " << e.what() << '\n';
	  }
      }

Output:
      42
      error: bleah

http://cppreference.com 	   2024.06.10		std::coroutine_traits(3)

home | help