{"id":15651522,"url":"https://github.com/mpusz/mp-coro","last_synced_at":"2025-04-14T19:33:16.902Z","repository":{"id":139725177,"uuid":"430168302","full_name":"mpusz/mp-coro","owner":"mpusz","description":"Coroutine support tools","archived":false,"fork":false,"pushed_at":"2025-03-12T18:59:14.000Z","size":121,"stargazers_count":34,"open_issues_count":2,"forks_count":7,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-28T07:51:11.331Z","etag":null,"topics":["cmake","coroutines","cpp20","library"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mpusz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-20T17:36:15.000Z","updated_at":"2025-03-25T10:37:43.000Z","dependencies_parsed_at":"2024-10-03T12:39:28.849Z","dependency_job_id":"57d8abd1-aa7f-4e86-9509-317a3aaf298e","html_url":"https://github.com/mpusz/mp-coro","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpusz%2Fmp-coro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpusz%2Fmp-coro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpusz%2Fmp-coro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpusz%2Fmp-coro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mpusz","download_url":"https://codeload.github.com/mpusz/mp-coro/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248945741,"owners_count":21187376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cmake","coroutines","cpp20","library"],"created_at":"2024-10-03T12:38:52.857Z","updated_at":"2025-04-14T19:33:16.856Z","avatar_url":"https://github.com/mpusz.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GitHub license](https://img.shields.io/github/license/mpusz/mp-coro?cacheSeconds=3600\u0026color=informational\u0026label=License)](./LICENSE.md)\n\n# `mp-coro` - A C++20 coroutine support library\n\nThe library originated as a support code for the [\"Introduction to C++20\"](https://train-it.eu/trainings/cpp/78-cpp-20)\nworkshop. The workshop covers the design choices and implementation steps needed to implement most of\nthe features provided here.\n\nOn the way it turned out that `cppcoro` is not maintained anymore and there are no good libraries of similar\nquality (at least I was not able to find them). As a result this library was refactored and cleaned up\nin the hope that others will find it useful and that the C++ community can provide feedback on it and ways\nto improve it.\n\nStill, one of the main goals of the library is to provide clean, short, simple but powerful interfaces to help\nC++ engineers learn how C++ coroutines work.\n\n\n## Acknowledgments\n\nThe design of this library is heavily influenced by:\n- `cppcoro` library by Lewis Baker, https://github.com/lewissbaker/cppcoro\n- \"Asymmetric Transfer\" blog by Lewis Baker, https://lewissbaker.github.io\n- \"Understanding C++ coroutines by example\" by Pavel Novikov on C++ London, https://www.youtube.com/watch?v=7sKUAyWXNHA\n\n\n## Differences from `cppcoro`\n\n### General\n\n- Compatible with the C++20 Standard rather then Coroutines TS specification\n- CMake usage allows better adoptability\n- Usage of C++20 synchronization features increase portability\n- Easier, stronger, simpler interfaces and implementation thanks to\n  - RAII usage for managing coroutine lifetime\n  - usage of C++20 synchronization features\n  - usage of C++20 concepts\n  - usage of other C++20 features (i.e. spaceship operator)\n  - using `[[nodiscard]]` attributes in much more places\n  - `synchronized_task\u003cSync, T\u003e` class template\n    - task coroutine return type for handling non-standard (coroutine-like) synchronization\n    - takes a synchronization primitive as the first template parameter\n    - used in `sync_await` and `when_all`\n  - `storage\u003cT\u003e` class template\n    - responsible for storage of the result or the current exception\n    - implementation uses `std::variant` under the hood\n    - interface similar to `std::promise`/`std::future` pair\n    - could be replaced with `std::expected` proposed in [P0323](https://wg21.link/p0323) in the future\n    - used in `task`, `synchronized_task`, and `async`\n  - `nonvoid_storage\u003cT\u003e` class template\n    - returns `void_type` for a task of `void`\n    - needed for `when_all`\n  - `task_promise_storage\u003cT\u003e` class template\n    - separates coroutine promise logic from its storage\n    - used in `task` and `synchronized_task`\n    - NOTE: It is an undefined behavior to have `return_value()` and `return_result` in a coroutine\n      promise type (even if mutually exclusive thanks to constraining with C++20 concepts) :-(\n\n\n### `task\u003cT\u003e`\n\n- Not default-constructible\n- Internal storage is not mutable for task lvalues (`const` reference returned to the user)\n\n```cpp\n// task\u003cint\u003e\nstatic_assert(awaitable_of\u003ctask\u003cint\u003e, int\u0026\u0026\u003e);\nstatic_assert(awaitable_of\u003ctask\u003cint\u003e\u0026, const int\u0026\u003e);\nstatic_assert(awaitable_of\u003cconst task\u003cint\u003e\u0026, const int\u0026\u003e);\nstatic_assert(awaitable_of\u003ctask\u003cint\u003e\u0026\u0026, int\u0026\u0026\u003e);\n\n// task\u003cint\u0026\u003e\nstatic_assert(awaitable_of\u003ctask\u003cint\u0026\u003e, int\u0026\u003e);\nstatic_assert(awaitable_of\u003ctask\u003cint\u0026\u003e\u0026, int\u0026\u003e);\nstatic_assert(awaitable_of\u003cconst task\u003cint\u0026\u003e\u0026, int\u0026\u003e);\nstatic_assert(awaitable_of\u003ctask\u003cint\u0026\u003e\u0026\u0026, int\u0026\u003e);\n\n// task\u003cconst int\u0026\u003e\nstatic_assert(awaitable_of\u003ctask\u003cconst int\u0026\u003e, const int\u0026\u003e);\nstatic_assert(awaitable_of\u003ctask\u003cconst int\u0026\u003e\u0026, const int\u0026\u003e);\nstatic_assert(awaitable_of\u003cconst task\u003cconst int\u0026\u003e\u0026, const int\u0026\u003e);\nstatic_assert(awaitable_of\u003ctask\u003cconst int\u0026\u003e\u0026\u0026, const int\u0026\u003e);\n\n// task\u003cvoid\u003e\nstatic_assert(awaitable_of\u003ctask\u003cvoid\u003e, void\u003e);\nstatic_assert(awaitable_of\u003ctask\u003cvoid\u003e\u0026, void\u003e);\nstatic_assert(awaitable_of\u003cconst task\u003cvoid\u003e\u0026, void\u003e);\nstatic_assert(awaitable_of\u003ctask\u003cvoid\u003e\u0026\u0026, void\u003e);\n```\n\n### `sync_await()`\n\n- Uses `std::binary_semaphore` for synchronization\n- Much cleaner and shorter design\n\n\n### `when_all()`\n\n- Returns and instance of `void_type` in a tuple of results in case of `awaitable_of\u003cvoid\u003e`\n- Much cleaner and shorter design\n\n\n### `generator`\n\n- Not default-constructible\n- Produced values are not mutable (`const_iterator` returned to the user)\n- As this is lazy synchronous generator a promise type does not waste space for storing\n  `std::exception_ptr` but instead rethrows the exception right away\n  - also no branches are taken in `begin()` and `operator++` to check if an exception should\n    be re-thrown\n- Returns `std::default_sentinel_t` from `end()` which immediately makes it usable with\n  `std::counted_iterator` and possibly other facilities\n\n```cpp\nstatic_assert(!awaitable\u003cgenerator\u003cint\u003e\u003e);\nstatic_assert(std::ranges::input_range\u003cgenerator\u003cint\u003e\u003e);\n```\n\n```cpp\nmp_coro::generator\u003cstd::uint64_t\u003e fibonacci()\n{\n  std::uint64_t a = 0, b = 1;\n  while (true) {\n    co_yield b;\n    a = std::exchange(b, a + b);\n  }\n}\n\nvoid f()\n{\n  auto gen = fibonacci();\n  for(auto i : std::views::counted(gen.begin(), 10))\n    std::cout \u003c\u003c i \u003c\u003c ' ';\n  std::cout \u003c\u003c '\\n';\n}\n```\n\n\n## New features\n\n### C++20 Concepts\n\n#### `awaiter\u003cT\u003e`\n\nA concept that indicates a type contains the `await_ready()`, `await_suspend()` and `await_resume()`\nmember functions required to implement the protocol for suspending/resuming an awaiting coroutine.\n\n#### `awaiter_of\u003cT, Value\u003e`\n\nA concept that ensures that type `T` is an awaiter and that `await_resume()` returns `Value` type.\n\n#### `awaitable\u003cT\u003e`\n\nA concept that indicates that a type can be `co_await`ed in a coroutine context that has no\n`await_transform()` overloads.\n\nAny type that implements the `awaiter\u003cT\u003e` concept also implements the `awaitable\u003cT\u003e` concept.\n\n#### `awaitable_of\u003cT, Value\u003e`\n\nA concept that ensures that type `T` is an awaitable and that `await_resume()` returns `Value` type.\n\nFor example, the type `task\u003cT\u003e` implements the concept `awaitable_of\u003cT\u0026\u0026\u003e` whereas the type\n`task\u003cT\u003e\u0026` implements the concept `awaitable_of\u003cconst T\u0026\u003e`.\n\n#### `task_result\u003cT\u003e`\n\nA concept that ensures that the task result type is either `std::move_constructible` or a\nreference or a `void` type.\n\n\n### `coro_ptr`\n\nA `std::unique_ptr` with a custom deleter.\n\n\n### `async`\n\nAwaitable that allows to asynchronously `co_await` on any invocable. More efficient than `std::async`\nas it never allocates memory for shared `std::promise`/`std::future` storage.\n\nNOTE: `async` should be `co_await`ed only once and that is why it works only for rvalues.\n\n```cpp\n// async\u003cint(*)()\u003e\nstatic_assert(awaitable_of\u003casync\u003cint(*)()\u003e, int\u0026\u0026\u003e);\nstatic_assert(awaitable_of\u003casync\u003cint(*)()\u003e\u0026\u0026, int\u0026\u0026\u003e);\nstatic_assert(!awaitable\u003casync\u003cint(*)()\u003e\u0026\u003e);\nstatic_assert(!awaitable\u003cconst async\u003cint(*)()\u003e\u0026\u003e);\n\n// async\u003cint\u0026(*)()\u003e\nstatic_assert(awaitable_of\u003casync\u003cint\u0026(*)()\u003e, int\u0026\u003e);\nstatic_assert(awaitable_of\u003casync\u003cint\u0026(*)()\u003e\u0026\u0026, int\u0026\u003e);\nstatic_assert(!awaitable\u003casync\u003cint\u0026(*)()\u003e\u0026\u003e);\nstatic_assert(!awaitable\u003cconst async\u003cint\u0026(*)()\u003e\u0026\u003e);\n\n// async\u003cconst int\u0026(*)()\u003e\nstatic_assert(awaitable_of\u003casync\u003cconst int\u0026(*)()\u003e, const int\u0026\u003e);\nstatic_assert(awaitable_of\u003casync\u003cconst int\u0026(*)()\u003e\u0026\u0026, const int\u0026\u003e);\nstatic_assert(!awaitable\u003casync\u003cconst int\u0026(*)()\u003e\u0026\u003e);\nstatic_assert(!awaitable\u003cconst async\u003cconst int\u0026(*)()\u003e\u0026\u003e);\n\n// async\u003cvoid(*)()\u003e\nstatic_assert(awaitable_of\u003casync\u003cvoid(*)()\u003e, void\u003e);\nstatic_assert(awaitable_of\u003casync\u003cvoid(*)()\u003e\u0026\u0026, void\u003e);\nstatic_assert(!awaitable\u003casync\u003cvoid(*)()\u003e\u0026\u003e);\nstatic_assert(!awaitable\u003cconst async\u003cint(*)()\u003e\u0026\u003e);\n```\n\n\n### `TRACE_FUNC()`\n\nA macro used across the library to facilitate debugging and learning of coroutines workflow.\nTracing level can be selected with `MP_CORO_TRACE_LEVEL` preprocessor define and CMake cache\nvariable.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpusz%2Fmp-coro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmpusz%2Fmp-coro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpusz%2Fmp-coro/lists"}