diff --git a/include/boost/coroutine2/detail/pull_control_block.hpp b/include/boost/coroutine2/detail/pull_control_block.hpp index 45b655b..b5e1aa3 100644 --- a/include/boost/coroutine2/detail/pull_control_block.hpp +++ b/include/boost/coroutine2/detail/pull_control_block.hpp @@ -8,6 +8,7 @@ #define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP #include +#include #include #include @@ -30,7 +31,7 @@ struct pull_coroutine< T >::control_block { state_t state; std::exception_ptr except; bool bvalid; - typename std::aligned_storage< sizeof( T), alignof( T) >::type storage[1]; + typename std::aligned_storage< sizeof( T), alignof( T) >::type storage; template< typename StackAllocator, typename Fn > control_block( context::preallocated, StackAllocator, Fn &&); diff --git a/include/boost/coroutine2/detail/pull_control_block.ipp b/include/boost/coroutine2/detail/pull_control_block.ipp index a958ccb..174600b 100644 --- a/include/boost/coroutine2/detail/pull_control_block.ipp +++ b/include/boost/coroutine2/detail/pull_control_block.ipp @@ -8,6 +8,7 @@ #define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP #include +#include #include #include @@ -32,10 +33,37 @@ namespace detail { template< typename T > template< typename StackAllocator, typename Fn > pull_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn_) : + Fn && fn) : other{ nullptr }, +#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) ctx{ std::allocator_arg, palloc, salloc, - [this,fn=std::forward< Fn >( fn_),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { + std::bind( + [this]( typename std::decay< Fn >::type & fn, boost::context::execution_context ctx, void *) mutable noexcept { + // create synthesized push_coroutine< T > + typename push_coroutine< T >::control_block synthesized_cb{ this, ctx }; + push_coroutine< T > synthesized{ & synthesized_cb }; + other = & synthesized_cb; + try { + // call coroutine-fn with synthesized push_coroutine as argument + fn( synthesized); + } catch ( forced_unwind const&) { + // do nothing for unwinding exception + } catch (...) { + // store other exceptions in exception-pointer + except = std::current_exception(); + } + // set termination flags + state |= state_t::complete; + // jump back to ctx + other->ctx(); + BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); + }, + std::forward< Fn >( fn), + boost::context::execution_context::current(), + std::placeholders::_1)}, +#else + ctx{ std::allocator_arg, palloc, salloc, + [this,fn=std::forward< Fn >( fn),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { // create synthesized push_coroutine< T > typename push_coroutine< T >::control_block synthesized_cb{ this, ctx }; push_coroutine< T > synthesized{ & synthesized_cb }; @@ -55,6 +83,7 @@ pull_coroutine< T >::control_block::control_block( context::preallocated palloc, other->ctx(); BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); }}, +#endif state{ state_t::unwind }, except{}, bvalid{ false }, @@ -84,7 +113,7 @@ pull_coroutine< T >::control_block::~control_block() noexcept { } // destroy data if it set if ( bvalid) { - reinterpret_cast< T const* >( storage)->~T(); + reinterpret_cast< T * >( std::addressof( storage) )->~T(); } } @@ -107,10 +136,10 @@ void pull_coroutine< T >::control_block::set( T * t) { // destroy data if it set if ( bvalid) { - reinterpret_cast< T const* >( storage)->~T(); + reinterpret_cast< T * >( std::addressof( storage) )->~T(); } if ( nullptr != t) { - new ( storage) T( std::move( * t) ); // FIXME: differrentiation between move/copy + ::new ( static_cast< void * >( std::addressof( storage) ) ) T( std::move( * t) ); bvalid = true; } else { bvalid = false; @@ -120,7 +149,7 @@ pull_coroutine< T >::control_block::set( T * t) { template< typename T > T & pull_coroutine< T >::control_block::get() noexcept { - return * reinterpret_cast< T * >( storage); + return * reinterpret_cast< T * >( std::addressof( storage) ); } template< typename T > @@ -135,10 +164,37 @@ pull_coroutine< T >::control_block::valid() const noexcept { template< typename T > template< typename StackAllocator, typename Fn > pull_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn_) : + Fn && fn) : other{ nullptr }, +#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) ctx{ std::allocator_arg, palloc, salloc, - [this,fn=std::forward< Fn >( fn_),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { + std::bind( + [this]( typename std::decay< Fn >::type & fn, boost::context::execution_context ctx, void *) mutable noexcept { + // create synthesized push_coroutine< T > + typename push_coroutine< T & >::control_block synthesized_cb{ this, ctx }; + push_coroutine< T & > synthesized{ & synthesized_cb }; + other = & synthesized_cb; + try { + // call coroutine-fn with synthesized push_coroutine as argument + fn( synthesized); + } catch ( forced_unwind const&) { + // do nothing for unwinding exception + } catch (...) { + // store other exceptions in exception-pointer + except = std::current_exception(); + } + // set termination flags + state |= state_t::complete; + // jump back to ctx + other->ctx(); + BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); + }, + std::forward< Fn >( fn), + boost::context::execution_context::current(), + std::placeholders::_1)}, +#else + ctx{ std::allocator_arg, palloc, salloc, + [this,fn=std::forward< Fn >( fn),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { // create synthesized push_coroutine< T > typename push_coroutine< T & >::control_block synthesized_cb{ this, ctx }; push_coroutine< T & > synthesized{ & synthesized_cb }; @@ -158,6 +214,7 @@ pull_coroutine< T & >::control_block::control_block( context::preallocated pallo other->ctx(); BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); }}, +#endif state{ state_t::unwind }, except{}, t{ nullptr } { @@ -216,10 +273,37 @@ pull_coroutine< T & >::control_block::valid() const noexcept { template< typename StackAllocator, typename Fn > pull_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn_) : + Fn && fn) : other{ nullptr }, +#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) ctx{ std::allocator_arg, palloc, salloc, - [this,fn=std::forward< Fn >( fn_),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { + std::bind( + [this]( typename std::decay< Fn >::type & fn, boost::context::execution_context ctx, void *) mutable noexcept { + // create synthesized push_coroutine< T > + typename push_coroutine< void >::control_block synthesized_cb{ this, ctx }; + push_coroutine< void > synthesized{ & synthesized_cb }; + other = & synthesized_cb; + try { + // call coroutine-fn with synthesized push_coroutine as argument + fn( synthesized); + } catch ( forced_unwind const&) { + // do nothing for unwinding exception + } catch (...) { + // store other exceptions in exception-pointer + except = std::current_exception(); + } + // set termination flags + state |= state_t::complete; + // jump back to ctx + other->ctx(); + BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); + }, + std::forward< Fn >( fn), + boost::context::execution_context::current(), + std::placeholders::_1)}, +#else + ctx{ std::allocator_arg, palloc, salloc, + [this,fn=std::forward< Fn >( fn),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { // create synthesized push_coroutine< T > typename push_coroutine< void >::control_block synthesized_cb{ this, ctx }; push_coroutine< void > synthesized{ & synthesized_cb }; @@ -239,6 +323,7 @@ pull_coroutine< void >::control_block::control_block( context::preallocated pall other->ctx(); BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); }}, +#endif state{ state_t::unwind }, except{} { // enter coroutine-fn in order to have first value available after ctor returns diff --git a/include/boost/coroutine2/detail/push_control_block.hpp b/include/boost/coroutine2/detail/push_control_block.hpp index c0c3cc5..e5127bf 100644 --- a/include/boost/coroutine2/detail/push_control_block.hpp +++ b/include/boost/coroutine2/detail/push_control_block.hpp @@ -7,6 +7,7 @@ #ifndef BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP #define BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP +#include #include #include diff --git a/include/boost/coroutine2/detail/push_control_block.ipp b/include/boost/coroutine2/detail/push_control_block.ipp index 473a380..d6c1c1a 100644 --- a/include/boost/coroutine2/detail/push_control_block.ipp +++ b/include/boost/coroutine2/detail/push_control_block.ipp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -33,10 +34,41 @@ namespace detail { template< typename T > template< typename StackAllocator, typename Fn > push_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn_) : + Fn && fn) : other{ nullptr }, +#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) ctx{ std::allocator_arg, palloc, salloc, - [this,fn=std::forward< Fn >( fn_),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { + std::bind( + [this]( typename std::decay< Fn >::type & fn, boost::context::execution_context ctx, void * vp) mutable noexcept { + // create synthesized pull_coroutine< T > + typename pull_coroutine< T >::control_block synthesized_cb{ this, ctx }; + pull_coroutine< T > synthesized{ & synthesized_cb }; + other = & synthesized_cb; + // jump back to ctor + T * t = static_cast< T * >( ctx() ); + // set transferred value + synthesized_cb.set( t); + try { + // call coroutine-fn with synthesized pull_coroutine as argument + fn( synthesized); + } catch ( forced_unwind const&) { + // do nothing for unwinding exception + } catch (...) { + // store other exceptions in exception-pointer + except = std::current_exception(); + } + // set termination flags + state |= state_t::complete; + // jump back to ctx + other->ctx(); + BOOST_ASSERT_MSG( false, "push_coroutine is complete"); + }, + std::forward< Fn >( fn), + boost::context::execution_context::current(), + std::placeholders::_1)}, +#else + ctx{ std::allocator_arg, palloc, salloc, + [this,fn=std::forward< Fn >( fn),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { // create synthesized pull_coroutine< T > typename pull_coroutine< T >::control_block synthesized_cb{ this, ctx }; pull_coroutine< T > synthesized{ & synthesized_cb }; @@ -60,6 +92,7 @@ push_coroutine< T >::control_block::control_block( context::preallocated palloc, other->ctx(); BOOST_ASSERT_MSG( false, "push_coroutine is complete"); }}, +#endif state{ state_t::unwind }, except{} { // enter coroutine-fn in order to get other set @@ -127,10 +160,41 @@ push_coroutine< T >::control_block::valid() const noexcept { template< typename T > template< typename StackAllocator, typename Fn > push_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn_) : + Fn && fn) : other{ nullptr }, +#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) ctx{ std::allocator_arg, palloc, salloc, - [this,fn=std::forward< Fn >( fn_),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { + std::bind( + [this]( typename std::decay< Fn >::type & fn, boost::context::execution_context ctx, void * vp) mutable noexcept { + // create synthesized pull_coroutine< T > + typename pull_coroutine< T & >::control_block synthesized_cb{ this, ctx }; + pull_coroutine< T & > synthesized{ & synthesized_cb }; + other = & synthesized_cb; + // jump back to ctor + T * t = static_cast< T * >( ctx() ); + // set transferred value + synthesized_cb.t = t; + try { + // call coroutine-fn with synthesized pull_coroutine as argument + fn( synthesized); + } catch ( forced_unwind const&) { + // do nothing for unwinding exception + } catch (...) { + // store other exceptions in exception-pointer + except = std::current_exception(); + } + // set termination flags + state |= state_t::complete; + // jump back to ctx + other->ctx(); + BOOST_ASSERT_MSG( false, "push_coroutine is complete"); + }, + std::forward< Fn >( fn), + boost::context::execution_context::current(), + std::placeholders::_1)}, +#else + ctx{ std::allocator_arg, palloc, salloc, + [this,fn=std::forward< Fn >( fn),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { // create synthesized pull_coroutine< T > typename pull_coroutine< T & >::control_block synthesized_cb{ this, ctx }; pull_coroutine< T & > synthesized{ & synthesized_cb }; @@ -154,6 +218,7 @@ push_coroutine< T & >::control_block::control_block( context::preallocated pallo other->ctx(); BOOST_ASSERT_MSG( false, "push_coroutine is complete"); }}, +#endif state{ state_t::unwind }, except{} { // enter coroutine-fn in order to get other set @@ -206,6 +271,36 @@ push_coroutine< T & >::control_block::valid() const noexcept { template< typename StackAllocator, typename Fn > push_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, Fn && fn_) : other{ nullptr }, +#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) + ctx{ std::allocator_arg, palloc, salloc, + std::bind( + [this]( typename std::decay< Fn >::type & fn, boost::context::execution_context ctx, + void * vp) mutable noexcept { + // create synthesized pull_coroutine< T > + typename pull_coroutine< void >::control_block synthesized_cb{ this, ctx }; + pull_coroutine< void > synthesized{ & synthesized_cb }; + other = & synthesized_cb; + // jump back to ctor + ctx(); + try { + // call coroutine-fn with synthesized pull_coroutine as argument + fn( synthesized); + } catch ( forced_unwind const&) { + // do nothing for unwinding exception + } catch (...) { + // store other exceptions in exception-pointer + except = std::current_exception(); + } + // set termination flags + state |= state_t::complete; + // jump back to ctx + other->ctx(); + BOOST_ASSERT_MSG( false, "push_coroutine is complete"); + }, + std::forward< Fn >( fn_), + boost::context::execution_context::current(), + std::placeholders::_1)}, +#else ctx{ std::allocator_arg, palloc, salloc, [this,fn=std::forward< Fn >( fn_),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { // create synthesized pull_coroutine< T > @@ -229,6 +324,7 @@ push_coroutine< void >::control_block::control_block( context::preallocated pall other->ctx(); BOOST_ASSERT_MSG( false, "push_coroutine is complete"); }}, +#endif state{ state_t::unwind }, except{} { // enter coroutine-fn in order to get other set diff --git a/test/test_coroutine.cpp b/test/test_coroutine.cpp index bc25135..e47972d 100644 --- a/test/test_coroutine.cpp +++ b/test/test_coroutine.cpp @@ -193,10 +193,6 @@ void f12( coro::coroutine< void >::pull_type & c) c(); } -template< typename E > -void f14( coro::coroutine< void >::pull_type &, E const& e) -{ throw e; } - void f16( coro::coroutine< int >::push_type & c) { c( 1); @@ -216,13 +212,6 @@ void f17( coro::coroutine< int >::pull_type & c, std::vector< int > & vec) } } -void f19( coro::coroutine< int& >::push_type & c, int & i1, int & i2, int & i3) -{ - c( i1); - c( i2); - c( i3); -} - void f20( coro::coroutine< int >::push_type &) {} @@ -426,6 +415,12 @@ void test_const_ref() BOOST_CHECK_EQUAL( & a, value5); } +void test_no_result() +{ + coro::coroutine< int >::pull_type coro( f20); + BOOST_CHECK( ! coro); +} + void test_tuple() { value8 = 0; @@ -466,7 +461,9 @@ void test_exceptions() try { coro::coroutine< void >::push_type coro( - std::bind( f14< std::runtime_error >, std::placeholders::_1, ex) ); + [&msg]( coro::coroutine< void >::pull_type &) { + throw std::runtime_error( msg); + }); BOOST_CHECK( coro); coro(); BOOST_CHECK( ! coro); @@ -512,7 +509,12 @@ void test_input_iterator() { int i1 = 1, i2 = 2, i3 = 3; coro::coroutine< int& >::pull_type coro( - std::bind( f19, std::placeholders::_1, std::ref( i1), std::ref( i2), std::ref( i3) ) ); + [&i1,&i2,&i3](coro::coroutine< int& >::push_type & c){ + c( i1); + c( i2); + c( i3); + }); + int counter = 1; for ( int & i : coro) { switch ( counter) { @@ -564,12 +566,6 @@ void test_output_iterator() BOOST_CHECK_EQUAL( ( int)4, vec[3] ); } -void test_no_result() -{ - coro::coroutine< int >::pull_type coro( f20); - BOOST_CHECK( ! coro); -} - std::vector< int > vec; coro::coroutine< void >::pull_type * child = nullptr;