Coco is a C++20 coroutine library that aims to be convenient and simple to use.
Using CPM
CPMFindPackage( NAMEcocoVERSION4.6.0GIT_REPOSITORY"https://github.com/Curve/coco" )
Using FetchContent
include(FetchContent) FetchContent_Declare(coco GIT_REPOSITORY"https://github.com/Curve/coco"GIT_TAG v4.6.0) FetchContent_MakeAvailable(coco) target_link_libraries(<target>cr::coco)
A simple coroutine primitive. This coroutine is evaluated eagerly and does not return anything.
coco::stray basic()
{
co_awaitsomething();
}This coroutine is evaluated eagerly and returns a result of type T.
It is also possible to suspend the task until it is awaited (i.e. make it lazy evaluated) by calling co_await task<T>::make_lazy{}; from within it.
Warning
When calling co_await task<T>::make_lazy more than once or not as the first thing inside of a coroutine, the behavior is undefined.
coco::task<int> task()
{
co_awaitsomething();
co_return10;
}
coco::task<int> task_lazy()
{
co_await coco::task<int>::make_lazy{};
co_return10;
}
coco::stray basic()
{
auto result = co_awaittask();
auto lazy_result = co_awaittask_lazy();
// ...
}
voidnot_a_coroutine()
{
auto result = coco::await(task());
// or...coco::then(task(), [](int result) { /* ... */ });
}A replacement for std::promise<T> / std::future<T> that allows to co_await the result.
coco::future<int> compute()
{
auto promise = coco::promise<int>{};
auto future = promise.get_future();
std::thread thread{[promise = std::move(promise)]() mutable
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
promise.set_value(10);
}};
thread.detach();
return future;
}
coco::stray basic()
{
auto result = co_awaitcompute();
// ...
}
voidnot_a_coroutine()
{
auto result = coco::await(compute());
// or...coco::then(compute(), [](int result) { /* ... */ });
}Warning
Do not use coco::future<T> as a primitive to reschedule a coroutine to another thread! Use coco::courier instead.
A simple generator with iterator support.
This generator does not support yielding another generator (i.e. no support for ranges::elements_of).
Convenience member functions such as find_if, find and skip are also provided.
coco::generator<int> generator()
{
for (auto i = 0; 10 > i; ++i)
{
co_yield i;
}
}
voidnot_a_coroutine()
{
for (constauto& value : generator())
{
// ...
}
}A simple generator that can use co_await internally.
As with coco::generator<T>, this generator does not support yielding another generator (i.e. no support for ranges::elements_of).
task<int> some_task(int);
coco::async_generator<int> generator()
{
for (auto i = 0; 10 > i; ++i)
{
co_yieldco_awaitsome_task(i);
}
};
coco::stray basic()
{
auto gen = generator();
auto end = gen.end();
for (auto it = co_await gen.begin(); it != end; co_await ++it)
{
std::println("{}", *it);
}
}A simple latch which can be awaited.
coco::latch latch{2};
coco::stray basic()
{
co_awaitsomething();
latch.count_down();
}
coco::stray wait()
{
basic();
basic();
co_await latch; }Convenience functions for awaitable objects.
task<int> some_task();
task<int> some_lazy_task();
voidnot_a_coroutine()
{
// Discard a lazy `task<T>`coco::forget(some_lazy_task());
// Synchronously await any awaitableauto result = coco::await(some_task());
// Invoke callback when awaitable is resolvedcoco::then(some_task(), [](int) { /* ... */ });
// Await multiple awaitablesauto [r1, r2] = coco::await(coco::when_all(some_task(), some_lazy_task()));
}A simple primitive that can be used to reschedule a coroutine to another thread / context.
Unlike coco::future<T>, coco::transition is guaranteed to be resumed on the thread that calls coco::courier::schedule().
template <typename F>
voidrun_on_thread_pool(F&&);
coco::stray basic()
{
auto [courier, transition] = coco::courier::create();
constauto id = std::this_thread::get_id();
run_on_thread_pool([courier = std::move(courier)]() mutable { std::move(courier).schedule(); });
co_awaitstd::move(transition);
assert(std::this_thread::get_id() != id);
}