coroutine

Generator

// g++ -O3 -std=c++20 -Wall -Werror co.cc

#include <iostream>
#include <coroutine>

template <typename T>
class generator {
public:
  struct promise_type;
  using handle_type = std::coroutine_handle<promise_type>;
  handle_type h_;

  struct promise_type {
    T value_;
    std::exception_ptr exception_;
    generator<T> get_return_object() {
      return { handle_type::from_promise(*this) };
    }
    void unhandled_exception() { exception_ = std::current_exception(); }
    void return_void() {}
    std::suspend_always initial_suspend() noexcept { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    std::suspend_always yield_value(T v) noexcept {
      value_ = std::move(v);
      return {};
    }
  };

public:

  generator(handle_type h) : h_(h) {}
  ~generator() { h_.destroy(); }
  explicit operator bool() {
    next();
    return !h_.done();
  }

  T operator() () {
    next();
    cached_ = false;
    return std::move(h_.promise().value_);
  }

private:
  bool cached_ = false;
  void next() {
    if (cached_) {
      return;
    }
    h_();
    if (h_.promise().exception_) {
      std::rethrow_exception(h_.promise().exception_);
    }
    cached_ = true;
  }
};

generator<uint64_t> fib(uint64_t n) {
  uint64_t a = 0, b = 1;
  for (uint64_t i = 0; i <= n; ++i) {
    co_yield a;
    uint64_t t = b;
    b = a + b;
    a = t;
  }
}

int main(int argc, char *argv[]) {
  auto g = fib(10);
  while (g) {
    std::cout << g() << " ";
  }
  // ./a.out
  //  0 1 1 2 3 5 8 13 21 34 55
}

Boost ASIO Echo Server

#include <iostream>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/write.hpp>

using boost::asio::ip::tcp;
using boost::asio::awaitable;
using boost::asio::co_spawn;
using boost::asio::detached;
using boost::asio::use_awaitable;
namespace this_coro = boost::asio::this_coro;

constexpr uint64_t BUFSIZE = 1024;

awaitable<void> echo(tcp::socket &socket) {
  for (;;) {
    char data[BUFSIZE] = {0};
    auto n = co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable);
    co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable);
  }
}

awaitable<void> handle(tcp::socket socket) {
  try {
    co_await echo(socket);
  } catch(const std::exception &e) {
    std::cerr << e.what();
  }
}

awaitable<void> listener() {
  auto e = co_await this_coro::executor;
  tcp::acceptor acceptor(e, {tcp::v4(), 8888});
  for (;;) {
    tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
    co_spawn(e, handle(std::move(socket)), detached);
  }
}

int main(int argc, char *argv[]) {
  boost::asio::io_context io_context;
  boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
  signals.async_wait([&](auto, auto){ io_context.stop(); });
  co_spawn(io_context, listener(), detached);
  io_context.run();
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
set(target a.out)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
project(example)
find_package(Boost)
add_executable(${target} a.cc)
target_include_directories(${target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(${target} PRIVATE "${Boost_INCLUDE_DIR}")
target_link_libraries(${target} ${Boost_LIBRARIES})
target_link_libraries(${target} INTERFACE Boost::coroutine)