SkillAgentSearch skills...

Cppcoro

A library of C++ coroutine abstractions for the coroutines TS

Install / Use

/learn @lewissbaker/Cppcoro

README

CppCoro - A coroutine library for C++

The 'cppcoro' library provides a large set of general-purpose primitives for making use of the coroutines TS proposal described in N4680.

These include:

This library is an experimental library that is exploring the space of high-performance, scalable asynchronous programming abstractions that can be built on top of the C++ coroutines proposal.

It has been open-sourced in the hope that others will find it useful and that the C++ community can provide feedback on it and ways to improve it.

It requires a compiler that supports the coroutines TS:

  • Windows + Visual Studio 2017 Windows Build Status
  • Linux + Clang 5.0/6.0 + libc++ Build Status

The Linux version is functional except for the io_context and file I/O related classes which have not yet been implemented for Linux (see issue #15 for more info).

Class Details

task<T>

A task represents an asynchronous computation that is executed lazily in that the execution of the coroutine does not start until the task is awaited.

Example:

#include <cppcoro/read_only_file.hpp>
#include <cppcoro/task.hpp>

cppcoro::task<int> count_lines(std::string path)
{
  auto file = co_await cppcoro::read_only_file::open(path);

  int lineCount = 0;

  char buffer[1024];
  size_t bytesRead;
  std::uint64_t offset = 0;
  do
  {
    bytesRead = co_await file.read(offset, buffer, sizeof(buffer));
    lineCount += std::count(buffer, buffer + bytesRead, '\n');
    offset += bytesRead;
  } while (bytesRead > 0);

  co_return lineCount;
}

cppcoro::task<> usage_example()
{
  // Calling function creates a new task but doesn't start
  // executing the coroutine yet.
  cppcoro::task<int> countTask = count_lines("foo.txt");

  // ...

  // Coroutine is only started when we later co_await the task.
  int lineCount = co_await countTask;

  std::cout << "line count = " << lineCount << std::endl;
}

API Overview:

// <cppcoro/task.hpp>
namespace cppcoro
{
  template<typename T>
  class task
  {
  public:

    using promise_type = <unspecified>;
    using value_type = T;

    task() noexcept;

    task(task&& other) noexcept;
    task& operator=(task&& other);

    // task is a move-only type.
    task(const task& other) = delete;
    task& operator=(const task& other) = delete;

    // Query if the task result is ready.
    bool is_ready() const noexcept;

    // Wait for the task to complete and return the result or rethrow the
    // exception if the operation completed with an unhandled exception.
    //
    // If the task is not yet ready then the awaiting coroutine will be
    // suspended until the task completes. If the the task is_ready() then
    // this operation will return the result synchronously without suspending.
    Awaiter<T&> operator co_await() const & noexcept;
    Awaiter<T&&> operator co_await() const && noexcept;

    // Returns an awaitable that can be co_await'ed to suspend the current
    // coroutine until the task completes.
    //
    // The 'co_await t.when_ready()' expression differs from 'co_await t' in
    // that when_ready() only performs synchronization, it does not return
    // the result or rethrow the exception.
    //
    // This can be useful if you want to synchronize with the task without
    // the possibility of it throwing an exception.
    Awaitable<void> when_ready() const noexcept;
  };

  template<typename T>
  void swap(task<T>& a, task<T>& b);

  // Creates a task that yields the result of co_await'ing the specified awaitable.
  //
  // This can be used as a form of type-erasure of the concrete awaitable, allowing
  // different awaitables that return the same await-result type to be stored in
  // the same task<RESULT> type.
  template<
    typename AWAITABLE,
    typename RESULT = typename awaitable_traits<AWAITABLE>::await_result_t>
  task<RESULT> make_task(AWAITABLE awaitable);
}

You can create a task<T> object by calling a coroutine function that returns a task<T>.

The coroutine must contain a usage of either co_await or co_return. Note that a task<T> coroutine may not use the co_yield keyword.

When a coroutine that returns a task<T> is called, a coroutine frame is allocated if necessary and the parameters are captured in the coroutine frame. The coroutine is suspended at the start of the coroutine body and execution is returned to the caller and a task<T> value that represents the asynchronous computation is returned from the function call.

The coroutine body will start executing when the task<T> value is co_awaited. This will suspend the awaiting coroutine and start execution of the coroutine associated with the awaited task<T> value.

The awaiting coroutine will later be resumed on the thread that completes execution of the awaited task<T>'s coroutine. ie. the thread that executes the co_return or that throws an unhandled exception that terminates execution of the coroutine.

If the task has already run to completion then awaiting it again will obtain the already-computed result without suspending the awaiting coroutine.

If the task object is destroyed before it is awaited then the coroutine never executes and the destructor simply destructs the captured parameters and frees any memory used by the coroutine frame.

shared_task<T>

The shared_task<T> class is a coroutine type that yields a single value asynchronously.

It is 'lazy' in that execution of the task does not start until it is awaited by some coroutine.

It is 'shared' in that the task value can be copied, allowing multiple references to the result of the task to be created. It also allows multiple coroutines to concurrently await the result.

The task will start executing on the thread that first co_awaits the task. Subsequent awaiters will either be suspended and be queued for resumption when the task completes or will continue synchronously if the task has already run to completion.

If an awaiter is suspended while waiting for the task to complete then it will be resumed on the thread that completes execution of the task. ie. the thread that executes the co_return or that throws the unhandled exception that terminates execution of the coroutine.

API Summary

namespace cppcoro
{
  template<typename T = void>
  class shared_task
  {
  public:

    using promise_type = <unspecified>;
    using value_type = T;

    shared_task() noexcept;
    shared_task(const shared_task& other) noexcept;
    shared_task(shared_task&& other) noexcept;
    shared_task& operator=(const shared_task& other) noexcept;
    shared_task& operator=(shared_task&& other) noexcept;

    void swap(shared_task& other) noexcept;

    // Query if the task has completed and the result is ready.
    bool is_ready() const noexcept;

    // Returns an operation that when awaited will suspend the
    // current coroutine until the task completes and the result
    // is available.
    //
    // The type of the result of the 'co_await someTask' expression
    // is an l-value reference to the task's result value (unless T
    // is void in which case the expression has type 'void').
    // If the task completed with an unhandled exception then the
    // exception will be rethrown by the co_await expression.
    Awaiter<T&> operator co_await() const noexcept;

    // Returns an operation that when awaited will suspend the
    // calling coroutine until the task completes and the result
    // is available.
    //
    // The result is not returned from the co_await expression.
    // This can be used to synchronize with the task without the
    // possibility 

Related Skills

View on GitHub
GitHub Stars3.8k
CategoryDevelopment
Updated1d ago
Forks505

Languages

C++

Security Score

100/100

Audited on Mar 23, 2026

No findings