/coro

Single-header library facilities for C++2a Coroutines

Primary LanguageC++

coro

This is a collection of single-header library facilities for C++2a Coroutines.

coro/include/

co_future.h

Provides co_future<T>, which is like std::future<T> but models Awaitable.

co_optional.h

Provides co_optional<T>, which is like std::optional<T> but is awaitable. This is based on code originally by Toby Allsopp. Notice that co_optional<T> is awaitable only in coroutine contexts where the return type is itself co_optional<U> — not, say, task<U> or generator<U> — and therefore co_optional<T> does not model the P1288R0 Awaitable concept.

concepts.h

Provides definitions for the concepts and type-traits from Lewis Baker's P1288R0, based on his own reference implementation.

  • concept Awaitable
  • concept AwaitableOf<R>
  • concept Awaiter
  • concept AwaiterOf<R>
  • awaiter_type_t<T>
  • await_result_t<T>
  • get_awaiter(Awaitable t)

gor_generator.h

Gor Nishanov's generator<R>. The difference between this one and "mcnellis_generator.h" is that this one stores the value of coro_.done() in a bool member variable. That change makes it more friendly to the compiler's optimizer. This is the only generator that works as intended with "disappearing_coroutine.cpp".

This generator is move-only.

mcnellis_generator.h

James McNellis's int_generator example from "Introduction to C++ Coroutines" (CppCon 2016), templatized into generator<T>. I had to fill in some boilerplate he didn't show, such as iterator comparison, return_void/unhandled_exception, and several constructors. Any mistakes are likely mine, not his.

This generator is neither moveable nor copyable.

shared_generator.h, unique_generator.h

unique_generator<R> is basically equivalent to cppcoro::generator<R>. It expresses unique ownership of a coroutine handle, which means it is move-only. This is the most natural way to implement a generator object, but it does have the downside that it is not a range-v3 viewable_range.

shared_generator<R> is basically equivalent to range-v3's ranges::experimental::generator<R>. It expresses reference-counted ownership of a coroutine handle, so that it is copyable. It is a full viewable_range and interoperates correctly with range-v3.

These generators' end() methods return a sentinel type instead of iterator, which means that these generators do not interoperate with the C++17 STL algorithms.

resumable_thing.h

James McNellis's resumable_thing example from "Introduction to C++ Coroutines" (CppCon 2016).

sync_wait.h

Lewis Baker's implementation of P1171 sync_wait(Awaitable t). Suppose you're in main(), and you have a task<int> that you've received from a coroutine. You can't co_await it, because as soon as you use the co_await keyword you turn into a coroutine yourself. If (and only if?) the coroutine is being executed in another thread, then you can pass the task off to sync_wait.

TODO: this needs some example code!

task.h, gor_task.h

task<R> is basically equivalent to cppcoro::task<R>. It models Awaitable (as defined in "concepts.h").

"gor_task.h" provides another implementation of task<R>, as shown in Gor Nishanov's "C++ Coroutines: Under the Covers" (CppCon 2016).

TODO: this needs some example code!

examples/

co_optional.cpp

Simple examples of using co_optional monadic operations with co_await and co_return.

co_optional_tests.cpp

Toby Allsopp's monadic optional comes with a test suite. This is that test suite.

disappearing_coroutine.cpp

Gor Nishanov's example of passing a generator to std::accumulate, from his talk "C++ Coroutines: Under the Covers" (CppCon 2016). Clang can optimize this down to a single printf; but only if you use "gor_generator.h". If you use one of the generators that doesn't cache coro_.done() in a data member, Clang will not be able to optimize it.

generate_ints.cpp

A very simple example of unique_generator with co_yield.

generator_as_viewable_range.cpp

Similar to generate_ints.cpp, this example demonstrates mixing Coroutines with Ranges. It uses shared_generator (which models ranges::viewable_range) and pipes the generator object through rv::take(10).

mcnellis_generator.cpp

James McNellis's int_generator example from "Introduction to C++ Coroutines" (CppCon 2016).

mcnellis_resumable_thing.cpp

James McNellis's first resumable_thing example from "Introduction to C++ Coroutines" (CppCon 2016).

mcnellis_resumable_thing_dangling.cpp

James McNellis's second resumable_thing example from "Introduction to C++ Coroutines" (CppCon 2016), showing the interleaved execution of two named_counter coroutines.

We show both McNellis's working named_counter, which captures a std::string by value, and a broken_named_counter that captures a std::string_view and thus suffers from a subtle dangling-reference bug whose effects are visible in the output.

p1288r0_concepts.cpp

Lewis Baker's reference implementation of P1288R0 comes with a test suite. This is that test suite.

pythagorean_triples_generator.cpp

Eric's Famous Pythagorean Triples, but using a shared_generator that co_yields tuples. This is almost identical to generator_as_viewable_range.cpp; it's just a slightly more interesting application.