user2449761
user2449761

Reputation: 1229

Can coroutine return std::future? (unable to find the promise type for this coroutine)

I have tried to compile coroutine example from the CppCon presentation https://youtu.be/ZTqHjjm86Bw?t=560

enter image description here

Unfortunately compilation fails:

$ g++-10 -pedantic -Wall -std=c++20 -fcoroutines main.cpp 
main.cpp: In function ‘std::future<int> compute_value()’:
main.cpp:7:16: error: unable to find the promise type for this coroutine
    7 |   int result = co_await std::async([]
      |                ^~~~~~~~

At the beginning presenter warns that what he is about to present is just a proposal. So it makes me confused: can std::future be return from a coroutine, or do I just try to call it incorrectly?

Full code:

#include <coroutine>
#include <iostream>
#include <future>

std::future<int> compute_value(){
  int result = co_await std::async([] 
  {
    return 30;
  });

  co_return result;
}

int main() {
    std::cout << compute_value().get() << std::endl;
}

Upvotes: 4

Views: 3262

Answers (2)

Nicol Bolas
Nicol Bolas

Reputation: 473946

There are (basically1) no standard library types in C++20 that implement the necessary coroutine machinery to make coroutines work. This includes std::promise<T>/std::future<T>.

You could write wrappers for them that implement the coroutine machinery though.

1: There are support types like std::suspend_always/std::suspend_never which have coroutine machinery, but they don't really do anything like what you're thinking of.

Upvotes: 7

Oleksandr Kozlov
Oleksandr Kozlov

Reputation: 822

In C++20 a user should manually enable the use of std::future<T> as a coroutine type. A minimal complete example for std::future<int>:

#include <coroutine>
#include <future>
#include <iostream>

template <>
struct std::coroutine_traits<std::future<int>> {
  struct promise_type : std::promise<int> {
    std::future<int> get_return_object() { return this->get_future(); }
    std::suspend_never initial_suspend() noexcept { return {}; }
    std::suspend_never final_suspend() noexcept { return {}; }
    void return_value(int value) { this->set_value(value); }
    void unhandled_exception() {
      this->set_exception(std::current_exception());
    }
  };
};

auto operator co_await(std::future<int> future) {
  struct awaiter : std::future<int> {
    bool await_ready() { return false; } // suspend always
    void await_suspend(std::coroutine_handle<> handle) {
      std::thread([this, handle]() {
        this->wait();
        handle.resume();
      }).detach();
    }
    int await_resume() { return this->get(); }
  };
  return awaiter{std::move(future)};
}

std::future<int> compute_value() {
  int result = co_await std::async([] { return 30; });

  co_return result;
}

int main() { std::cout << compute_value().get() << std::endl; }

Reference: https://en.cppreference.com/w/cpp/coroutine/coroutine_traits

Upvotes: 2

Related Questions