2
0
mirror of https://github.com/boostorg/context.git synced 2026-01-19 04:02:17 +00:00

make execution_context (v2) typesafe

This commit is contained in:
Oliver Kowalke
2016-02-08 18:36:58 +01:00
parent 68a57f29b3
commit cf783fd907
11 changed files with 691 additions and 174 deletions

View File

@@ -26,22 +26,30 @@ project boost/context/example/execution_context
<threading>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
;

View File

@@ -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<<std::get<1>(result)<<" ";
}
std::cout<<std::endl;

View File

@@ -11,27 +11,20 @@
namespace ctx = boost::context;
ctx::execution_context f1( ctx::execution_context ctxm, void * data) {
std::cout << "f1: entered first time" << std::endl;
std::tie( ctxm, data) = ctxm();
std::cout << "f1: entered second time" << std::endl;
ctx::execution_context ctx2 = std::move( * static_cast< ctx::execution_context * >( 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;

31
example/v2/jump_void.cpp Normal file
View File

@@ -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 <cstdlib>
#include <iostream>
#include <boost/context/all.hpp>
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;
}

View File

@@ -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;

41
example/v2/ontop_void.cpp Normal file
View File

@@ -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 <cstdlib>
#include <iostream>
#include <tuple>
#include <boost/context/all.hpp>
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;
}

View File

@@ -10,28 +10,31 @@
#include <memory>
#include <string>
#include <boost/variant.hpp>
#include <boost/context/all.hpp>
#include <boost/lexical_cast.hpp>
typedef boost::variant<int,std::string> variant_t;
namespace ctx = boost::context;
class X{
private:
std::exception_ptr excptr_;
ctx::execution_context ctx_;
ctx::execution_context<variant_t> ctx_;
public:
X():
excptr_(),
ctx_(
[=](ctx::execution_context ctx, void * vp){
[=](ctx::execution_context<variant_t> ctx, variant_t data){
try {
for (;;) {
int i = * static_cast< int * >( vp);
std::string str = boost::lexical_cast<std::string>(i);
auto result = ctx( & str);
int i = boost::get<int>(data);
data = boost::lexical_cast<std::string>(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<std::string>(data);
}
};

View File

@@ -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<char> source(
[&is,&done,&except](ctx::execution_context<char> 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<char*>(vp));
std::tie(source,vp) = source();
printf("Parsed: %c\n",c);
std::tie(source,c) = source('\0');
if (except) {
std::rethrow_exception(except);
}

View File

@@ -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 <boost/context/execution_context_v2_void.ipp>
template< typename ... Args >
void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept {
l.swap( r);
}

View File

@@ -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_);
}
};

View File

@@ -13,12 +13,16 @@
#include <boost/array.hpp>
#include <boost/assert.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/utility.hpp>
#include <boost/variant.hpp>
#include <boost/context/all.hpp>
#include <boost/context/detail/config.hpp>
typedef boost::variant<int,std::string> 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;
}