首页 > 解决方案 > 将协程的返回设置为另一个协程内的变量会使编译器尝试转换承诺类型

问题描述

我目前遇到一个问题,我只是尝试 co_await 协程 B 将其返回保存到协程 A 内的变量中,但它尝试将 A 的承诺类型转换为 B 的承诺类型。

forStackoverflow.cpp: In function ‘myTask<std::optional<int> > getValue(std::vector<int>&, int, bool)’:
forStackoverflow.cpp:68:60: error: cannot convert ‘coroutine_handle<promise_type<std::optional<int>>>’ to ‘coroutine_handle<promise_type<int>>’
   68 |     leaf = co_await suspendableLocateValue(key, array, true);
      |                                                            ^
forStackoverflow.cpp:42:58: note:   initializing argument 1 of ‘void myTask<T>::await_suspend(std::__n4861::coroutine_handle<myTask<T>::promise_type>) const [with T = int]’
   42 |   void await_suspend(std::coroutine_handle<promise_type> coro) const noexcept { coro.promise().boolIsDone = true; }
      |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
#include <iostream>
#include <optional>
#include <utility>
#include <vector>
#include <concepts>
#include <coroutine>
#include <exception>
#include <iostream>
#include <vector>

template <typename T>
struct myTask {
  struct promise_type {
    T value_;
    ~promise_type() {}

    myTask<T> get_return_object() {
      return myTask<T> {
        .h_ = std::coroutine_handle<promise_type>::from_promise(*this)
      };
    }

    std::suspend_never initial_suspend() { return {}; }
    std::suspend_always final_suspend() 
    { 
      boolIsDone = true; 
      return {};
    }

    void unhandled_exception() { std::terminate(); }

    std::suspend_always return_value(auto value) {
      value_ = value;
      return {};
    }

    bool boolIsDone = false;
    auto isDone() { return boolIsDone; }
  };

  bool await_ready() const noexcept { return false; }
  void await_suspend(std::coroutine_handle<promise_type> coro) const noexcept { coro.promise().boolIsDone = true; }
  void await_resume() const noexcept {}

  std::coroutine_handle<promise_type> h_;
  operator std::coroutine_handle<promise_type>() const { return h_; }
};

myTask<int> suspendableLocateValue(int key, std::vector<int>& array, bool interleave)
{
    int currentInt = 0;
    if (interleave = true)
    {
        //do something here
        co_await std::suspend_always{};
    }
    currentInt = array.at(key);
    co_return currentInt;
}

myTask<std::optional<int>> getValue(std::vector<int>& array, int key, bool interleave)
{
  //int result = array.at(key);
  int leaf;

  if (interleave = true)
  {
    leaf = co_await suspendableLocateValue(key, array, true);
  }

  co_return std::make_optional(leaf);
}

void minimalInterleavedExecution(std::vector<int>& lookup, 
                            std::vector<int>& keys, 
                            std::vector<std::optional<int>>& results,
                            int groupsize)
{
  std::vector<std::coroutine_handle<myTask<std::optional<int>>::promise_type>> handles;

  for (int i = 0; i < groupsize; ++i)
  {
    handles.push_back(getValue(lookup, keys.at(i), true));
  }
  int notDone = groupsize;
  int i = groupsize;
  while (notDone > 0)
  {
    for (int handleIndex = 0; handleIndex < handles.size(); ++handleIndex)
    {
      if (!handles.at(handleIndex).promise().isDone())
      {
        handles.at(handleIndex).resume(); 
      }
      else 
      {
        results.push_back(handles.at(handleIndex).promise().value_);
        if (i < keys.size())
        {
          handles.at(handleIndex) = getValue(lookup, keys.at(i), true);
          ++i;
        }
        else 
        { 
          --notDone; 
          handles.erase(handles.begin() + handleIndex);
          --handleIndex;
        }
      }
    }
  }
}

int main()
{
  std::vector<int> lookup = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
  std::vector<int> keys = {4, 2, 0, 6, 9, 0};
  std::vector<std::optional<int>> results;

  minimalInterleavedExecution(lookup, keys, results, 4);
}

我的原始代码实际上涉及一个 B+Tree 并且suspendableLocateValue应该是一个函数,它查找并返回myTask<Node>具有给定键的类型的叶子,并且getValue应该将节点返回函数的返回值设置为新节点并获取特定的节点中的值以将其作为可选值返回。

标签: c++c++20coroutine

解决方案


在返回值为的函数中myTask<std::optional<int>>有一个调用

leaf = co_await suspendableLocateValue(key, array, true);

suspendableLocateValue返回myTask<int>。但是await_suspend没有模板化,因此不能使用:

void await_suspend(std::coroutine_handle<promise_type> coro) const noexcept

所以myTask<U>只能在myTask<U>协程中等待。

co_await something在协程中遇到时,编译器会执行几个步骤来确定该表达式的含义:

  1. 是否有await_transform封闭协程?
  2. operator co_await吗?

否则(您的情况)它会生成具有something.await_suspend(h),其中hastd::coroutine_handle<P>和 P 是封闭协程承诺的类型的代码。

在您的示例中:

  • somethingmyTask<int>
  • hstd::coroutine_handle<myTask<std::optional<int>>::promise_type>

所以有一个电话,myTask<int>::await_suspend(std::coroutine_handle<myTask<std::optional<int>>::promise_type>)但没有这样的功能。编译器尝试转换std::coroutine_handle<myTask<std::optional<int>>::promise_type>await_suspend( std::coroutine_handle<myTask<int>::promise_type>) 的实际参数类型并写入有关它的错误。


推荐阅读