Opened 8 years ago

Closed 7 years ago

#10664 closed Bugs (wontfix)

Missing support with respect to moves

Reported by: anonymous Owned by: olli
Milestone: To Be Determined Component: coroutine
Version: Boost 1.56.0 Severity: Problem
Keywords: Cc:

Description

Some parts of the documentation suggests that coroutines can be used with move-only types (e.g. boost::coroutines::asymmetric_coroutine<std::unique_ptr<int>>):

If R is a move-only type, you may only call get() once before the next asymmetric_coroutine<>::pull_type::operator() call.

However, if the template parameter is a move-only type, symmetric_coroutine<>::yield_type::get() may only be called once before the next symmetric_coroutine<>::yield_type::operator() call.

However those get members are implemented as some form of return * result_;, where the result type is that move-only type. This rightfully won't work.

Note that using the input iterator (in the asymmetric case) works, because its operator* uses the get_pointer() member (of the non-public implementation) rather than get().

My first thought on how to solve this is to elaborate the public interface, as I believe only one get() member won't cut it. Even if e.g. get() performs some equivalent of return move(*result_); for move-only types, there is a missed opportunity for copyable types that have an optimized move. Consider a coroutine of std::vector<int>, for which we only call get() at most once per value yielded: we would like each value to be passed around with moves, to avoid unnecessary allocations.

IOW, there is a clear separation of concerns between a getter that can be called at most once, for which returning by value is a natural fit; and a getter that can be called any number of times, for which returning by value is not unheard of, but in which case it shouldn't compile for move-only types--although the presence and uses of get_pointer() suggests a need for returning by reference. This makes it possible at all to write correct generic code, too:

auto f(Functor functor, Coro& coro)
{
    // for a double-duty get(), we can't tell if
    // we're performing two moves (which is bad)
    // or two copies (which could be inefficient)
    return functor(coro.get(), coro.get());

    // okay: this won't compile for move-only types, and
    // this performs the required work when we really need
    // additional copies
    return functor(coro.get_copy(), coro.get_copy());
    // a savvy programmer that wants as few copies as possible,
    // when moves are assumed to be acceptable, has the tools
    // to achieve that goal:
    auto copy = coro.get_copy();
    return functor(move(copy), move(coro.get_reference()));
    // minimal work: one copy, one move
    // possible thanks to the sequencing guarantees of { }
    return Foo { coro.get_copy(), move(coro.get_reference) };

    // okay, doesn't even require moveability
    return functor(coro.get_reference(), coro_get_reference());

    // not okay, can't be caught by the compiler but hopefully
    // obvious to the programmer
    return functor(coro.get_move(), coro.get_move());
}

(The names are painfully spelled out for the purpose of illustration and not a serious suggestion.)

Change History (1)

comment:1 by olli, 7 years ago

Resolution: wontfix
Status: newclosed

moveable types are supported by boost.coroutine2

Note: See TracTickets for help on using tickets.