diff --git a/example/cpp03/Jamfile.v2 b/example/cpp03/Jamfile.v2 index ed49320..3286936 100644 --- a/example/cpp03/Jamfile.v2 +++ b/example/cpp03/Jamfile.v2 @@ -68,3 +68,11 @@ exe unwind exe same_fringe : same_fringe.cpp ; + +exe layout + : layout.cpp + ; + +exe chaining + : chaining.cpp + ; diff --git a/example/cpp03/chaining.cpp b/example/cpp03/chaining.cpp new file mode 100644 index 0000000..bc89974 --- /dev/null +++ b/example/cpp03/chaining.cpp @@ -0,0 +1,203 @@ + +// Copyright Nat Goodspeed 2013. +// 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 +#include + +#include +#include +#include + +typedef boost::coroutines::coroutine coro_t; + +// deliver each line of input stream to sink as a separate string +void readlines(coro_t::push_type& sink, std::istream& in) +{ + std::string line; + while (std::getline(in, line)) + sink(line); +} + +void tokenize(coro_t::push_type& sink, coro_t::pull_type& source) +{ + // This tokenizer doesn't happen to be stateful: you could reasonably + // implement it with a single call to push each new token downstream. But + // I've worked with stateful tokenizers, in which the meaning of input + // characters depends in part on their position within the input line. At + // the time, I wished for a way to resume at the suspend point! + BOOST_FOREACH(std::string line, source) + { + std::string::size_type pos = 0; + while (pos < line.length()) + { + if (line[pos] == '"') + { + std::string token; + ++pos; // skip open quote + while (pos < line.length() && line[pos] != '"') + token += line[pos++]; + ++pos; // skip close quote + sink(token); // pass token downstream + } + else if (std::isspace(line[pos])) + { + ++pos; // outside quotes, ignore whitespace + } + else if (std::isalpha(line[pos])) + { + std::string token; + while (pos < line.length() && std::isalpha(line[pos])) + token += line[pos++]; + sink(token); // pass token downstream + } + else // punctuation + { + sink(std::string(1, line[pos++])); + } + } + } +} + +void only_words(coro_t::push_type& sink, coro_t::pull_type& source) +{ + BOOST_FOREACH(std::string token, source) + { + if (! token.empty() && std::isalpha(token[0])) + sink(token); + } +} + +void trace(coro_t::push_type& sink, coro_t::pull_type& source) +{ + BOOST_FOREACH(std::string token, source) + { + std::cout << "trace: '" << token << "'\n"; + sink(token); + } +} + +struct FinalEOL +{ + ~FinalEOL() { std::cout << std::endl; } +}; + +void layout(coro_t::pull_type& source, int num, int width) +{ + // Finish the last line when we leave by whatever means + FinalEOL eol; + + // Pull values from upstream, lay them out 'num' to a line + for (;;) + { + for (int i = 0; i < num; ++i) + { + // when we exhaust the input, stop + if (! source) + return; + + std::cout << std::setw(width) << source.get(); + // now that we've handled this item, advance to next + source(); + } + // after 'num' items, line break + std::cout << std::endl; + } +} + +int main(int argc, char *argv[]) +{ + // For example purposes, instead of having a separate text file in the + // local filesystem, construct an istringstream to read. + std::string data( + "This is the first line.\n" + "This, the second.\n" + "The third has \"a phrase\"!\n" + ); + + { + std::cout << "\nreadlines:\n"; + std::istringstream infile(data); + // Each coroutine-function has a small, specific job to do. Instead of + // adding conditional logic to a large, complex input function, the + // caller composes smaller functions into the desired processing + // chain. + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tracer(boost::bind(trace, _1, boost::ref(reader))); + BOOST_FOREACH(std::string line, tracer) + { + std::cout << "got: " << line << "\n"; + } + } + + { + std::cout << "\ncompose a chain:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::pull_type tracer(boost::bind(trace, _1, boost::ref(tokenizer))); + BOOST_FOREACH(std::string token, tracer) + { + // just iterate, we're already pulling through tracer + } + } + + { + std::cout << "\nfilter:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::pull_type filter(boost::bind(only_words, _1, boost::ref(tokenizer))); + coro_t::pull_type tracer(boost::bind(trace, _1, boost::ref(filter))); + BOOST_FOREACH(std::string token, tracer) + { + // just iterate, we're already pulling through tracer + } + } + + { + std::cout << "\nlayout() as coroutine::push_type:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::pull_type filter(boost::bind(only_words, _1, boost::ref(tokenizer))); + coro_t::push_type writer(boost::bind(layout, _1, 5, 15)); + BOOST_FOREACH(std::string token, filter) + { + writer(token); + } + } + + { + std::cout << "\ncalling layout() directly:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::pull_type filter(boost::bind(only_words, _1, boost::ref(tokenizer))); + // Because of the symmetry of the API, we can directly call layout() + // instead of using it as a coroutine-function. + layout(filter, 5, 15); + } + + { + std::cout << "\nfiltering output:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::push_type writer(boost::bind(layout, _1, 5, 15)); + // Because of the symmetry of the API, we can use any of these + // chaining functions in a push_type coroutine chain as well. + coro_t::push_type filter(boost::bind(only_words, boost::ref(writer), _1)); + BOOST_FOREACH(std::string token, tokenizer) + { + filter(token); + } + } + + return 0; +} diff --git a/example/cpp03/layout.cpp b/example/cpp03/layout.cpp new file mode 100644 index 0000000..2b5f915 --- /dev/null +++ b/example/cpp03/layout.cpp @@ -0,0 +1,71 @@ + +// Copyright Nat Goodspeed 2013. +// 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 +#include + +#include +#include +#include +#include + +struct FinalEOL +{ + ~FinalEOL() { std::cout << std::endl; } +}; + +void layout(boost::coroutines::coroutine::pull_type& in, int num, int width) +{ + // Finish the last line when we leave by whatever means + FinalEOL eol; + + // Pull values from upstream, lay them out 'num' to a line + for (;;) + { + for (int i = 0; i < num; ++i) + { + // when we exhaust the input, stop + if (! in) + return; + + std::cout << std::setw(width) << in.get(); + // now that we've handled this item, advance to next + in(); + } + // after 'num' items, line break + std::cout << std::endl; + } +} + +int main(int argc, char *argv[]) +{ + std::vector words = boost::assign::list_of + ("peas") + ("porridge") + ("hot") + ("peas") + ("porridge") + ("cold") + ("peas") + ("porridge") + ("in") + ("the") + ("pot") + ("nine") + ("days") + ("old") + ; + + boost::coroutines::coroutine::push_type writer( + boost::bind(layout, _1, 5, 15)); + + std::copy(boost::begin(words), boost::end(words), boost::begin(writer)); + + return 0; +} diff --git a/example/cpp03/same_fringe.cpp b/example/cpp03/same_fringe.cpp index a2df612..5be93bc 100644 --- a/example/cpp03/same_fringe.cpp +++ b/example/cpp03/same_fringe.cpp @@ -1,5 +1,5 @@ -// Copyright Oliver Kowalke 2009. +// Copyright Nat Goodspeed 2013. // 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) @@ -7,109 +7,155 @@ #include #include #include +#include #include #include #include #include +#include +#include -#include "tree.h" - -std::pair< node::ptr_t, node::ptr_t > create_eq_trees() +struct node { - branch::ptr_t tree1 = branch::create( - leaf::create( "A"), - branch::create( - leaf::create( "B"), - leaf::create( "C") ) ); + typedef boost::shared_ptr< node > ptr_t; - branch::ptr_t tree2 = branch::create( - branch::create( - leaf::create( "A"), - leaf::create( "B") ), - leaf::create( "C") ); + // Each tree node has an optional left subtree, an optional right subtree + // and a value of its own. The value is considered to be between the left + // subtree and the right. + ptr_t left, right; + std::string value; - return std::make_pair( tree1, tree2); + // construct leaf + node(const std::string& v): + left(), right(), value(v) + {} + // construct nonleaf + node(ptr_t l, const std::string& v, ptr_t r): + left(l), right(r), value(v) + {} + + static ptr_t create(const std::string& v) + { + return ptr_t(new node(v)); + } + + static ptr_t create(ptr_t l, const std::string& v, ptr_t r) + { + return ptr_t(new node(l, v, r)); + } +}; + +node::ptr_t create_left_tree_from(const std::string& root) +{ + /* -------- + root + / \ + b e + / \ + a c + -------- */ + return node::create( + node::create( + node::create("a"), + "b", + node::create("c")), + root, + node::create("e")); } -std::pair< node::ptr_t, node::ptr_t > create_diff_trees() +node::ptr_t create_right_tree_from(const std::string& root) { - branch::ptr_t tree1 = branch::create( - leaf::create( "A"), - branch::create( - leaf::create( "B"), - leaf::create( "C") ) ); - - branch::ptr_t tree2 = branch::create( - branch::create( - leaf::create( "A"), - leaf::create( "X") ), - leaf::create( "C") ); - - return std::make_pair( tree1, tree2); + /* -------- + root + / \ + a d + / \ + c e + -------- */ + return node::create( + node::create("a"), + root, + node::create( + node::create("c"), + "d", + node::create("e"))); } -#ifdef BOOST_COROUTINES_UNIDIRECT -bool match_trees( boost::coroutines::coroutine< leaf & >::pull_type & c1, - boost::coroutines::coroutine< leaf & >::pull_type & c2) +// recursively walk the tree, delivering values in order +void traverse(node::ptr_t n,boost::coroutines::coroutine::push_type& out) { - typedef boost::range_iterator< boost::coroutines::coroutine< leaf & >::pull_type >::type iterator_t; - iterator_t i1( boost::begin( c1) ); - iterator_t e1( boost::end( c1) ); - iterator_t i2( boost::begin( c2) ); - return std::equal( i1, e1, i2); + if (n->left) traverse(n->left,out); + out(n->value); + if (n->right) traverse(n->right,out); } int main() { { - std::pair< node::ptr_t, node::ptr_t > pt = create_eq_trees(); - boost::coroutines::coroutine< leaf & >::pull_type te1( boost::bind( enumerate_leafs, _1, pt.first) ); - boost::coroutines::coroutine< leaf & >::pull_type te2( boost::bind( enumerate_leafs, _1, pt.second) ); - bool result = match_trees( te1, te2); - std::cout << std::boolalpha << "eq. trees matched == " << result << std::endl; + node::ptr_t left_d(create_left_tree_from("d")); + boost::coroutines::coroutine::pull_type left_d_reader( + boost::bind(traverse, left_d, _1)); + std::cout << "left tree from d:\n"; + std::copy(boost::begin(left_d_reader), + boost::end(left_d_reader), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + node::ptr_t right_b(create_right_tree_from("b")); + boost::coroutines::coroutine::pull_type right_b_reader( + boost::bind(traverse, right_b, _1)); + std::cout << "right tree from b:\n"; + std::copy(boost::begin(right_b_reader), + boost::end(right_b_reader), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + node::ptr_t right_x(create_right_tree_from("x")); + boost::coroutines::coroutine::pull_type right_x_reader( + boost::bind(traverse, right_x, _1)); + std::cout << "right tree from x:\n"; + std::copy(boost::begin(right_x_reader), + boost::end(right_x_reader), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; } + { - std::pair< node::ptr_t, node::ptr_t > pt = create_diff_trees(); - boost::coroutines::coroutine< leaf & >::pull_type te1( boost::bind( enumerate_leafs, _1, pt.first) ); - boost::coroutines::coroutine< leaf & >::pull_type te2( boost::bind( enumerate_leafs, _1, pt.second) ); - bool result = match_trees( te1, te2); - std::cout << std::boolalpha << "diff. trees matched == " << result << std::endl; + node::ptr_t left_d(create_left_tree_from("d")); + boost::coroutines::coroutine::pull_type left_d_reader( + boost::bind(traverse, left_d, _1)); + + node::ptr_t right_b(create_right_tree_from("b")); + boost::coroutines::coroutine::pull_type right_b_reader( + boost::bind(traverse, right_b, _1)); + + std::cout << "left tree from d == right tree from b? " + << std::boolalpha + << std::equal(boost::begin(left_d_reader), + boost::end(left_d_reader), + boost::begin(right_b_reader)) + << std::endl; + } + + { + node::ptr_t left_d(create_left_tree_from("d")); + boost::coroutines::coroutine::pull_type left_d_reader( + boost::bind(traverse, left_d, _1)); + + node::ptr_t right_x(create_right_tree_from("x")); + boost::coroutines::coroutine::pull_type right_x_reader( + boost::bind(traverse, right_x, _1)); + + std::cout << "left tree from d == right tree from x? " + << std::boolalpha + << std::equal(boost::begin(left_d_reader), + boost::end(left_d_reader), + boost::begin(right_x_reader)) + << std::endl; } std::cout << "Done" << std::endl; return EXIT_SUCCESS; } -#else -bool match_trees( coro_t & c1, coro_t & c2) -{ - typedef boost::range_iterator< coro_t >::type iterator_t; - iterator_t i1( boost::begin( c1) ); - iterator_t e1( boost::end( c1) ); - iterator_t i2( boost::begin( c2) ); - return std::equal( i1, e1, i2); -} - -int main() -{ - { - std::pair< node::ptr_t, node::ptr_t > pt = create_eq_trees(); - coro_t te1( boost::bind( enumerate_leafs, _1, pt.first) ); - coro_t te2( boost::bind( enumerate_leafs, _1, pt.second) ); - bool result = match_trees( te1, te2); - std::cout << std::boolalpha << "eq. trees matched == " << result << std::endl; - } - { - std::pair< node::ptr_t, node::ptr_t > pt = create_diff_trees(); - coro_t te1( boost::bind( enumerate_leafs, _1, pt.first) ); - coro_t te2( boost::bind( enumerate_leafs, _1, pt.second) ); - bool result = match_trees( te1, te2); - std::cout << std::boolalpha << "diff. trees matched == " << result << std::endl; - } - - std::cout << "Done" << std::endl; - - return EXIT_SUCCESS; -} -#endif diff --git a/example/cpp11/Jamfile.v2 b/example/cpp11/Jamfile.v2 index 8cec863..925e676 100644 --- a/example/cpp11/Jamfile.v2 +++ b/example/cpp11/Jamfile.v2 @@ -52,3 +52,7 @@ exe fibonacci exe await_emu : await_emu.cpp ; + +exe layout + : layout.cpp + ; diff --git a/example/cpp11/await_emu.cpp b/example/cpp11/await_emu.cpp index 1260851..3f06af5 100644 --- a/example/cpp11/await_emu.cpp +++ b/example/cpp11/await_emu.cpp @@ -192,6 +192,6 @@ void async_user_handler() for(auto i=0; i!=5; ++i) fs.push_back( asynchronous([i]{ return foo(i+1); }) ); - BOOST_FOREACH(auto &&f, fs) + for(auto &&f : fs) cout << await f << ":\tafter end" << endl; } diff --git a/example/cpp11/layout.cpp b/example/cpp11/layout.cpp new file mode 100644 index 0000000..2505f62 --- /dev/null +++ b/example/cpp11/layout.cpp @@ -0,0 +1,50 @@ + +// Copyright Nat Goodspeed 2013. +// 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 +#include + +#include + +struct FinalEOL{ + ~FinalEOL(){ + std::cout << std::endl; + } +}; + +int main(int argc,char* argv[]){ + std::vector words{ + "peas", "porridge", "hot", "peas", + "porridge", "cold", "peas", "porridge", + "in", "the", "pot", "nine", + "days", "old" }; + + int num=5,width=15; + boost::coroutines::coroutine::push_type writer( + [&](boost::coroutines::coroutine::pull_type& in){ + // finish the last line when we leave by whatever means + FinalEOL eol; + // pull values from upstream, lay them out 'num' to a line + for (;;){ + for(int i=0;i #include #include +#include +#include +#include #include -#include "tree.h" - -node::ptr_t create_tree1() +struct node { - return branch::create( - leaf::create( "A"), - branch::create( - leaf::create( "B"), - leaf::create( "C") ) ); -} + typedef std::shared_ptr< node > ptr_t; -node::ptr_t create_tree2() -{ - return branch::create( - branch::create( - leaf::create( "A"), - leaf::create( "B") ), - leaf::create( "C") ); -} + // Each tree node has an optional left subtree, an optional right subtree + // and a value of its own. The value is considered to be between the left + // subtree and the right. + ptr_t left, right; + std::string value; -#ifdef BOOST_COROUTINES_UNIDIRECT -class coro_visitor : public visitor -{ -private: - boost::coroutines::coroutine< leaf& >::push_type & c_; - -public: - coro_visitor( boost::coroutines::coroutine< leaf& >::push_type & c) : - c_( c) + // construct leaf + node(const std::string& v): + left(),right(),value(v) + {} + // construct nonleaf + node(ptr_t l, const std::string& v, ptr_t r): + left(l),right(r),value(v) {} - void visit( branch & b) + static ptr_t create(const std::string& v) { - if ( b.left) b.left->accept( * this); - if ( b.right) b.right->accept( * this); + return ptr_t(new node(v)); } - void visit( leaf & l) - { c_( l); } + static ptr_t create(ptr_t l, const std::string& v, ptr_t r) + { + return ptr_t(new node(l, v, r)); + } }; +node::ptr_t create_left_tree_from(const std::string& root) +{ + /* -------- + root + / \ + b e + / \ + a c + -------- */ + + return node::create( + node::create( + node::create("a"), + "b", + node::create("c")), + root, + node::create("e")); +} + +node::ptr_t create_right_tree_from(const std::string& root) +{ + /* -------- + root + / \ + a d + / \ + c e + -------- */ + + return node::create( + node::create("a"), + root, + node::create( + node::create("c"), + "d", + node::create("e"))); +} + +// recursively walk the tree, delivering values in order +void traverse(node::ptr_t n, boost::coroutines::coroutine::push_type& out) +{ + if (n->left) + traverse(n->left,out); + out(n->value); + if (n->right) + traverse(n->right,out); +} + int main() { - node::ptr_t t1 = create_tree1(); - boost::coroutines::coroutine< leaf& >::pull_type c1( - [&]( boost::coroutines::coroutine< leaf & >::push_type & c) { - coro_visitor v( c); - t1->accept( v); - }); - - node::ptr_t t2 = create_tree2(); - boost::coroutines::coroutine< leaf& >::pull_type c2( - [&]( boost::coroutines::coroutine< leaf & >::push_type & c) { - coro_visitor v( c); - t2->accept( v); - }); - - bool result = std::equal( - boost::begin( c1), - boost::end( c1), - boost::begin( c2) ); - - std::cout << std::boolalpha << "same fringe == " << result << "\nDone" << std::endl; - - return EXIT_SUCCESS; -} -#else -class coro_visitor : public visitor -{ -private: - boost::coroutines::coroutine< void( leaf&) > & c_; - -public: - coro_visitor( boost::coroutines::coroutine< void( leaf&) > & c) : - c_( c) - {} - - void visit( branch & b) { - if ( b.left) b.left->accept( * this); - if ( b.right) b.right->accept( * this); + node::ptr_t left_d(create_left_tree_from("d")); + boost::coroutines::coroutine::pull_type left_d_reader( + [&]( boost::coroutines::coroutine::push_type & out) { + traverse(left_d,out); + }); + std::cout << "left tree from d:\n"; + std::copy(std::begin(left_d_reader), + std::end(left_d_reader), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + node::ptr_t right_b(create_right_tree_from("b")); + boost::coroutines::coroutine::pull_type right_b_reader( + [&]( boost::coroutines::coroutine::push_type & out) { + traverse(right_b,out); + }); + std::cout << "right tree from b:\n"; + std::copy(std::begin(right_b_reader), + std::end(right_b_reader), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + node::ptr_t right_x(create_right_tree_from("x")); + boost::coroutines::coroutine::pull_type right_x_reader( + [&]( boost::coroutines::coroutine::push_type & out) { + traverse(right_x,out); + }); + std::cout << "right tree from x:\n"; + std::copy(std::begin(right_x_reader), + std::end(right_x_reader), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; } - void visit( leaf & l) - { c_( l); } -}; + { + node::ptr_t left_d(create_left_tree_from("d")); + boost::coroutines::coroutine::pull_type left_d_reader( + [&]( boost::coroutines::coroutine::push_type & out) { + traverse(left_d,out); + }); -int main() -{ - node::ptr_t t1 = create_tree1(); - boost::coroutines::coroutine< leaf&() > c1( - [&]( boost::coroutines::coroutine< void( leaf &) > & c) { - coro_visitor v( c); - t1->accept( v); - }); + node::ptr_t right_b(create_right_tree_from("b")); + boost::coroutines::coroutine::pull_type right_b_reader( + [&]( boost::coroutines::coroutine::push_type & out) { + traverse(right_b,out); + }); - node::ptr_t t2 = create_tree2(); - boost::coroutines::coroutine< leaf&() > c2( - [&]( boost::coroutines::coroutine< void( leaf &) > & c) { - coro_visitor v( c); - t2->accept( v); - }); + std::cout << "left tree from d == right tree from b? " + << std::boolalpha + << std::equal(std::begin(left_d_reader), + std::end(left_d_reader), + std::begin(right_b_reader)) + << std::endl; + } - bool result = std::equal( - boost::begin( c1), - boost::end( c1), - boost::begin( c2) ); + { + node::ptr_t left_d(create_left_tree_from("d")); + boost::coroutines::coroutine::pull_type left_d_reader( + [&]( boost::coroutines::coroutine::push_type & out) { + traverse(left_d,out); + }); - std::cout << std::boolalpha << "same fringe == " << result << "\nDone" << std::endl; + node::ptr_t right_x(create_right_tree_from("x")); + boost::coroutines::coroutine::pull_type right_x_reader( + [&]( boost::coroutines::coroutine::push_type & out) { + traverse(right_x,out); + }); + + std::cout << "left tree from d == right tree from x? " + << std::boolalpha + << std::equal(std::begin(left_d_reader), + std::end(left_d_reader), + std::begin(right_x_reader)) + << std::endl; + } + + std::cout << "Done" << std::endl; return EXIT_SUCCESS; } -#endif diff --git a/include/boost/coroutine/v2/coroutine.hpp b/include/boost/coroutine/v2/coroutine.hpp index ac8b10e..ed26c2e 100644 --- a/include/boost/coroutine/v2/coroutine.hpp +++ b/include/boost/coroutine/v2/coroutine.hpp @@ -249,7 +249,7 @@ public: return * this; } #else - push_coroutine & operator()( Arg arg) + push_coroutine & operator()( Arg const& arg) { BOOST_ASSERT( * this); @@ -537,240 +537,6 @@ public: struct const_iterator; }; -template< typename Arg > -class push_coroutine< Arg const& > -{ -private: - template< - typename X, typename Y, typename Z, typename V, typename W - > - friend class detail::pull_coroutine_object; - - typedef detail::push_coroutine_base< Arg const& > base_t; - typedef typename base_t::ptr_t ptr_t; - - struct dummy - { void nonnull() {} }; - - typedef void ( dummy::*safe_bool)(); - - ptr_t impl_; - - BOOST_MOVABLE_BUT_NOT_COPYABLE( push_coroutine) - - template< typename Allocator > - push_coroutine( detail::coroutine_context const& callee, - bool unwind, bool preserve_fpu, - Allocator const& alloc) : - impl_() - { - typedef detail::push_coroutine_caller< - Arg const&, Allocator - > caller_t; - typename caller_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) caller_t( - callee, unwind, preserve_fpu, a) ); - } - -public: - push_coroutine() BOOST_NOEXCEPT : - impl_() - {} - -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES -#ifdef BOOST_MSVC - typedef void ( * coroutine_fn) ( pull_coroutine< Arg const& > &); - - explicit push_coroutine( coroutine_fn fn, attributes const& attr = attributes(), - stack_allocator const& stack_alloc = - stack_allocator(), - std::allocator< push_coroutine > const& alloc = - std::allocator< push_coroutine >(), - typename disable_if< - is_same< typename decay< coroutine_fn >::type, push_coroutine >, - dummy * - >::type = 0); - - template< typename StackAllocator > - explicit push_coroutine( coroutine_fn fn, attributes const& attr, - StackAllocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc = - std::allocator< push_coroutine >(), - typename disable_if< - is_same< typename decay< coroutine_fn >::type, push_coroutine >, - dummy * - >::type = 0); - - template< typename StackAllocator, typename Allocator > - explicit push_coroutine( coroutine_fn fn, attributes const& attr, - StackAllocator const& stack_alloc, - Allocator const& alloc, - typename disable_if< - is_same< typename decay< coroutine_fn >::type, push_coroutine >, - dummy * - >::type = 0); -#endif - template< typename Fn > - explicit push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr = attributes(), - stack_allocator const& stack_alloc = - stack_allocator(), - std::allocator< push_coroutine > const& alloc = - std::allocator< push_coroutine >(), - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type = 0); - - template< typename Fn, typename StackAllocator > - explicit push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - StackAllocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc = - std::allocator< push_coroutine >(), - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type = 0); - - template< typename Fn, typename StackAllocator, typename Allocator > - explicit push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - StackAllocator const& stack_alloc, - Allocator const& alloc, - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type = 0); -#else - template< typename Fn > - explicit push_coroutine( Fn fn, attributes const& attr = attributes(), - stack_allocator const& stack_alloc = - stack_allocator(), - std::allocator< push_coroutine > const& alloc = - std::allocator< push_coroutine >(), - typename disable_if< - is_convertible< Fn &, BOOST_RV_REF( Fn) >, - dummy * - >::type = 0); - - template< typename Fn, typename StackAllocator > - explicit push_coroutine( Fn fn, attributes const& attr, - StackAllocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc = - std::allocator< push_coroutine >(), - typename disable_if< - is_convertible< Fn &, BOOST_RV_REF( Fn) >, - dummy * - >::type = 0); - - template< typename Fn, typename StackAllocator, typename Allocator > - explicit push_coroutine( Fn fn, attributes const& attr, - StackAllocator const& stack_alloc, - Allocator const& alloc, - typename disable_if< - is_convertible< Fn &, BOOST_RV_REF( Fn) >, - dummy * - >::type = 0); - - template< typename Fn > - explicit push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr = attributes(), - stack_allocator const& stack_alloc = - stack_allocator(), - std::allocator< push_coroutine > const& alloc = - std::allocator< push_coroutine >(), - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type = 0); - - template< typename Fn, typename StackAllocator > - explicit push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - StackAllocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc = - std::allocator< push_coroutine >(), - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type = 0); - - template< typename Fn, typename StackAllocator, typename Allocator > - explicit push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - StackAllocator const& stack_alloc, - Allocator const& alloc, - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type = 0); -#endif - - push_coroutine( BOOST_RV_REF( push_coroutine) other) BOOST_NOEXCEPT : - impl_() - { swap( other); } - - push_coroutine & operator=( BOOST_RV_REF( push_coroutine) other) BOOST_NOEXCEPT - { - push_coroutine tmp( boost::move( other) ); - swap( tmp); - return * this; - } - - bool empty() const BOOST_NOEXCEPT - { return ! impl_; } - - operator safe_bool() const BOOST_NOEXCEPT - { return ( empty() || impl_->is_complete() ) ? 0 : & dummy::nonnull; } - - bool operator!() const BOOST_NOEXCEPT - { return empty() || impl_->is_complete(); } - - void swap( push_coroutine & other) BOOST_NOEXCEPT - { impl_.swap( other.impl_); } - - push_coroutine & operator()( Arg const& arg) - { - BOOST_ASSERT( * this); - - impl_->push( arg); - return * this; - } - - class iterator : public std::iterator< std::output_iterator_tag, void, void, void, void > - { - private: - push_coroutine< Arg const& > * c_; - - public: - iterator() : - c_( 0) - {} - - explicit iterator( push_coroutine< Arg const& > * c) : - c_( c) - {} - - iterator & operator=( Arg const& a) - { - BOOST_ASSERT( c_); - if ( ! ( * c_)( a) ) c_ = 0; - return * this; - } - - bool operator==( iterator const& other) - { return other.c_ == c_; } - - bool operator!=( iterator const& other) - { return other.c_ != c_; } - - iterator & operator*() - { return * this; } - - iterator & operator++() - { return * this; } - }; - - struct const_iterator; -}; - template<> class push_coroutine< void > { @@ -1475,6 +1241,507 @@ public: }; }; +template< typename R > +class pull_coroutine< R & > +{ +private: + template< + typename X, typename Y, typename Z, typename V, typename W + > + friend class detail::push_coroutine_object; + + typedef detail::pull_coroutine_base< R & > base_t; + typedef typename base_t::ptr_t ptr_t; + + struct dummy + { void nonnull() {} }; + + typedef void ( dummy::*safe_bool)(); + + ptr_t impl_; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( pull_coroutine) + + template< typename Allocator > + pull_coroutine( detail::coroutine_context const& callee, + bool unwind, bool preserve_fpu, + Allocator const& alloc, + optional< R * > const& result) : + impl_() + { + typedef detail::pull_coroutine_caller< + R &, Allocator + > caller_t; + typename caller_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) caller_t( + callee, unwind, preserve_fpu, a, result) ); + } + +public: + pull_coroutine() BOOST_NOEXCEPT : + impl_() + {} + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +#ifdef BOOST_MSVC + typedef void ( * coroutine_fn) ( push_coroutine< R & > &); + + explicit pull_coroutine( coroutine_fn fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< pull_coroutine > const& alloc = + std::allocator< pull_coroutine >(), + typename disable_if< + is_same< typename decay< coroutine_fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, coroutine_fn, stack_allocator, std::allocator< pull_coroutine >, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } + + template< typename StackAllocator > + explicit pull_coroutine( coroutine_fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< pull_coroutine > const& alloc = + std::allocator< pull_coroutine >(), + typename disable_if< + is_same< typename decay< coroutine_fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, coroutine_fn, StackAllocator, std::allocator< pull_coroutine >, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } + + template< typename StackAllocator, typename Allocator > + explicit pull_coroutine( coroutine_fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_same< typename decay< coroutine_fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, coroutine_fn, StackAllocator, Allocator, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } +#endif + template< typename Fn > + explicit pull_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< pull_coroutine > const& alloc = + std::allocator< pull_coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, stack_allocator, std::allocator< pull_coroutine >, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit pull_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< pull_coroutine > const& alloc = + std::allocator< pull_coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, StackAllocator, std::allocator< pull_coroutine >, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit pull_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_same< typename decay< Fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, StackAllocator, Allocator, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } +#else + template< typename Fn > + explicit pull_coroutine( Fn fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< pull_coroutine > const& alloc = + std::allocator< pull_coroutine >(), + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, stack_allocator, std::allocator< pull_coroutine >, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit pull_coroutine( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< pull_coroutine > const& alloc = + std::allocator< pull_coroutine >(), + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, StackAllocator, std::allocator< pull_coroutine >, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit pull_coroutine( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, StackAllocator, Allocator, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn > + explicit pull_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< pull_coroutine > const& alloc = + std::allocator< pull_coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, stack_allocator, std::allocator< pull_coroutine >, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit pull_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< pull_coroutine > const& alloc = + std::allocator< pull_coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, StackAllocator, std::allocator< pull_coroutine >, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit pull_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_same< typename decay< Fn >::type, pull_coroutine >, + dummy * + >::type = 0) : + impl_() + { + typedef detail::pull_coroutine_object< + R &, Fn, StackAllocator, Allocator, + push_coroutine< R & > + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } +#endif + + pull_coroutine( BOOST_RV_REF( pull_coroutine) other) BOOST_NOEXCEPT : + impl_() + { swap( other); } + + pull_coroutine & operator=( BOOST_RV_REF( pull_coroutine) other) BOOST_NOEXCEPT + { + pull_coroutine tmp( boost::move( other) ); + swap( tmp); + return * this; + } + + bool empty() const BOOST_NOEXCEPT + { return ! impl_; } + + operator safe_bool() const BOOST_NOEXCEPT + { return ( empty() || impl_->is_complete() ) ? 0 : & dummy::nonnull; } + + bool operator!() const BOOST_NOEXCEPT + { return empty() || impl_->is_complete(); } + + void swap( pull_coroutine & other) BOOST_NOEXCEPT + { impl_.swap( other.impl_); } + + pull_coroutine & operator()() + { + BOOST_ASSERT( * this); + + impl_->pull(); + return * this; + } + + bool has_result() const + { + BOOST_ASSERT( ! empty() ); + + return impl_->has_result(); + } + + R & get() const + { + BOOST_ASSERT( has_result() ); + + return impl_->get(); + } + + class iterator : public std::iterator< std::input_iterator_tag, R > + { + private: + pull_coroutine< R & > * c_; + optional< R & > val_; + + void fetch_() + { + BOOST_ASSERT( c_); + + if ( ! c_->has_result() ) + { + c_ = 0; + val_ = none; + return; + } + val_ = c_->get(); + } + + void increment_() + { + BOOST_ASSERT( c_); + BOOST_ASSERT( * c_); + + ( * c_)(); + fetch_(); + } + + public: + typedef typename iterator::pointer pointer_t; + typedef typename iterator::reference reference_t; + + iterator() : + c_( 0), val_() + {} + + explicit iterator( pull_coroutine< R & > * c) : + c_( c), val_() + { fetch_(); } + + iterator( iterator const& other) : + c_( other.c_), val_( other.val_) + {} + + iterator & operator=( iterator const& other) + { + if ( this == & other) return * this; + c_ = other.c_; + val_ = other.val_; + return * this; + } + + bool operator==( iterator const& other) + { return other.c_ == c_ && other.val_ == val_; } + + bool operator!=( iterator const& other) + { return other.c_ != c_ || other.val_ != val_; } + + iterator & operator++() + { + increment_(); + return * this; + } + + iterator operator++( int) + { + iterator tmp( * this); + ++*this; + return tmp; + } + + reference_t operator*() const + { return const_cast< optional< R & > & >( val_).get(); } + + pointer_t operator->() const + { return const_cast< optional< R & > & >( val_).get_ptr(); } + }; + + class const_iterator : public std::iterator< std::input_iterator_tag, R > + { + private: + pull_coroutine< R & > * c_; + optional< R & > val_; + + void fetch_() + { + BOOST_ASSERT( c_); + + if ( ! c_->has_result() ) + { + c_ = 0; + val_ = none; + return; + } + val_ = c_->get(); + } + + void increment_() + { + BOOST_ASSERT( c_); + BOOST_ASSERT( * c_); + + ( * c_)(); + fetch_(); + } + + public: + typedef typename const_iterator::pointer pointer_t; + typedef typename const_iterator::reference reference_t; + + const_iterator() : + c_( 0), val_() + {} + + explicit const_iterator( pull_coroutine< R & > const* c) : + c_( const_cast< pull_coroutine< R & > * >( c) ), val_() + { fetch_(); } + + const_iterator( const_iterator const& other) : + c_( other.c_), val_( other.val_) + {} + + const_iterator & operator=( const_iterator const& other) + { + if ( this == & other) return * this; + c_ = other.c_; + val_ = other.val_; + return * this; + } + + bool operator==( const_iterator const& other) + { return other.c_ == c_ && other.val_ == val_; } + + bool operator!=( const_iterator const& other) + { return other.c_ != c_ || other.val_ != val_; } + + const_iterator & operator++() + { + increment_(); + return * this; + } + + const_iterator operator++( int) + { + const_iterator tmp( * this); + ++*this; + return tmp; + } + + reference_t operator*() const + { return val_.get(); } + + pointer_t operator->() const + { return val_.get_ptr(); } + }; +}; + template<> class pull_coroutine< void > { @@ -1935,68 +2202,6 @@ push_coroutine< Arg & >::push_coroutine( coroutine_fn fn, attributes const& attr ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); } -template< typename Arg > -push_coroutine< Arg const& >::push_coroutine( coroutine_fn fn, attributes const& attr, - stack_allocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc, - typename disable_if< - is_same< typename decay< coroutine_fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, coroutine_fn, stack_allocator, std::allocator< push_coroutine >, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename StackAllocator > -push_coroutine< Arg const& >::push_coroutine( coroutine_fn fn, attributes const& attr, - StackAllocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc, - typename disable_if< - is_same< typename decay< coroutine_fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, coroutine_fn, StackAllocator, std::allocator< push_coroutine >, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename StackAllocator, typename Allocator > -push_coroutine< Arg const& >::push_coroutine( coroutine_fn fn, attributes const& attr, - StackAllocator const& stack_alloc, - Allocator const& alloc, - typename disable_if< - is_same< typename decay< coroutine_fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, coroutine_fn, StackAllocator, Allocator, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); -} - push_coroutine< void >::push_coroutine( coroutine_fn fn, attributes const& attr, stack_allocator const& stack_alloc, std::allocator< push_coroutine > const& alloc, @@ -2182,69 +2387,6 @@ push_coroutine< Arg & >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); } -template< typename Arg > -template< typename Fn > -push_coroutine< Arg const& >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - stack_allocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc, - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, stack_allocator, std::allocator< push_coroutine >, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename Fn, typename StackAllocator > -push_coroutine< Arg const& >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - StackAllocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc, - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, StackAllocator, std::allocator< push_coroutine >, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename Fn, typename StackAllocator, typename Allocator > -push_coroutine< Arg const& >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - StackAllocator const& stack_alloc, - Allocator const& alloc, - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, StackAllocator, Allocator, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); -} - template< typename Fn > push_coroutine< void >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, stack_allocator const& stack_alloc, @@ -2538,7 +2680,7 @@ push_coroutine< Arg & >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& template< typename Arg > template< typename Fn, typename StackAllocator, typename Allocator > -push_coroutine::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, +push_coroutine< Arg & >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, StackAllocator const& stack_alloc, Allocator const& alloc, typename disable_if< @@ -2557,132 +2699,6 @@ push_coroutine::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); } -template< typename Arg > -template< typename Fn > -push_coroutine< Arg const& >::push_coroutine( Fn fn, attributes const& attr, - stack_allocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc, - typename disable_if< - is_convertible< Fn &, BOOST_RV_REF( Fn) >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, stack_allocator, std::allocator< push_coroutine >, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename Fn, typename StackAllocator > -push_coroutine< Arg const& >::push_coroutine( Fn fn, attributes const& attr, - StackAllocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc, - typename disable_if< - is_convertible< Fn &, BOOST_RV_REF( Fn) >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, StackAllocator, std::allocator< push_coroutine >, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename Fn, typename StackAllocator, typename Allocator > -push_coroutine< Arg const& >::push_coroutine( Fn fn, attributes const& attr, - StackAllocator const& stack_alloc, - Allocator const& alloc, - typename disable_if< - is_convertible< Fn &, BOOST_RV_REF( Fn) >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, StackAllocator, Allocator, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename Fn > -push_coroutine< Arg const& >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - stack_allocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc, - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, stack_allocator, std::allocator< push_coroutine >, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename Fn, typename StackAllocator > -push_coroutine< Arg const& >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - StackAllocator const& stack_alloc, - std::allocator< push_coroutine > const& alloc, - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, StackAllocator, std::allocator< push_coroutine >, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); -} - -template< typename Arg > -template< typename Fn, typename StackAllocator, typename Allocator > -push_coroutine< Arg const& >::push_coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, - StackAllocator const& stack_alloc, - Allocator const& alloc, - typename disable_if< - is_same< typename decay< Fn >::type, push_coroutine >, - dummy * - >::type) : - impl_() -{ - typedef detail::push_coroutine_object< - Arg const&, Fn, StackAllocator, Allocator, - pull_coroutine< Arg const& > - > object_t; - typename object_t::allocator_t a( alloc); - impl_ = ptr_t( - // placement new - ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); -} - template< typename Fn > push_coroutine< void >::push_coroutine( Fn fn, attributes const& attr, stack_allocator const& stack_alloc, @@ -2836,30 +2852,6 @@ typename pull_coroutine< R >::const_iterator range_end( pull_coroutine< R > const&) { return typename pull_coroutine< R >::const_iterator(); } -template< typename R > -inline -typename pull_coroutine< R >::iterator -begin( pull_coroutine< R > & c) -{ return boost::begin( c); } - -template< typename R > -inline -typename pull_coroutine< R >::iterator -end( pull_coroutine< R > & c) -{ return boost::end( c); } - -template< typename R > -inline -typename pull_coroutine< R >::const_iterator -begin( pull_coroutine< R > const& c) -{ return boost::const_begin( c); } - -template< typename R > -inline -typename pull_coroutine< R >::const_iterator -end( pull_coroutine< R > const& c) -{ return boost::const_end( c); } - template< typename Arg > inline typename push_coroutine< Arg >::iterator @@ -2884,31 +2876,6 @@ typename push_coroutine< Arg >::const_iterator range_end( push_coroutine< Arg > const&) { return typename push_coroutine< Arg >::const_iterator(); } -template< typename Arg > -inline -typename push_coroutine< Arg >::iterator -begin( push_coroutine< Arg > & c) -{ return boost::begin( c); } - -template< typename Arg > -inline -typename push_coroutine< Arg >::iterator -end( push_coroutine< Arg > & c) -{ return boost::end( c); } - -template< typename Arg > -inline -typename push_coroutine< Arg >::const_iterator -begin( push_coroutine< Arg > const& c) -{ return boost::const_begin( c); } - -template< typename Arg > -inline -typename push_coroutine< Arg >::const_iterator -end( push_coroutine< Arg > const& c) -{ return boost::const_end( c); } - - template< typename T > struct coroutine { @@ -2936,6 +2903,58 @@ struct range_const_iterator< coroutines::pull_coroutine< R > > } +namespace std { + +template< typename R > +inline +typename boost::coroutines::pull_coroutine< R >::iterator +begin( boost::coroutines::pull_coroutine< R > & c) +{ return boost::begin( c); } + +template< typename R > +inline +typename boost::coroutines::pull_coroutine< R >::iterator +end( boost::coroutines::pull_coroutine< R > & c) +{ return boost::end( c); } + +template< typename R > +inline +typename boost::coroutines::pull_coroutine< R >::const_iterator +begin( boost::coroutines::pull_coroutine< R > const& c) +{ return boost::const_begin( c); } + +template< typename R > +inline +typename boost::coroutines::pull_coroutine< R >::const_iterator +end( boost::coroutines::pull_coroutine< R > const& c) +{ return boost::const_end( c); } + +template< typename R > +inline +typename boost::coroutines::push_coroutine< R >::iterator +begin( boost::coroutines::push_coroutine< R > & c) +{ return boost::begin( c); } + +template< typename R > +inline +typename boost::coroutines::push_coroutine< R >::iterator +end( boost::coroutines::push_coroutine< R > & c) +{ return boost::end( c); } + +template< typename R > +inline +typename boost::coroutines::push_coroutine< R >::const_iterator +begin( boost::coroutines::push_coroutine< R > const& c) +{ return boost::const_begin( c); } + +template< typename R > +inline +typename boost::coroutines::push_coroutine< R >::const_iterator +end( boost::coroutines::push_coroutine< R > const& c) +{ return boost::const_end( c); } + +} + #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif diff --git a/include/boost/coroutine/v2/detail/pull_coroutine_base.hpp b/include/boost/coroutine/v2/detail/pull_coroutine_base.hpp index 5c94175..b5d049d 100644 --- a/include/boost/coroutine/v2/detail/pull_coroutine_base.hpp +++ b/include/boost/coroutine/v2/detail/pull_coroutine_base.hpp @@ -136,6 +136,108 @@ public: } }; +template< typename R > +class pull_coroutine_base< R & > : private noncopyable +{ +public: + typedef intrusive_ptr< pull_coroutine_base > ptr_t; + +private: + template< + typename X, typename Y, typename Z, typename V, typename W + > + friend class push_coroutine_object; + + unsigned int use_count_; + +protected: + int flags_; + exception_ptr except_; + coroutine_context caller_; + coroutine_context callee_; + optional< R * > result_; + + virtual void deallocate_object() = 0; + +public: + pull_coroutine_base( coroutine_context::ctx_fn fn, + stack_context * stack_ctx, + bool unwind, bool preserve_fpu) : + use_count_( 0), + flags_( 0), + except_(), + caller_(), + callee_( fn, stack_ctx), + result_() + { + if ( unwind) flags_ |= flag_force_unwind; + if ( preserve_fpu) flags_ |= flag_preserve_fpu; + } + + pull_coroutine_base( coroutine_context const& callee, + bool unwind, bool preserve_fpu, + optional< R * > const& result) : + use_count_( 0), + flags_( 0), + except_(), + caller_(), + callee_( callee), + result_( result) + { + if ( unwind) flags_ |= flag_force_unwind; + if ( preserve_fpu) flags_ |= flag_preserve_fpu; + } + + virtual ~pull_coroutine_base() + {} + + bool force_unwind() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_force_unwind); } + + bool unwind_requested() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_unwind_stack); } + + bool preserve_fpu() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_preserve_fpu); } + + bool is_complete() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_complete); } + + friend inline void intrusive_ptr_add_ref( pull_coroutine_base * p) BOOST_NOEXCEPT + { ++p->use_count_; } + + friend inline void intrusive_ptr_release( pull_coroutine_base * p) BOOST_NOEXCEPT + { if ( --p->use_count_ == 0) p->deallocate_object(); } + + void pull() + { + BOOST_ASSERT( ! is_complete() ); + + holder< R & > hldr_to( & caller_); + holder< R & > * hldr_from( + reinterpret_cast< holder< R & > * >( + hldr_to.ctx->jump( + callee_, + reinterpret_cast< intptr_t >( & hldr_to), + preserve_fpu() ) ) ); + BOOST_ASSERT( hldr_from->ctx); + callee_ = * hldr_from->ctx; + result_ = hldr_from->data; + if ( hldr_from->force_unwind) throw forced_unwind(); + if ( except_) rethrow_exception( except_); + } + + bool has_result() const + { return result_; } + + R & get() const + { + BOOST_ASSERT( has_result() ); + + return * result_.get(); + } +}; + template<> class pull_coroutine_base< void > : private noncopyable { diff --git a/include/boost/coroutine/v2/detail/pull_coroutine_caller.hpp b/include/boost/coroutine/v2/detail/pull_coroutine_caller.hpp index 6be6226..0e9cf44 100644 --- a/include/boost/coroutine/v2/detail/pull_coroutine_caller.hpp +++ b/include/boost/coroutine/v2/detail/pull_coroutine_caller.hpp @@ -48,6 +48,33 @@ private: } }; +template< typename R, typename Allocator > +class pull_coroutine_caller< R &, Allocator > : public pull_coroutine_base< R & > +{ +public: + typedef typename Allocator::template rebind< + pull_coroutine_caller< R &, Allocator > + >::other allocator_t; + + pull_coroutine_caller( coroutine_context const& callee, bool unwind, bool preserve_fpu, + allocator_t const& alloc, optional< R * > const& data) BOOST_NOEXCEPT : + pull_coroutine_base< R & >( callee, unwind, preserve_fpu, data), + alloc_( alloc) + {} + + void deallocate_object() + { destroy_( alloc_, this); } + +private: + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, pull_coroutine_caller * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } +}; + template< typename Allocator > class pull_coroutine_caller< void, Allocator > : public pull_coroutine_base< void > { diff --git a/include/boost/coroutine/v2/detail/pull_coroutine_object.hpp b/include/boost/coroutine/v2/detail/pull_coroutine_object.hpp index c30d829..08d0b76 100644 --- a/include/boost/coroutine/v2/detail/pull_coroutine_object.hpp +++ b/include/boost/coroutine/v2/detail/pull_coroutine_object.hpp @@ -399,6 +399,362 @@ public: { destroy_( alloc_, this); } }; +template< + typename R, typename Fn, + typename StackAllocator, typename Allocator, + typename Caller +> +class pull_coroutine_object< R &, Fn, StackAllocator, Allocator, Caller > : + private stack_tuple< StackAllocator >, + public pull_coroutine_base< R & > +{ +public: + typedef typename Allocator::template rebind< + pull_coroutine_object< + R &, Fn, StackAllocator, Allocator, Caller + > + >::other allocator_t; + +private: + typedef stack_tuple< StackAllocator > pbase_type; + typedef pull_coroutine_base< R & > base_type; + + Fn fn_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, pull_coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + pull_coroutine_object( pull_coroutine_object &); + pull_coroutine_object & operator=( pull_coroutine_object const&); + + void enter_() + { + holder< R * > * hldr_from( + reinterpret_cast< holder< R * > * >( + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( this), + this->preserve_fpu() ) ) ); + this->callee_ = * hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< R * > hldr_to( & this->caller_, true); + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + pull_coroutine_object( Fn && fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< pull_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + alloc_( alloc) + { enter_(); } +#else + pull_coroutine_object( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< pull_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + alloc_( alloc) + { enter_(); } + + pull_coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< pull_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + alloc_( alloc) + { enter_(); } +#endif + + ~pull_coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) + unwind_stack_(); + } + + void run() + { + coroutine_context callee; + coroutine_context caller; + + { + // create push_coroutine + Caller c( this->caller_, false, this->preserve_fpu(), alloc_); + try + { fn_( c); } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + callee = c.impl_->callee_; + } + + this->flags_ |= flag_complete; + holder< R * > hldr_to( & caller); + caller.jump( + callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename R, typename Fn, + typename StackAllocator, typename Allocator, + typename Caller +> +class pull_coroutine_object< R &, reference_wrapper< Fn >, StackAllocator, Allocator, Caller > : + private stack_tuple< StackAllocator >, + public pull_coroutine_base< R & > +{ +public: + typedef typename Allocator::template rebind< + pull_coroutine_object< + R &, Fn, StackAllocator, Allocator, Caller + > + >::other allocator_t; + +private: + typedef stack_tuple< StackAllocator > pbase_type; + typedef pull_coroutine_base< R & > base_type; + + Fn fn_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, pull_coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + pull_coroutine_object( pull_coroutine_object &); + pull_coroutine_object & operator=( pull_coroutine_object const&); + + void enter_() + { + holder< R * > * hldr_from( + reinterpret_cast< holder< R * > * >( + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( this), + this->preserve_fpu() ) ) ); + this->callee_ = * hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< R * > hldr_to( & this->caller_, true); + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + pull_coroutine_object( reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< pull_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + alloc_( alloc) + { enter_(); } + + ~pull_coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) + unwind_stack_(); + } + + void run() + { + coroutine_context callee; + coroutine_context caller; + + { + // create pull_coroutine + Caller c( this->caller_, false, this->preserve_fpu(), alloc_); + try + { fn_( c); } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + callee = c.impl_->callee_; + } + + this->flags_ |= flag_complete; + holder< R * > hldr_to( & caller); + caller.jump( + callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename R, typename Fn, + typename StackAllocator, typename Allocator, + typename Caller +> +class pull_coroutine_object< R &, const reference_wrapper< Fn >, StackAllocator, Allocator, Caller > : + private stack_tuple< StackAllocator >, + public pull_coroutine_base< R & > +{ +public: + typedef typename Allocator::template rebind< + pull_coroutine_object< + R &, Fn, StackAllocator, Allocator, Caller + > + >::other allocator_t; + +private: + typedef stack_tuple< StackAllocator > pbase_type; + typedef pull_coroutine_base< R & > base_type; + + Fn fn_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, pull_coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + pull_coroutine_object( pull_coroutine_object &); + pull_coroutine_object & operator=( pull_coroutine_object const&); + + void enter_() + { + holder< R * > * hldr_from( + reinterpret_cast< holder< R * > * >( + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( this), + this->preserve_fpu() ) ) ); + this->callee_ = * hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< R * > hldr_to( & this->caller_, true); + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + pull_coroutine_object( const reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< pull_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + alloc_( alloc) + { enter_(); } + + ~pull_coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) + unwind_stack_(); + } + + void run() + { + coroutine_context callee; + coroutine_context caller; + + { + // create pull_coroutine + Caller c( this->caller_, false, this->preserve_fpu(), alloc_); + try + { fn_( c); } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + callee = c.impl_->callee_; + } + + this->flags_ |= flag_complete; + holder< R * > hldr_to( & caller); + caller.jump( + callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + template< typename Fn, typename StackAllocator, typename Allocator, diff --git a/include/boost/coroutine/v2/detail/push_coroutine_base.hpp b/include/boost/coroutine/v2/detail/push_coroutine_base.hpp index 24e2fab..595a167 100644 --- a/include/boost/coroutine/v2/detail/push_coroutine_base.hpp +++ b/include/boost/coroutine/v2/detail/push_coroutine_base.hpp @@ -122,7 +122,7 @@ public: { BOOST_ASSERT( ! is_complete() ); - holder< Arg > hldr_to( & caller_, arg); + holder< Arg > hldr_to( & caller_, boost::forward( arg) ); holder< Arg > * hldr_from( reinterpret_cast< holder< Arg > * >( hldr_to.ctx->jump( @@ -135,7 +135,7 @@ public: if ( except_) rethrow_exception( except_); } #else - void push( Arg arg) + void push( Arg const& arg) { BOOST_ASSERT( ! is_complete() ); @@ -244,96 +244,9 @@ public: { BOOST_ASSERT( ! is_complete() ); - holder< Arg & > hldr_to( & caller_, arg); - holder< Arg & > * hldr_from( - reinterpret_cast< holder< Arg & > * >( - hldr_to.ctx->jump( - callee_, - reinterpret_cast< intptr_t >( & hldr_to), - preserve_fpu() ) ) ); - BOOST_ASSERT( hldr_from->ctx); - callee_ = * hldr_from->ctx; - if ( hldr_from->force_unwind) throw forced_unwind(); - if ( except_) rethrow_exception( except_); - } -}; - -template< typename Arg > -class push_coroutine_base< Arg const& > : private noncopyable -{ -public: - typedef intrusive_ptr< push_coroutine_base > ptr_t; - -private: - template< - typename X, typename Y, typename Z, typename V, typename W - > - friend class pull_coroutine_object; - - unsigned int use_count_; - -protected: - int flags_; - exception_ptr except_; - coroutine_context caller_; - coroutine_context callee_; - - virtual void deallocate_object() = 0; - -public: - push_coroutine_base( coroutine_context::ctx_fn fn, - stack_context * stack_ctx, - bool unwind, bool preserve_fpu) : - use_count_( 0), - flags_( 0), - except_(), - caller_(), - callee_( fn, stack_ctx) - { - if ( unwind) flags_ |= flag_force_unwind; - if ( preserve_fpu) flags_ |= flag_preserve_fpu; - } - - push_coroutine_base( coroutine_context const& callee, - bool unwind, bool preserve_fpu) : - use_count_( 0), - flags_( 0), - except_(), - caller_(), - callee_( callee) - { - if ( unwind) flags_ |= flag_force_unwind; - if ( preserve_fpu) flags_ |= flag_preserve_fpu; - } - - virtual ~push_coroutine_base() - {} - - bool force_unwind() const BOOST_NOEXCEPT - { return 0 != ( flags_ & flag_force_unwind); } - - bool unwind_requested() const BOOST_NOEXCEPT - { return 0 != ( flags_ & flag_unwind_stack); } - - bool preserve_fpu() const BOOST_NOEXCEPT - { return 0 != ( flags_ & flag_preserve_fpu); } - - bool is_complete() const BOOST_NOEXCEPT - { return 0 != ( flags_ & flag_complete); } - - friend inline void intrusive_ptr_add_ref( push_coroutine_base * p) BOOST_NOEXCEPT - { ++p->use_count_; } - - friend inline void intrusive_ptr_release( push_coroutine_base * p) BOOST_NOEXCEPT - { if ( --p->use_count_ == 0) p->deallocate_object(); } - - void push( Arg const& arg) - { - BOOST_ASSERT( ! is_complete() ); - - holder< Arg const& > hldr_to( & caller_, arg); - holder< Arg const& > * hldr_from( - reinterpret_cast< holder< Arg const& > * >( + holder< Arg * > hldr_to( & caller_, & arg); + holder< Arg * > * hldr_from( + reinterpret_cast< holder< Arg * > * >( hldr_to.ctx->jump( callee_, reinterpret_cast< intptr_t >( & hldr_to), diff --git a/include/boost/coroutine/v2/detail/push_coroutine_object.hpp b/include/boost/coroutine/v2/detail/push_coroutine_object.hpp index 08db5a1..52a7569 100644 --- a/include/boost/coroutine/v2/detail/push_coroutine_object.hpp +++ b/include/boost/coroutine/v2/detail/push_coroutine_object.hpp @@ -428,6 +428,391 @@ public: { destroy_( alloc_, this); } }; +template< + typename Arg, typename Fn, + typename StackAllocator, typename Allocator, + typename Caller +> +class push_coroutine_object< Arg &, Fn, StackAllocator, Allocator, Caller > : + private stack_tuple< StackAllocator >, + public push_coroutine_base< Arg & > +{ +public: + typedef typename Allocator::template rebind< + push_coroutine_object< + Arg &, Fn, StackAllocator, Allocator, Caller + > + >::other allocator_t; + +private: + typedef stack_tuple< StackAllocator > pbase_type; + typedef push_coroutine_base< Arg & > base_type; + + Fn fn_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, push_coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + push_coroutine_object( push_coroutine_object &); + push_coroutine_object & operator=( push_coroutine_object const&); + + void enter_() + { + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( this), + this->preserve_fpu() ) ) ); + this->callee_ = * hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< Arg * > hldr_to( & this->caller_, true); + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + push_coroutine_object( Fn && fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< push_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + alloc_( alloc) + { enter_(); } +#else + push_coroutine_object( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< push_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + alloc_( alloc) + { enter_(); } + + push_coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< push_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + alloc_( alloc) + { enter_(); } +#endif + + ~push_coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) + unwind_stack_(); + } + + void run() + { + coroutine_context callee; + coroutine_context caller; + + { + holder< void > hldr_to( & caller); + holder< Arg * > * hldr_from( + reinterpret_cast< holder< Arg * > * >( + caller.jump( + this->caller_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + BOOST_ASSERT( hldr_from->ctx); + BOOST_ASSERT( hldr_from->data); + + // create pull_coroutine + Caller c( * hldr_from->ctx, false, this->preserve_fpu(), alloc_, hldr_from->data); + try + { fn_( c); } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + callee = c.impl_->callee_; + } + + this->flags_ |= flag_complete; + holder< Arg * > hldr_to( & caller); + caller.jump( + callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "push_coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Arg, typename Fn, + typename StackAllocator, typename Allocator, + typename Caller +> +class push_coroutine_object< Arg &, reference_wrapper< Fn >, StackAllocator, Allocator, Caller > : + private stack_tuple< StackAllocator >, + public push_coroutine_base< Arg & > +{ +public: + typedef typename Allocator::template rebind< + push_coroutine_object< + Arg &, Fn, StackAllocator, Allocator, Caller + > + >::other allocator_t; + +private: + typedef stack_tuple< StackAllocator > pbase_type; + typedef push_coroutine_base< Arg & > base_type; + + Fn fn_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, push_coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + push_coroutine_object( push_coroutine_object &); + push_coroutine_object & operator=( push_coroutine_object const&); + + void enter_() + { + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( this), + this->preserve_fpu() ) ) ); + this->callee_ = * hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< Arg * > hldr_to( & this->caller_, true); + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + push_coroutine_object( reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< push_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + alloc_( alloc) + { enter_(); } + + ~push_coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) + unwind_stack_(); + } + + void run() + { + coroutine_context callee; + coroutine_context caller; + + { + holder< void > hldr_to( & caller); + holder< Arg * > * hldr_from( + reinterpret_cast< holder< Arg * > * >( + caller.jump( + this->caller_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + BOOST_ASSERT( hldr_from->ctx); + BOOST_ASSERT( hldr_from->data); + + // create pull_coroutine + Caller c( * hldr_from->ctx, false, this->preserve_fpu(), alloc_, hldr_from->data); + try + { fn_( c); } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + callee = c.impl_->callee_; + } + + this->flags_ |= flag_complete; + holder< Arg * > hldr_to( & caller); + caller.jump( + callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "push_coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Arg, typename Fn, + typename StackAllocator, typename Allocator, + typename Caller +> +class push_coroutine_object< Arg &, const reference_wrapper< Fn >, StackAllocator, Allocator, Caller > : + private stack_tuple< StackAllocator >, + public push_coroutine_base< Arg & > +{ +public: + typedef typename Allocator::template rebind< + push_coroutine_object< + Arg, Fn, StackAllocator, Allocator, Caller + > + >::other allocator_t; + +private: + typedef stack_tuple< StackAllocator > pbase_type; + typedef push_coroutine_base< Arg & > base_type; + + Fn fn_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, push_coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + push_coroutine_object( push_coroutine_object &); + push_coroutine_object & operator=( push_coroutine_object const&); + + void enter_() + { + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( this), + this->preserve_fpu() ) ) ); + this->callee_ = * hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< Arg * > hldr_to( & this->caller_, true); + this->caller_.jump( + this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + push_coroutine_object( const reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + pbase_type( stack_alloc, attr.size), + base_type( + trampoline1< push_coroutine_object >, + & this->stack_ctx, + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + alloc_( alloc) + { enter_(); } + + ~push_coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) + unwind_stack_(); + } + + void run() + { + coroutine_context callee; + coroutine_context caller; + + { + holder< void > hldr_to( & caller); + holder< Arg * > * hldr_from( + reinterpret_cast< holder< Arg * > * >( + caller.jump( + this->caller_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + BOOST_ASSERT( hldr_from->ctx); + BOOST_ASSERT( hldr_from->data); + + // create pull_coroutine + Caller c( * hldr_from->ctx, false, this->preserve_fpu(), alloc_, hldr_from->data); + try + { fn_( c); } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + callee = c.impl_->callee_; + } + + this->flags_ |= flag_complete; + holder< Arg * > hldr_to( & caller); + caller.jump( + callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "push_coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + template< typename Fn, typename StackAllocator, typename Allocator,