C ++ находит co_await ожидаемый тип результата - PullRequest
6 голосов
/ 17 апреля 2020

Я хотел бы знать, возможно ли найти тип результата co_await для ожидаемого: поскольку невозможно использовать co_await в неоцененном контексте, я не могу сделать

template<class Awaitable>
task<> f(Awaitable&& awaitable)
{
  using result_type = decltype(co_await awaitable);
}

Есть ли В любом случае это может быть сделано?

Основная цель здесь состоит в том, чтобы определить, является ли тип result_type пустым, вот, в основном, то, что у нас есть: мы хотим запустить fire_and_forget задачу и вызвать метод по завершении, но из-за типа возврата void это не так просто

template<class Awaitable, class Success, class Failure >
detail::fire_and_forget_task call_async_then(Awaitable awaitable, Success success, Failure failure)
{
  try
  {
    using result_type = ??
    if constexpr (std::is_same_v<void, result_t>)
    {
      co_await awaitable;
      success();
    }
    else
      success(co_await f);
  }
  catch(...)
  {
    failure(std::current_exception());
  }
}

Возможно, есть более простой способ сделать это, но сейчас я не думаю ни о каком.

Спасибо

Ответы [ 2 ]

2 голосов
/ 17 апреля 2020

В общем случае сделать это невозможно, поскольку co_await механизм сопрограмм частично зависит от типа обещания для функции, которая вызывает co_await. Когда вызывается co_await <expr>, ему придется преобразовать <expr> в ожидаемый тип. Часть этого процесса включает запрос типа обещания для сопрограммы, чтобы взвешивать это преобразование, если оно того пожелает. Поскольку тип обещания определяется сигнатурой сопрограммы, поэтому тип результата любого co_await <expr> зависит от сигнатуры вызывающей его функции.

Именно поэтому co_await нельзя использовать в недооцененный контекст; его поведение зависит от контекста - .

Теперь, если вы знаете , что ваш тип обещания не имеет await_transform (что вы, вероятно, знаете, так как он ваш тип обещания), тогда тип результата можно вычислить. Ожидаемым типом для co_await <expr> будет просто тип <expr>. Затем он преобразуется в объект ожидания через вызов operator co_await в выражении. Это немного сложно вычислить, так как он может быть вызван с помощью функции-члена или вызова оператора, не являющегося членом, так что это сложная часть метапрограммирования.

Как только у вас есть тип объекта awaiter, вы можете получить тип возвращаемого значения await_resume(); это тип выражения co_await <expr>.

0 голосов
/ 17 апреля 2020

Хорошо, большое спасибо, теперь я понимаю причину, по которой decltype(co_await awaitable) не допускается. Так что, если я вас правильно понял, и если я возьму псевдокод из https://lewissbaker.github.io/2017/11/17/understanding-operator-co-await, чтобы получить ожидающего от ожидаемого типа (скажем, я пропустил не являющийся членом оператор для упрощения), я могу получить результат вот так (может быть, есть что-то попроще, я не привык писать идентификаторы обнаружения):

#include <type_traits>

template< class, class = std::void_t<> >
struct has_co_await_operator : std::false_type
{};

template< class T >
struct has_co_await_operator< T, std::void_t< decltype(std::declval<T>().operator co_await()) > >
    : std::true_type
{};

template< class T >
constexpr bool has_co_await_operator_v = has_co_await_operator<T>::value;

template< class T >
struct get_awaiter : std::conditional< has_co_await_operator_v<T>, decltype(std::declval<T>().operator co_await()), T>
{};

template<class T>
using get_awaiter_t = typename get_awaiter<T>::type;

template<class Awaitable>
struct awaitable_result
{
    using type = decltype(std::declval<get_awaiter_t<Awaitable>>().await_resume());
};

template<class T>
using awaitable_result_t = typename awaitable_result<T>::type;

https://godbolt.org/z/dAVfVX

Вам это кажется нормальным?

...