Search code examples
c++c++20c++-coroutine

Turning a function call which takes a callback into a coroutine


I am exploring and trying to learn C++ Coroutines (added in C++20). An SDK I am using has asynchronous API calls which all take a callback, the callbacks are invoked on some background thread managed by the SDK.


namespace third_party {

bool api_call(const std::string& some_parameter, const std::function<void(std::error_code)>& callback);

} // namespace third_party

I would like wrap this API call into something which can be awaited instead:


namespace my_third_party_sdk_wrapper {

cppcoro::task<std::error_code> api_call(const std::string& some_parameter);
cppcoro::task<std::error_code> api_call(const std::string& some_parameter, cppcoro::cancellation_token token);

} // namespace my_third_party_sdk_wrapper 

I am considering using the cppcoro lib but that isn't a requirement unless the implementation of the wrapper gets much simpler by doing so.

The problem is I cannot figure out how implement the wrapper.


Solution

  • There's a really good article by Raymond Chen, you can find it here.

    In your case, you can do something like this.

    namespace my_third_party_async_sdk_wrapper 
    {
    
        auto api_call_async(const std::string& some_parameter)
        {
              struct awaiter : public std::experimental::suspend_always
              {
                   awaiter(const std::string &parameter)
                   :parameter_(parmeter) {}
    
                   bool await_ready() { return true; }
    
                   void await_suspend(std::experimental::coroutine_handle<> handle)
                   { 
                       // use your third party lib call directly here.
                       api_call(parameter_, [](std::error_code ec) 
                       { 
                           // call the handle to resume the coroutine
                           handle(); 
                       }
                   }
              };
              return awaiter(some_parameter);
        }
    
    }
    

    This should do what you want.