diff --git a/example/v2/Jamfile.v2 b/example/v2/Jamfile.v2 index 0dbae14..72c409a 100644 --- a/example/v2/Jamfile.v2 +++ b/example/v2/Jamfile.v2 @@ -26,22 +26,30 @@ project boost/context/example/execution_context multi ; -exe jump - : jump.cpp +exe jump_void + : jump_void.cpp ; -exe parser - : parser.cpp +exe jump + : jump.cpp ; exe fibonacci : fibonacci.cpp ; +exe parser + : parser.cpp + ; + exe parameter : parameter.cpp ; +exe ontop_void + : ontop_void.cpp + ; + exe ontop : ontop.cpp ; diff --git a/example/v2/fibonacci.cpp b/example/v2/fibonacci.cpp index 3c8fbee..a0dbb3f 100644 --- a/example/v2/fibonacci.cpp +++ b/example/v2/fibonacci.cpp @@ -14,12 +14,12 @@ namespace ctx = boost::context; int main() { int n=35; - ctx::execution_context source( - [n](ctx::execution_context sink,void*)mutable{ + ctx::execution_context< int > source( + [n](ctx::execution_context< int > sink, int) mutable { int a=0; int b=1; while(n-->0){ - auto result=sink(&a); + auto result=sink(a); sink=std::move(std::get<0>(result)); auto next=a+b; a=b; @@ -28,9 +28,9 @@ int main() { return sink; }); for(int i=0;i<10;++i){ - auto result=source(); + auto result=source(i); source=std::move(std::get<0>(result)); - std::cout<<*(int*)std::get<1>(result)<<" "; + std::cout<(result)<<" "; } std::cout<( data) ); - ctx2( & ctxm); - return ctxm; -} - -ctx::execution_context f2( ctx::execution_context ctx1, void * data) { - std::cout << "f2: entered first time" << std::endl; - ctx::execution_context ctxm = std::move( * static_cast< ctx::execution_context * >( data) ); +ctx::execution_context< int > f1( ctx::execution_context< int > ctxm, int data) { + std::cout << "f1: entered first time: " << data << std::endl; + std::tie( ctxm, data) = ctxm( data + 2); + std::cout << "f1: entered second time: " << data << std::endl; return ctxm; } int main() { - ctx::execution_context ctx1( f1); - void * ignored; - std::tie( ctx1, ignored) = ctx1(); - ctx::execution_context ctx2( f2); - std::tie( ctx1, ignored) = ctx1( & ctx2); + int data = 1; + ctx::execution_context< int > ctx1( f1); + std::tie( ctx1, data) = ctx1( data + 2); + std::cout << "f1: returned first time: " << data << std::endl; + std::tie( ctx1, data) = ctx1( data + 2); + std::cout << "f1: returned second time: " << data << std::endl; std::cout << "main: done" << std::endl; diff --git a/example/v2/jump_void.cpp b/example/v2/jump_void.cpp new file mode 100644 index 0000000..00594c8 --- /dev/null +++ b/example/v2/jump_void.cpp @@ -0,0 +1,31 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include + +namespace ctx = boost::context; + +ctx::execution_context< void > f1( ctx::execution_context< void > ctxm) { + std::cout << "f1: entered first time" << std::endl; + ctxm = ctxm(); + std::cout << "f1: entered second time" << std::endl; + return ctxm; +} + +int main() { + ctx::execution_context< void > ctx1( f1); + ctx1 = ctx1(); + std::cout << "f1: returned first time" << std::endl; + ctx1 = ctx1(); + std::cout << "f1: returned second time" << std::endl; + + std::cout << "main: done" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/example/v2/ontop.cpp b/example/v2/ontop.cpp index 34b6e9b..abd7b56 100644 --- a/example/v2/ontop.cpp +++ b/example/v2/ontop.cpp @@ -12,29 +12,29 @@ namespace ctx = boost::context; -ctx::execution_context f1( ctx::execution_context ctx, void * ignored) { - std::cout << "f1: entered first time" << std::endl; - std::tie( ctx, ignored) = ctx(); - std::cout << "f1: entered second time" << std::endl; - std::tie( ctx, ignored) = ctx(); - std::cout << "f1: entered third time" << std::endl; +ctx::execution_context< int > f1( ctx::execution_context< int > ctx, int data) { + std::cout << "f1: entered first time: " << data << std::endl; + std::tie( ctx, data) = ctx( data); + std::cout << "f1: entered second time: " << data << std::endl; + std::tie( ctx, data) = ctx( data); + std::cout << "f1: entered third time: " << data << std::endl; return ctx; } -std::tuple< ctx::execution_context, void * > f2( ctx::execution_context ctx, void * data) { - std::cout << "f2: entered" << std::endl; - return std::make_tuple( std::move( ctx), data); +ctx::execution_context< int > f2( ctx::execution_context< int > ctx, int data) { + std::cout << "f2: entered: " << data << std::endl; + return ctx; } int main() { - void * ignored; - ctx::execution_context ctx( f1); - std::tie( ctx, ignored) = ctx(); - std::cout << "f1: returned first time" << std::endl; - std::tie( ctx, ignored) = ctx(); - std::cout << "f1: returned second time" << std::endl; - std::tie( ctx, ignored) = ctx( ctx::exec_ontop_arg, f2); - std::cout << "f1: returned third time" << std::endl; + int data = 3; + ctx::execution_context< int > ctx( f1); + std::tie( ctx, data) = ctx( data); + std::cout << "f1: returned first time: " << data << std::endl; + std::tie( ctx, data) = ctx( data); + std::cout << "f1: returned second time: " << data << std::endl; + std::tie( ctx, data) = ctx( ctx::exec_ontop_arg, f2, data + 2); + std::cout << "f1: returned third time: " << data << std::endl; std::cout << "main: done" << std::endl; diff --git a/example/v2/ontop_void.cpp b/example/v2/ontop_void.cpp new file mode 100644 index 0000000..6584bb2 --- /dev/null +++ b/example/v2/ontop_void.cpp @@ -0,0 +1,41 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include + +namespace ctx = boost::context; + +ctx::execution_context< void > f1( ctx::execution_context< void > ctx) { + std::cout << "f1: entered first time" << std::endl; + ctx = ctx(); + std::cout << "f1: entered second time" << std::endl; + ctx = ctx(); + std::cout << "f1: entered third time" << std::endl; + return ctx; +} + +ctx::execution_context< void > f2( ctx::execution_context< void > ctx) { + std::cout << "f2: entered" << std::endl; + return ctx; +} + +int main() { + ctx::execution_context< void > ctx( f1); + ctx = ctx(); + std::cout << "f1: returned first time" << std::endl; + ctx = ctx(); + std::cout << "f1: returned second time" << std::endl; + ctx = ctx( ctx::exec_ontop_arg, f2); + std::cout << "f1: returned third time" << std::endl; + + std::cout << "main: done" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/example/v2/parameter.cpp b/example/v2/parameter.cpp index 381f5fc..6165f18 100644 --- a/example/v2/parameter.cpp +++ b/example/v2/parameter.cpp @@ -10,28 +10,31 @@ #include #include +#include #include #include +typedef boost::variant variant_t; + namespace ctx = boost::context; class X{ private: std::exception_ptr excptr_; - ctx::execution_context ctx_; + ctx::execution_context ctx_; public: X(): excptr_(), ctx_( - [=](ctx::execution_context ctx, void * vp){ + [=](ctx::execution_context ctx, variant_t data){ try { for (;;) { - int i = * static_cast< int * >( vp); - std::string str = boost::lexical_cast(i); - auto result = ctx( & str); + int i = boost::get(data); + data = boost::lexical_cast(i); + auto result = ctx( data); ctx = std::move( std::get<0>( result) ); - vp = std::get<1>( result); + data = std::get<1>( result); } } catch ( ctx::detail::forced_unwind const&) { throw; @@ -43,13 +46,14 @@ public: {} std::string operator()(int i){ - auto result = ctx_( & i); + variant_t data = i; + auto result = ctx_( data); ctx_ = std::move( std::get<0>( result) ); - void * ret = std::get<1>( result); + data = std::get<1>( result); if(excptr_){ std::rethrow_exception(excptr_); } - return * static_cast< std::string * >( ret); + return boost::get(data); } }; diff --git a/example/v2/parser.cpp b/example/v2/parser.cpp index 82ff57d..2c2500e 100644 --- a/example/v2/parser.cpp +++ b/example/v2/parser.cpp @@ -96,13 +96,13 @@ int main() { std::exception_ptr except; // execute parser in new execution context - boost::context::execution_context source( - [&is,&done,&except](ctx::execution_context sink,void*){ + boost::context::execution_context source( + [&is,&done,&except](ctx::execution_context sink,char){ // create parser with callback function Parser p( is, [&sink](char ch){ // resume main execution context - auto result = sink(&ch); + auto result = sink(ch); sink = std::move(std::get<0>(result)); }); try { @@ -120,15 +120,15 @@ int main() { // user-code pulls parsed data from parser // invert control flow - auto result = source(); + auto result = source('\0'); source = std::move(std::get<0>(result)); - void * vp = std::get<1>(result); + char c = std::get<1>(result); if ( except) { std::rethrow_exception(except); } while( ! done) { - printf("Parsed: %c\n",* static_cast(vp)); - std::tie(source,vp) = source(); + printf("Parsed: %c\n",c); + std::tie(source,c) = source('\0'); if (except) { std::rethrow_exception(except); } diff --git a/include/boost/context/execution_context_v2.hpp b/include/boost/context/execution_context_v2.hpp index 8d59a5b..aa74285 100644 --- a/include/boost/context/execution_context_v2.hpp +++ b/include/boost/context/execution_context_v2.hpp @@ -76,31 +76,29 @@ void context_entry( transfer_t t_) noexcept { BOOST_ASSERT_MSG( false, "context already terminated"); } -template< typename Ctx, typename Fn, typename Tpl > +template< typename Ctx, typename Fn, typename ArgsTpl > transfer_t context_ontop( transfer_t t) { - auto tpl = static_cast< std::tuple< void *, Fn, Tpl > * >( t.data); + auto tpl = static_cast< std::tuple< Fn, ArgsTpl > * >( t.data); BOOST_ASSERT( nullptr != tpl); - auto data = std::get< 0 >( * tpl); - typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 1 >( * tpl) ); - auto args = std::forward< Tpl >( std::get< 2 >( * tpl) ); + typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * tpl) ); + auto args = std::get< 1 >( * tpl); Ctx ctx{ t.fctx }; // execute function - std::tie( ctx, data) = apply( + ctx = apply( fn, std::tuple_cat( - args, std::forward_as_tuple( std::move( ctx) ), - std::tie( data) ) ); - return { exchange( ctx.fctx_, nullptr), data }; + args) ); + return { exchange( ctx.fctx_, nullptr), nullptr }; } -template< typename Ctx, typename StackAlloc, typename Fn, typename ... Args > +template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > class record { private: StackAlloc salloc_; stack_context sctx_; typename std::decay< Fn >::type fn_; - std::tuple< typename std::decay< Args >::type ... > args_; + std::tuple< typename std::decay< Params >::type ... > params_; static void destroy( record * p) noexcept { StackAlloc salloc = p->salloc_; @@ -113,11 +111,11 @@ private: public: record( stack_context sctx, StackAlloc const& salloc, - Fn && fn, Args && ... args) noexcept : + Fn && fn, Params && ... params) noexcept : salloc_( salloc), sctx_( sctx), fn_( std::forward< Fn >( fn) ), - args_( std::forward< Args >( args) ... ) { + params_( std::forward< Params >( params) ... ) { } record( record const&) = delete; @@ -129,20 +127,21 @@ public: transfer_t run( transfer_t t) { Ctx from{ t.fctx }; + typename Ctx::args_tpl_t args = std::move( * static_cast< typename Ctx::args_tpl_t * >( t.data) ); // invoke context-function Ctx cc = apply( - fn_, + std::move( fn_), std::tuple_cat( - args_, + params_, std::forward_as_tuple( std::move( from) ), - std::tie( t.data) ) ); + std::move( args) ) ); return { exchange( cc.fctx_, nullptr), nullptr }; } }; -template< typename Ctx, typename StackAlloc, typename Fn, typename ... Args > -fcontext_t context_create( StackAlloc salloc, Fn && fn, Args && ... args) { - typedef record< Ctx, StackAlloc, Fn, Args ... > record_t; +template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > +fcontext_t context_create( StackAlloc salloc, Fn && fn, Params && ... params) { + typedef record< Ctx, StackAlloc, Fn, Params ... > record_t; auto sctx = salloc.allocate(); // reserve space for control structure @@ -166,14 +165,14 @@ fcontext_t context_create( StackAlloc salloc, Fn && fn, Args && ... args) { BOOST_ASSERT( nullptr != fctx); // placment new for control structure on context-stack auto rec = ::new ( sp) record_t{ - sctx, salloc, std::forward< Fn >( fn), std::forward< Args >( args) ... }; + sctx, salloc, std::forward< Fn >( fn), std::forward< Params >( params) ... }; // transfer control structure to context-stack return jump_fcontext( fctx, rec).fctx; } -template< typename Ctx, typename StackAlloc, typename Fn, typename ... Args > -fcontext_t context_create( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args) { - typedef record< Ctx, StackAlloc, Fn, Args ... > record_t; +template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > +fcontext_t context_create( preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params) { + typedef record< Ctx, StackAlloc, Fn, Params ... > record_t; // reserve space for control structure #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) @@ -196,19 +195,23 @@ fcontext_t context_create( preallocated palloc, StackAlloc salloc, Fn && fn, Arg BOOST_ASSERT( nullptr != fctx); // placment new for control structure on context-stack auto rec = ::new ( sp) record_t{ - palloc.sctx, salloc, std::forward< Fn >( fn), std::forward< Args >( args) ... }; + palloc.sctx, salloc, std::forward< Fn >( fn), std::forward< Params >( params) ... }; // transfer control structure to context-stack return jump_fcontext( fctx, rec).fctx; } } -class BOOST_CONTEXT_DECL execution_context { +template< typename ... Args > +class execution_context { private: - template< typename Ctx, typename StackAlloc, typename Fn, typename ... Args > + typedef std::tuple< typename std::decay< Args >::type ... > args_tpl_t; + typedef std::tuple< execution_context, typename std::decay< Args >::type ... > ret_tpl_t; + + template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > friend class detail::record; - template< typename Ctx, typename Fn, typename Tpl > + template< typename Ctx, typename Fn, typename ArgsTpl > friend detail::transfer_t detail::context_ontop( detail::transfer_t); detail::fcontext_t fctx_{ nullptr }; @@ -217,34 +220,23 @@ private: fctx_( fctx) { } - template< typename Fn, typename ... Args > - detail::transfer_t resume_ontop_( void * data, Fn && fn, Args && ... args) { - typedef std::tuple< typename std::decay< Args >::type ... > tpl_t; - tpl_t tpl{ std::forward< Args >( args) ... }; - std::tuple< void *, Fn, tpl_t > p = std::forward_as_tuple( data, fn, tpl); - return detail::ontop_fcontext( - detail::exchange( fctx_, nullptr), - & p, - detail::context_ontop< execution_context, Fn, tpl_t >); - } - public: constexpr execution_context() noexcept = default; #if defined(BOOST_USE_SEGMENTED_STACKS) // segmented-stack requires to preserve the segments of the `current` context // which is not possible (no global pointer to current context) - template< typename Fn, typename ... Args > - execution_context( std::allocator_arg_t, segmented_stack, Fn &&, Args && ...) = delete; + template< typename Fn, typename ... Params > + execution_context( std::allocator_arg_t, segmented_stack, Fn &&, Params && ...) = delete; - template< typename Fn, typename ... Args > - execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Args && ...) = delete; + template< typename Fn, typename ... Params > + execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete; #else template< typename Fn, - typename ... Args, + typename ... Params, typename = detail::disable_overload< execution_context, Fn > > - execution_context( Fn && fn, Args && ... args) : + execution_context( Fn && fn, Params && ... params) : // deferred execution of fn and its arguments // arguments are stored in std::tuple<> // non-type template parameter pack via std::index_sequence_for<> @@ -253,14 +245,14 @@ public: fctx_( detail::context_create< execution_context >( fixedsize_stack(), std::forward< Fn >( fn), - std::forward< Args >( args) ... ) ) { + std::forward< Params >( params) ... ) ) { } template< typename StackAlloc, typename Fn, - typename ... Args + typename ... Params > - execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Args && ... args) : + execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params) : // deferred execution of fn and its arguments // arguments are stored in std::tuple<> // non-type template parameter pack via std::index_sequence_for<> @@ -269,14 +261,14 @@ public: fctx_( detail::context_create< execution_context >( salloc, std::forward< Fn >( fn), - std::forward< Args >( args) ... ) ) { + std::forward< Params >( params) ... ) ) { } template< typename StackAlloc, typename Fn, - typename ... Args + typename ... Params > - execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args) : + execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params) : // deferred execution of fn and its arguments // arguments are stored in std::tuple<> // non-type template parameter pack via std::index_sequence_for<> @@ -285,7 +277,7 @@ public: fctx_( detail::context_create< execution_context >( palloc, salloc, std::forward< Fn >( fn), - std::forward< Args >( args) ... ) ) { + std::forward< Params >( params) ... ) ) { } #endif @@ -311,28 +303,31 @@ public: execution_context( execution_context const& other) noexcept = delete; execution_context & operator=( execution_context const& other) noexcept = delete; - std::tuple< execution_context, void * > operator()( void * data = nullptr) { + ret_tpl_t operator()( Args ... args) { BOOST_ASSERT( nullptr != fctx_); - detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), data); - return std::make_tuple( execution_context( t.fctx), t.data); + args_tpl_t tpl( std::forward< Args >( args) ... ); + detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), & tpl); + args_tpl_t data; // Note: value-initialization, all elements must be default constructible + if ( nullptr != t.data) { + data = std::move( * static_cast< args_tpl_t * >( t.data) ); + } + return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) ); } - template< typename Fn, typename ... Args > - std::tuple< execution_context, void * > operator()( exec_ontop_arg_t, Fn && fn, Args && ... args) { + template< typename Fn > + ret_tpl_t operator()( exec_ontop_arg_t, Fn && fn, Args ... args) { BOOST_ASSERT( nullptr != fctx_); - detail::transfer_t t = resume_ontop_( nullptr, - std::forward< Fn >( fn), - std::forward< Args >( args) ... ); - return std::make_tuple( execution_context( t.fctx), t.data); - } - - template< typename Fn, typename ... Args > - std::tuple< execution_context, void * > operator()( void * data, exec_ontop_arg_t, Fn && fn, Args && ... args) { - BOOST_ASSERT( nullptr != fctx_); - detail::transfer_t t = resume_ontop_( data, - std::forward< Fn >( fn), - std::forward< Args >( args) ... ); - return std::make_tuple( execution_context( t.fctx), t.data); + args_tpl_t tpl{ std::forward< Args >( args) ... }; + std::tuple< Fn, args_tpl_t > p = std::forward_as_tuple( fn, tpl); + detail::transfer_t t = detail::ontop_fcontext( + detail::exchange( fctx_, nullptr), + & p, + detail::context_ontop< execution_context, Fn, args_tpl_t >); + args_tpl_t data; // Note: value-initialization, all elements must be default constructible + if ( nullptr != t.data) { + data = * static_cast< args_tpl_t * >( t.data); + } + return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), data); } explicit operator bool() const noexcept { @@ -382,8 +377,10 @@ public: } }; -inline -void swap( execution_context & l, execution_context & r) noexcept { +#include + +template< typename ... Args > +void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept { l.swap( r); } diff --git a/include/boost/context/execution_context_v2_void.ipp b/include/boost/context/execution_context_v2_void.ipp new file mode 100644 index 0000000..d0659be --- /dev/null +++ b/include/boost/context/execution_context_v2_void.ipp @@ -0,0 +1,290 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +namespace detail { + +template< typename Ctx, typename Fn > +transfer_t context_ontop_void( transfer_t t) { + auto tpl = static_cast< std::tuple< Fn > * >( t.data); + BOOST_ASSERT( nullptr != tpl); + typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * tpl) ); + Ctx ctx{ t.fctx }; + // execute function + ctx = apply( + fn, + std::forward_as_tuple( std::move( ctx) ) ); + return { exchange( ctx.fctx_, nullptr), nullptr }; +} + +template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > +class record_void { +private: + StackAlloc salloc_; + stack_context sctx_; + typename std::decay< Fn >::type fn_; + std::tuple< typename std::decay< Params >::type ... > params_; + + static void destroy( record_void * p) noexcept { + StackAlloc salloc = p->salloc_; + stack_context sctx = p->sctx_; + // deallocate record + p->~record_void(); + // destroy stack with stack allocator + salloc.deallocate( sctx); + } + +public: + record_void( stack_context sctx, StackAlloc const& salloc, + Fn && fn, Params && ... params) noexcept : + salloc_( salloc), + sctx_( sctx), + fn_( std::forward< Fn >( fn) ), + params_( std::forward< Params >( params) ... ) { + } + + record_void( record_void const&) = delete; + record_void & operator=( record_void const&) = delete; + + void deallocate() noexcept { + destroy( this); + } + + transfer_t run( transfer_t t) { + Ctx from{ t.fctx }; + // invoke context-function + Ctx cc = apply( + fn_, + std::tuple_cat( + params_, + std::forward_as_tuple( std::move( from) ) ) ); + return { exchange( cc.fctx_, nullptr), nullptr }; + } +}; + +template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > +fcontext_t context_create_void( StackAlloc salloc, Fn && fn, Params && ... params) { + typedef record_void< Ctx, StackAlloc, Fn, Params ... > record_t; + + auto sctx = salloc.allocate(); + // reserve space for control structure +#if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) + const std::size_t size = sctx.size - sizeof( record_t); + void * sp = static_cast< char * >( sctx.sp) - sizeof( record_t); +#else + constexpr std::size_t func_alignment = 64; // alignof( record_t); + constexpr std::size_t func_size = sizeof( record_t); + // reserve space on stack + void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment; + // align sp pointer + std::size_t space = func_size + func_alignment; + sp = std::align( func_alignment, func_size, sp, space); + BOOST_ASSERT( nullptr != sp); + // calculate remaining size + const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) ); +#endif + // create fast-context + const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >); + BOOST_ASSERT( nullptr != fctx); + // placment new for control structure on context-stack + auto rec = ::new ( sp) record_t{ + sctx, salloc, std::forward< Fn >( fn), std::forward< Params >( params) ... }; + // transfer control structure to context-stack + return jump_fcontext( fctx, rec).fctx; +} + +template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > +fcontext_t context_create_void( preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params) { + typedef record_void< Ctx, StackAlloc, Fn, Params ... > record_t; + + // reserve space for control structure +#if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) + const std::size_t size = palloc.size - sizeof( record_t); + void * sp = static_cast< char * >( palloc.sp) - sizeof( record_t); +#else + constexpr std::size_t func_alignment = 64; // alignof( record_t); + constexpr std::size_t func_size = sizeof( record_t); + // reserve space on stack + void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment; + // align sp pointer + std::size_t space = func_size + func_alignment; + sp = std::align( func_alignment, func_size, sp, space); + BOOST_ASSERT( nullptr != sp); + // calculate remaining size + const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) ); +#endif + // create fast-context + const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >); + BOOST_ASSERT( nullptr != fctx); + // placment new for control structure on context-stack + auto rec = ::new ( sp) record_t{ + palloc.sctx, salloc, std::forward< Fn >( fn), std::forward< Params >( params) ... }; + // transfer control structure to context-stack + return jump_fcontext( fctx, rec).fctx; +} + +} + +template<> +class execution_context< void > { +private: + template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > + friend class detail::record_void; + + template< typename Ctx, typename Fn > + friend detail::transfer_t detail::context_ontop_void( detail::transfer_t); + + detail::fcontext_t fctx_{ nullptr }; + + execution_context( detail::fcontext_t fctx) noexcept : + fctx_( fctx) { + } + +public: + constexpr execution_context() noexcept = default; + +#if defined(BOOST_USE_SEGMENTED_STACKS) + // segmented-stack requires to preserve the segments of the `current` context + // which is not possible (no global pointer to current context) + template< typename Fn, typename ... Params > + execution_context( std::allocator_arg_t, segmented_stack, Fn &&, Params && ...) = delete; + + template< typename Fn, typename ... Params > + execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete; +#else + template< typename Fn, + typename ... Params, + typename = detail::disable_overload< execution_context, Fn > + > + execution_context( Fn && fn, Params && ... params) : + // deferred execution of fn and its arguments + // arguments are stored in std::tuple<> + // non-type template parameter pack via std::index_sequence_for<> + // preserves the number of arguments + // used to extract the function arguments from std::tuple<> + fctx_( detail::context_create_void< execution_context >( + fixedsize_stack(), + std::forward< Fn >( fn), + std::forward< Params >( params) ... ) ) { + } + + template< typename StackAlloc, + typename Fn, + typename ... Params + > + execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params) : + // deferred execution of fn and its arguments + // arguments are stored in std::tuple<> + // non-type template parameter pack via std::index_sequence_for<> + // preserves the number of arguments + // used to extract the function arguments from std::tuple<> + fctx_( detail::context_create_void< execution_context >( + salloc, + std::forward< Fn >( fn), + std::forward< Params >( params) ... ) ) { + } + + template< typename StackAlloc, + typename Fn, + typename ... Params + > + execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params) : + // deferred execution of fn and its arguments + // arguments are stored in std::tuple<> + // non-type template parameter pack via std::index_sequence_for<> + // preserves the number of arguments + // used to extract the function arguments from std::tuple<> + fctx_( detail::context_create_void< execution_context >( + palloc, salloc, + std::forward< Fn >( fn), + std::forward< Params >( params) ... ) ) { + } +#endif + + ~execution_context() { + if ( nullptr != fctx_) { + detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::context_unwind); + } + } + + execution_context( execution_context && other) noexcept : + fctx_( other.fctx_) { + other.fctx_ = nullptr; + } + + execution_context & operator=( execution_context && other) noexcept { + if ( this != & other) { + execution_context tmp = std::move( other); + swap( tmp); + } + return * this; + } + + execution_context( execution_context const& other) noexcept = delete; + execution_context & operator=( execution_context const& other) noexcept = delete; + + execution_context operator()() { + BOOST_ASSERT( nullptr != fctx_); + detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), nullptr); + return execution_context( t.fctx); + } + + template< typename Fn > + execution_context operator()( exec_ontop_arg_t, Fn && fn) { + BOOST_ASSERT( nullptr != fctx_); + std::tuple< Fn > p = std::forward_as_tuple( fn); + detail::transfer_t t = detail::ontop_fcontext( + detail::exchange( fctx_, nullptr), + & p, + detail::context_ontop_void< execution_context, Fn >); + return execution_context( t.fctx); + } + + explicit operator bool() const noexcept { + return nullptr != fctx_; + } + + bool operator!() const noexcept { + return nullptr == fctx_; + } + + bool operator==( execution_context const& other) const noexcept { + return fctx_ == other.fctx_; + } + + bool operator!=( execution_context const& other) const noexcept { + return fctx_ != other.fctx_; + } + + bool operator<( execution_context const& other) const noexcept { + return fctx_ < other.fctx_; + } + + bool operator>( execution_context const& other) const noexcept { + return other.fctx_ < fctx_; + } + + bool operator<=( execution_context const& other) const noexcept { + return ! ( * this > other); + } + + bool operator>=( execution_context const& other) const noexcept { + return ! ( * this < other); + } + + template< typename charT, class traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) { + if ( nullptr != other.fctx_) { + return os << other.fctx_; + } else { + return os << "{not-a-context}"; + } + } + + void swap( execution_context & other) noexcept { + std::swap( fctx_, other.fctx_); + } +}; diff --git a/test/test_execution_context_v2.impl b/test/test_execution_context_v2.impl index 240acff..b272a8e 100644 --- a/test/test_execution_context_v2.impl +++ b/test/test_execution_context_v2.impl @@ -13,12 +13,16 @@ #include #include +#include #include #include +#include #include #include +typedef boost::variant variant_t; + namespace ctx = boost::context; int value1 = 0; @@ -26,7 +30,7 @@ std::string value2; double value3 = 0.; struct X { - ctx::execution_context foo( int i, ctx::execution_context ctx, void * ignored) { + ctx::execution_context< void > foo( int i, ctx::execution_context< void > ctx) { value1 = i; return ctx; } @@ -42,21 +46,60 @@ struct Y { } }; -struct my_exception : public std::runtime_error { - ctx::execution_context ctx; +class moveable { +public: + bool state; + int value; - my_exception( char const* what, ctx::execution_context && ctx_) : - std::runtime_error( what), - ctx( std::forward< ctx::execution_context >( ctx_) ) { + moveable() : + state( false), + value( -1) { + } + + moveable( int v) : + state( true), + value( v) { + } + + moveable( moveable && other) : + state( other.state), + value( other.value) { + other.state = false; + other.value = -1; + } + + moveable & operator=( moveable && other) { + if ( this == & other) return * this; + state = other.state; + value = other.value; + other.state = false; + other.value = -1; + return * this; + } + + moveable( moveable const& other) = delete; + moveable & operator=( moveable const& other) = delete; + + void operator()() { + value1 = value; } }; -ctx::execution_context fn1( int i, ctx::execution_context ctx, void * ignored) { +struct my_exception : public std::runtime_error { + ctx::execution_context< void > ctx; + + my_exception( char const* what, ctx::execution_context< void > && ctx_) : + std::runtime_error( what), + ctx( std::forward< ctx::execution_context< void > >( ctx_) ) { + } +}; + +ctx::execution_context< void > fn1( int i, ctx::execution_context< void > ctx) { value1 = i; return ctx; } -ctx::execution_context fn2( const char * what, ctx::execution_context ctx, void * ignored) { +ctx::execution_context< void > fn2( const char * what, ctx::execution_context< void > ctx) { try { throw std::runtime_error( what); } catch ( std::runtime_error const& e) { @@ -65,30 +108,30 @@ ctx::execution_context fn2( const char * what, ctx::execution_context ctx, void return ctx; } -ctx::execution_context fn3( double d, ctx::execution_context ctx, void * ignored) { +ctx::execution_context< void > fn3( double d, ctx::execution_context< void > ctx) { d += 3.45; value3 = d; return ctx; } -ctx::execution_context fn5( ctx::execution_context ctx, void * ignored) { +ctx::execution_context< void > fn5( ctx::execution_context< void > ctx) { value1 = 3; return ctx; } -ctx::execution_context fn4( ctx::execution_context ctx, void * ignored) { - ctx::execution_context ctx1( fn5); +ctx::execution_context< void > fn4( ctx::execution_context< void > ctx) { + ctx::execution_context< void > ctx1( fn5); ctx1(); value3 = 3.14; return ctx; } -ctx::execution_context fn6( ctx::execution_context ctx, void * ignored) { +ctx::execution_context< void > fn6( ctx::execution_context< void > ctx) { try { value1 = 3; - std::tie( ctx, ignored) = ctx(); + ctx = ctx(); value1 = 7; - std::tie( ctx, ignored) = ctx( ignored); + ctx = ctx(); } catch ( my_exception & e) { value2 = e.what(); ctx = std::move( e.ctx); @@ -96,18 +139,55 @@ ctx::execution_context fn6( ctx::execution_context ctx, void * ignored) { return ctx; } -ctx::execution_context fn7( ctx::execution_context ctx, void * ignored) { +ctx::execution_context< void > fn7( ctx::execution_context< void > ctx) { Y y; - std::tie( ctx, ignored) = ctx(); + return ctx(); +} + +ctx::execution_context< int > fn8( ctx::execution_context< int > ctx, int i) { + value1 = i; return ctx; } +ctx::execution_context< int > fn9( ctx::execution_context< int > ctx, int i) { + std::tie( ctx, i) = ctx( i); + return ctx; +} + +ctx::execution_context< int & > fn10( ctx::execution_context< int & > ctx, int & i) { + std::tie( ctx, i) = ctx( i); + return ctx; +} + +ctx::execution_context< moveable > fn11( ctx::execution_context< moveable > ctx, moveable m) { + std::tie( ctx, m) = ctx( std::move( m) ); + return ctx; +} + +ctx::execution_context< int, std::string > fn12( ctx::execution_context< int, std::string > ctx, int i, std::string str) { + std::tie( ctx, i, str) = ctx( i, str); + return ctx; +} + +ctx::execution_context< int, moveable > fn13( ctx::execution_context< int, moveable > ctx, int i, moveable m) { + std::tie( ctx, i, m) = ctx( i, std::move( m) ); + return ctx; +} + +ctx::execution_context< variant_t > fn14( ctx::execution_context< variant_t > ctx, variant_t data) { + int i = boost::get< int >( data); + data = boost::lexical_cast< std::string >( i); + std::tie( ctx, data) = ctx( data); + return ctx; +} + + void test_move() { value1 = 0; - ctx::execution_context ctx; + ctx::execution_context< void > ctx; BOOST_CHECK( ! ctx); - ctx::execution_context ctx1( fn1, 1); - ctx::execution_context ctx2( fn1, 3); + ctx::execution_context< void > ctx1( fn1, 1); + ctx::execution_context< void > ctx2( fn1, 3); BOOST_CHECK( ctx1); BOOST_CHECK( ctx2); ctx1 = std::move( ctx2); @@ -121,21 +201,21 @@ void test_move() { void test_memfn() { value1 = 0; X x; - ctx::execution_context ctx( & X::foo, x, 7); + ctx::execution_context< void > ctx( & X::foo, x, 7); ctx(); BOOST_CHECK_EQUAL( 7, value1); } void test_exception() { const char * what = "hello world"; - ctx::execution_context ctx( fn2, what); + ctx::execution_context< void > ctx( fn2, what); ctx(); BOOST_CHECK_EQUAL( std::string( what), value2); } void test_fp() { double d = 7.13; - ctx::execution_context ctx( fn3, d); + ctx::execution_context< void > ctx( fn3, d); ctx(); BOOST_CHECK_EQUAL( 10.58, value3); } @@ -143,7 +223,7 @@ void test_fp() { void test_stacked() { value1 = 0; value3 = 0.; - ctx::execution_context ctx( fn4); + ctx::execution_context< void > ctx( fn4); ctx(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK_EQUAL( 3.14, value3); @@ -155,7 +235,7 @@ void test_prealloc() { ctx::stack_context sctx( alloc.allocate() ); void * sp = static_cast< char * >( sctx.sp) - 10; std::size_t size = sctx.size - 10; - ctx::execution_context ctx( std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc, fn1, 7); + ctx::execution_context< void > ctx( std::allocator_arg, ctx::preallocated( sp, size, sctx), alloc, fn1, 7); ctx(); BOOST_CHECK_EQUAL( 7, value1); } @@ -163,36 +243,31 @@ void test_prealloc() { void test_ontop() { value1 = 0; value2 = ""; - void * ignored; - ctx::execution_context ctx( fn6); - std::tie( ctx, ignored) = ctx(); + ctx::execution_context< void > ctx( fn6); + ctx = ctx(); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK( value2.empty() ); std::string str("abc"); - int i = 3; - std::tie( ctx, ignored) = ctx( & i, ctx::exec_ontop_arg, - [str](ctx::execution_context ctx, void * data){ - value2 = str; - return std::make_tuple( std::move( ctx), data); - } ); + ctx = ctx( ctx::exec_ontop_arg, + [str](ctx::execution_context< void > ctx){ + value2 = str; + return ctx; + } ); BOOST_CHECK_EQUAL( 7, value1); BOOST_CHECK_EQUAL( str, value2); - BOOST_CHECK_EQUAL( ignored, & i); - BOOST_CHECK_EQUAL( i, *( int*) ignored); } void test_ontop_exception() { value1 = 0; value2 = ""; - void * ignored; - ctx::execution_context ctx( fn6); - std::tie( ctx, ignored) = ctx(); + ctx::execution_context< void > ctx( fn6); + ctx = ctx(); BOOST_CHECK_EQUAL( 3, value1); const char * what = "hello world"; ctx( ctx::exec_ontop_arg, - [what](ctx::execution_context ctx, void * data){ + [what](ctx::execution_context< void > ctx){ throw my_exception( what, std::move( ctx) ); - return std::make_tuple( std::move( ctx), data); + return ctx; }); BOOST_CHECK_EQUAL( 3, value1); BOOST_CHECK_EQUAL( std::string( what), value2); @@ -201,15 +276,90 @@ void test_ontop_exception() { void test_termination() { value1 = 0; { - void * ignored; - ctx::execution_context ctx( fn7); + ctx::execution_context< void > ctx( fn7); BOOST_CHECK_EQUAL( 0, value1); - std::tie( ctx, ignored) = ctx(); + ctx = ctx(); BOOST_CHECK_EQUAL( 3, value1); } BOOST_CHECK_EQUAL( 7, value1); } +void test_one_arg() { + { + value1 = 0; + ctx::execution_context< int > ctx( fn8); + ctx( 7); + BOOST_CHECK_EQUAL( 7, value1); + } + { + int i = 3, j = 0; + ctx::execution_context< int > ctx( fn9); + std::tie( ctx, j) = ctx( i); + BOOST_CHECK_EQUAL( i, j); + } +#if 0 + { + int i = 3, j = 0; + int & k = j; + BOOST_CHECK( & i != & k); + BOOST_CHECK( & j == & k); + ctx::execution_context< int & > ctx( fn10); + std::tie( ctx, k) = ctx( i); + BOOST_CHECK( & i != & k); + } +#endif + { + moveable m1( 7), m2; + BOOST_CHECK( 7 == m1.value); + BOOST_CHECK( m1.state); + BOOST_CHECK( -1 == m2.value); + BOOST_CHECK( ! m2.state); + ctx::execution_context< moveable > ctx( fn11); + std::tie( ctx, m2) = ctx( std::move( m1) ); + BOOST_CHECK( -1 == m1.value); + BOOST_CHECK( ! m1.state); + BOOST_CHECK( 7 == m2.value); + BOOST_CHECK( m2.state); + } +} + +void test_two_args() { + { + int i1 = 3, i2 = 0; + std::string str1("abc"), str2; + ctx::execution_context< int, std::string > ctx( fn12); + std::tie( ctx, i2, str2) = ctx( i1, str1); + BOOST_CHECK_EQUAL( i1, i2); + BOOST_CHECK_EQUAL( str1, str2); + } + { + int i1 = 3, i2 = 0; + moveable m1( 7), m2; + BOOST_CHECK( 7 == m1.value); + BOOST_CHECK( m1.state); + BOOST_CHECK( -1 == m2.value); + BOOST_CHECK( ! m2.state); + ctx::execution_context< int, moveable > ctx( fn13); + std::tie( ctx, i2, m2) = ctx( i1, std::move( m1) ); + BOOST_CHECK_EQUAL( i1, i2); + BOOST_CHECK( -1 == m1.value); + BOOST_CHECK( ! m1.state); + BOOST_CHECK( 7 == m2.value); + BOOST_CHECK( m2.state); + } +} + +void test_variant() { + { + int i = 7; + variant_t data1 = i, data2; + ctx::execution_context< variant_t > ctx( fn14); + std::tie( ctx, data2) = ctx( data1); + std::string str = boost::get< std::string >( data2); + BOOST_CHECK_EQUAL( std::string("7"), str); + } +} + boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { boost::unit_test::test_suite * test = @@ -225,6 +375,9 @@ boost::unit_test::test_suite * init_unit_test_suite( int, char* []) test->add( BOOST_TEST_CASE( & test_ontop) ); test->add( BOOST_TEST_CASE( & test_ontop_exception) ); test->add( BOOST_TEST_CASE( & test_termination) ); + test->add( BOOST_TEST_CASE( & test_one_arg) ); + test->add( BOOST_TEST_CASE( & test_two_args) ); + test->add( BOOST_TEST_CASE( & test_variant) ); return test; }