{"id":24499821,"url":"https://github.com/cyanide-burnout/compromise","last_synced_at":"2025-09-01T01:09:27.233Z","repository":{"id":68103550,"uuid":"555782955","full_name":"cyanide-burnout/Compromise","owner":"cyanide-burnout","description":"Simple C++ coroutine helper library","archived":false,"fork":false,"pushed_at":"2024-03-11T09:27:10.000Z","size":95,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-21T22:15:51.423Z","etag":null,"topics":["coroutine","cpp"],"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/cyanide-burnout.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2022-10-22T10:14:33.000Z","updated_at":"2022-10-22T11:57:47.000Z","dependencies_parsed_at":"2024-03-11T10:50:53.007Z","dependency_job_id":null,"html_url":"https://github.com/cyanide-burnout/Compromise","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/cyanide-burnout%2FCompromise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanide-burnout%2FCompromise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanide-burnout%2FCompromise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanide-burnout%2FCompromise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cyanide-burnout","download_url":"https://codeload.github.com/cyanide-burnout/Compromise/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243696700,"owners_count":20332822,"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":["coroutine","cpp"],"created_at":"2025-01-21T22:15:57.466Z","updated_at":"2025-03-15T07:20:35.260Z","avatar_url":"https://github.com/cyanide-burnout.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Compromise\n\nSimple C++ coroutine helper library, suitable to integrate with main loop.\n\nArtem Prilutskiy, 2022-2025\n\n*Please note*, examples are incomplete but functional :)\n\n## Features\n\n* Can be integrated to main loop\n* Allows call coroutines from coroutines asynchronously (asymmetric coroutines)\n* Allows proceed coroutines status in callbacks (see Hook)\n* Provides base interface to create asynchronous wrappers to be called from coroutines (Compromise::Emitter)\n* **Not thread safe**\n\n## Main components\n\n### Compromise::Task / Compromise::Future\n\nCompromise::Future is main type that represents a coroutine, Compromise::Task is just an alias to simplify readability.\n\n```C++\nCompromise::Task TestYield()\n{\n  co_yield std::any();\n\n  for (int number = 0; number \u003c 10; number ++)\n    co_yield number;\n}\n\n```\n\nThere is no suspend on the start of coroutine. In case when you need to suspend coroutine immedietly after the start you can always use co_yield with empty std::any().\nNow is about co_return. To make coroutines more generic, there is no support of return values, but you can always use co_yield to pass result before exiting.\n\nCompromise::Future is a future class implementation no manage coroutine.\n\n* Can be created on the stack as well as on the heap\n* Controls live-cycle of coroutine\n* Provides awaitable interface to the caller, which allows to call one coroutine from another one\n\n#### Methods\n\n* **done()** - coroutine done or not exists\n* **value()** - get last value, passed by co_yield\n* **resume()** - resumes coroutine\n* **handle()** - get coroutine handle\n* **rethrow()** - rethrows an unhandled exception (if any)\n* **release()** - releases binding between coroutine and future\n* **operator bool()** - coroutine exists and not done\n* **operator ()()** - resumes coroutine and returns a value, passed by co_yield (synchronous call, value may be not set in case of incomplete execution)\n* **operator co_await()** - resumes coroutine and returns a value, passed by co_yield (asynchronous call)\n\n```C++\nCompromise::Task TestInvokeFromCoroutine()\n{\n  auto routine = TestYield();\n\n  while (routine)\n  {\n    auto\u0026 data = co_await routine;\n    if (data.has_value()) printf(\"Result: %d\\n\", std::any_cast\u003cint\u003e(data));\n  }\n}\n\nvoid TestInvokeFromFunction()\n{\n  auto routine = TestYield();\n\n  while (routine)\n  {\n    auto\u0026 data = routine();\n    if (data.has_value()) printf(\"Result: %d\\n\", std::any_cast\u003cint\u003e(data));\n  }\n}\n\nCompromise::Task taskOnHeap = new Compromise::Task(TestYield());\n\n```\n\n#### Exception forwarding and handling\n\nAny unhandled exception in coroutine code will be forwarded to the main code.\n\n```C++\nCompromise::Task TestException()\n{\n  throw std::exception();\n}\n\nauto routine = TestException();\n\ntry\n{\n  routine.rethrow();\n}\ncatch (const std::exception\u0026 exception)\n{\n  printf(\"Unhandled exception: %s\\n\", exception.what());\n}\n\n\n```\n\n#### Hook\n\nCompromise::Future allows you to hook events of coroutine in callback manner. For example for cases, when you need to wait a result without pooling a coroutine or collect  garbage.\n\n```C++\nauto routine = new Compromise::Future(TestYield());\nroutine-\u003ehook = [] (Compromise::Future* future, Compromise::Status status) -\u003e bool\n{\n  if (status == Compromise::Yield)\n  {\n    // This code will be called on co_yield\n    auto\u0026 data = future-\u003evalue();\n    if (data.has_value()) printf(\"Test hook: %d\\n\", std::any_cast\u003cint\u003e(data));\n    return true;  // Don't suspend a coroutine, the result is already handled\n  }\n\n  if (status == Compromise::Return)\n  {\n    // This code will be called after the end of coroutine's execution\n    printf(\"Test hook done\\n\");\n    future-\u003erelease();  // Unbind coroutine from the future to prevent collision and fault on future's destruction\n    delete future;      // Since we created future on the heap we have to delete it\n    return true;        // Allow coroutine to destruct itself\n  }\n\n  return false;  // Suspend a coroutine (just an example)\n};\n\n// To avoid a loose of hook, coroutine used call co_yield Compromise::Empty() to suspend its execution immediately after creation\nroutine-\u003eresume();\n```\n\n### Compromise::Emitter\n\nCompromise::Emitter is a wrapper to transform callback-style code into an awaitable, where Type is a type of object to return in co_await.\n\n#### Methods:\n\n* **wake(value)** - resumes coroutine\n* **update(value\u0026)** - optional virtual method, called by Emitter to initiate asynchronous call or update the value immediately\n\n```C++\nCompromise::Task TestClient(CloudClient* context)\n{\n  auto result = co_await CoCloud::Client(context, \"https://ya.ru/robots.txt\");\n\n  if (result.code \u003e= 0) printf(\"HTTP Code %d: %s\\n\", result.code, result.data);\n}\n\nCompromise::Task TestResolver(struct ResolverState* context)\n{\n  CoResolver::Query query1(context, \"127.0.0.1\");\n  CoResolver::Query query2(context, \"rotate.aprs.net\", AF_INET);\n\n  PrintHostAddress(co_await query1);\n\n  for (int count = 0; count \u003c 10; count ++) PrintHostAddress(co_await query2);\n}\n```\n\nHere is an example of wrapper code:\n\n```C++\nstruct ResolverEvent\n{\n  int status;\n  int timeouts;\n  struct hostent* entry;\n};\n\nclass Query : public Compromise::Emitter\u003cResolverEvent\u003e\n{\n  public:\n\n    Query(struct ResolverState* state, const char* name, int family = AF_UNSPEC) : state(state), name(name), family(family)\n    {\n      ClearHostEntryIterator(\u0026iterator);\n    }\n\n  private:\n\n    int family;\n    const char* name;\n    struct ResolverState* state;\n    struct HostEntryIterator iterator;\n\n    bool update(Event\u0026 data)\n    {\n      ResolveHostAddress(state, name, family, invoke, this, \u0026iterator);\n      return false;  // Update is incomplete, suspend coroutine and wait for call to wake()\n    }\n\n    static void invoke(void* argument, int status, int timeouts, struct hostent* entry)\n    {\n      CoResolver::Query* self = static_cast\u003cCoResolver::Query*\u003e(argument);\n      ValidateHostEntry(\u0026status, entry, \u0026self-\u003eiterator);\n      self-\u003ewake({ status, timeouts, entry });\n    }\n};\n\n\nstruct PollEvent\n{\n  int handle;\n  uint32_t flags;\n  uint64_t options;\n};\n\nclass CoPoll : public Compromise::Emitter\u003cPollEvent\u003e\n{\n  public:\n\n    CoPoll(struct FastPoll* poll) : poll(poll) {  }\n    ~CoPoll()                                  { RemoveAllEventHandlers(poll, invoke, this);            }\n\n    void add(int handle, uint64_t flags)       { AddEventHandler(poll, handle, flags, invoke, this);    }\n    void modify(int handle, uint64_t flags)    { ManageEventHandler(poll, handle, flags, invoke, this); }\n    void remove(int handle)                    { RemoveEventHandler(poll, handle);                      }\n\n  private:\n\n    struct FastPoll* poll;\n\n    static void invoke(int handle, uint32_t flags, void* data, uint64_t options)\n    {\n      static_cast\u003cCoPoll*\u003e(data)-\u003ewake({ handle, flags, options });\n    }\n};\n\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyanide-burnout%2Fcompromise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcyanide-burnout%2Fcompromise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyanide-burnout%2Fcompromise/lists"}