diff --git a/ostd/coroutine.hh b/ostd/coroutine.hh index 590eb51..c758530 100644 --- a/ostd/coroutine.hh +++ b/ostd/coroutine.hh @@ -27,93 +27,7 @@ template struct coroutine; namespace detail { - /* like reference_wrapper but for any value... doesn't have - * to be default constructible, so we can't store it directly - */ - template - struct arg_wrapper { - arg_wrapper() = default; - arg_wrapper(T arg): p_init(true) { - new (&p_arg) T(std::move(arg)); - } - ~arg_wrapper() { - if (p_init) { - reinterpret_cast(&p_arg)->~T(); - } - } - - void operator=(T arg) { - if (p_init) { - reinterpret_cast(&p_arg)->~T(); - } else { - p_init = true; - } - new (&p_arg) T(std::move(arg)); - } - operator T &&() { - return std::move(*reinterpret_cast(&p_arg)); - } - - void swap(arg_wrapper &other) { - using std::swap; - swap( - *reinterpret_cast(&p_arg), - *reinterpret_cast(&other.p_arg) - ); - } - - private: - std::aligned_storage_t p_arg; - bool p_init = false; - }; - - template - struct arg_wrapper { - arg_wrapper() = default; - arg_wrapper(T &&arg): p_arg(&arg) {} - - void operator=(T &&arg) { - p_arg = &arg; - } - operator T &&() { - return *p_arg; - } - - void swap(arg_wrapper &other) { - using std::swap; - swap(p_arg, other.p_arg); - } - - private: - T *p_arg = nullptr; - }; - - template - struct arg_wrapper { - arg_wrapper() = default; - arg_wrapper(T &arg): p_arg(&arg) {} - - void operator=(T &arg) { - p_arg = &arg; - } - operator T &() { - return *p_arg; - } - - void swap(arg_wrapper &other) { - using std::swap; - swap(p_arg, other.p_arg); - } - - private: - T *p_arg = nullptr; - }; - - template - inline void swap(arg_wrapper &a, arg_wrapper &b) { - a.swap(b); - } - + /* dealing with args */ template struct coro_types { using yield_type = std::tuple; @@ -162,6 +76,63 @@ namespace detail { template using coro_args = typename coro_types::yield_type; + /* storing and handling results */ + template + struct coro_rtype { + using type = std::aligned_storage_t; + + static void store(type &stor, R &&v) { + new (&stor) R(std::move(v)); + } + + static void store(type &stor, R const &v) { + new (&stor) R(v); + } + + static R get(type &stor) { + R &tstor = *reinterpret_cast(&stor); + R ret{std::forward(tstor)}; + /* this way we can make sure result is always uninitialized + * except when resuming, so no need to keep a bool flag etc. + */ + tstor.~R(); + return ret; + } + }; + + template + struct coro_rtype { + using type = R *; + + static void store(type &stor, R &v) { + stor = &v; + } + + static R &get(type stor) { + return *stor; + } + }; + + template + struct coro_rtype { + using type = std::aligned_storage_t; + + static void store(type &stor, R &&v) { + new (&stor) R(std::move(v)); + } + + static R &&get(type &stor) { + R &tstor = *reinterpret_cast(&stor); + R ret{std::forward(tstor)}; + tstor.~R(); + return std::move(ret); + } + }; + + template + using coro_result = typename coro_rtype::type; + + /* forward declare generic yielder for friends */ template struct coro_yielder; @@ -181,25 +152,27 @@ namespace detail { } R get_result() { - return std::forward(p_result); + return std::forward(coro_rtype::get(p_result)); } template void call_helper(F &func, std::index_sequence) { - p_result = std::forward(func( + coro_rtype::store(p_result, std::forward(func( Y{*this}, std::forward(*std::get(p_args))... - )); + ))); } void swap(coro_base &other) { using std::swap; swap(p_args, other.p_args); - swap(p_result, other.p_result); + /* no need to swap result as result is always only alive + * for the time of a single resume, in which no swap happens + */ coroutine_context::swap(other); } std::tuple...> p_args; - arg_wrapper p_result; + coro_result p_result; }; /* yield takes a value but doesn't return any args */ @@ -214,21 +187,19 @@ namespace detail { void set_args() {} R get_result() { - return std::forward(p_result); + return std::forward(coro_rtype::get(p_result)); } template void call_helper(F &func, std::index_sequence) { - p_result = std::forward(func(Y{*this})); + coro_rtype::store(p_result, std::forward(func(Y{*this}))); } void swap(coro_base &other) { - using std::swap; - swap(p_result, other.p_result); coroutine_context::swap(other); } - arg_wrapper p_result; + coro_result p_result; }; /* yield doesn't take a value and returns args */ @@ -290,13 +261,13 @@ namespace detail { coro_yielder(coro_base &coro): p_coro(coro) {} coro_args operator()(R &&ret) { - p_coro.p_result = std::forward(ret); + coro_rtype::store(p_coro.p_result, std::move(ret)); p_coro.yield_jump(); return p_coro.get_args(std::make_index_sequence{}); } coro_args operator()(R const &ret) { - p_coro.p_result = ret; + coro_rtype::store(p_coro.p_result, ret); p_coro.yield_jump(); return p_coro.get_args(std::make_index_sequence{}); } @@ -310,7 +281,7 @@ namespace detail { coro_yielder(coro_base &coro): p_coro(coro) {} coro_args operator()(R &ret) { - p_coro.p_result = ret; + coro_rtype::store(p_coro.p_result, ret); p_coro.yield_jump(); return p_coro.get_args(std::make_index_sequence{}); } @@ -324,7 +295,7 @@ namespace detail { coro_yielder(coro_base &coro): p_coro(coro) {} coro_args operator()(R &&ret) { - p_coro.p_result = std::forward(ret); + coro_rtype::store(p_coro.p_result, std::move(ret)); p_coro.yield_jump(); return p_coro.get_args(std::make_index_sequence{}); }