coroutine: update of interface

[SVN r85105]
This commit is contained in:
Oliver Kowalke
2013-07-22 15:03:42 +00:00
parent 865902f9b5
commit 01235d2ee7
14 changed files with 2037 additions and 802 deletions

View File

@@ -68,3 +68,11 @@ exe unwind
exe same_fringe
: same_fringe.cpp
;
exe layout
: layout.cpp
;
exe chaining
: chaining.cpp
;

203
example/cpp03/chaining.cpp Normal file
View File

@@ -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 <iostream>
#include <iomanip>
#include <string>
#include <cctype>
#include <sstream>
#include <boost/bind.hpp>
#include <boost/coroutine/all.hpp>
#include <boost/foreach.hpp>
typedef boost::coroutines::coroutine<std::string> 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;
}

71
example/cpp03/layout.cpp Normal file
View File

@@ -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 <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <utility>
#include <boost/assign/list_of.hpp>
#include <boost/bind.hpp>
#include <boost/coroutine/all.hpp>
#include <boost/range.hpp>
struct FinalEOL
{
~FinalEOL() { std::cout << std::endl; }
};
void layout(boost::coroutines::coroutine<std::string>::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<std::string> words = boost::assign::list_of
("peas")
("porridge")
("hot")
("peas")
("porridge")
("cold")
("peas")
("porridge")
("in")
("the")
("pot")
("nine")
("days")
("old")
;
boost::coroutines::coroutine<std::string>::push_type writer(
boost::bind(layout, _1, 5, 15));
std::copy(boost::begin(words), boost::end(words), boost::begin(writer));
return 0;
}

View File

@@ -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 <cstddef>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <string>
#include <utility>
#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/coroutine/all.hpp>
#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<std::string>::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<std::string>::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::string>(std::cout, " "));
std::cout << std::endl;
node::ptr_t right_b(create_right_tree_from("b"));
boost::coroutines::coroutine<std::string>::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::string>(std::cout, " "));
std::cout << std::endl;
node::ptr_t right_x(create_right_tree_from("x"));
boost::coroutines::coroutine<std::string>::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::string>(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<std::string>::pull_type left_d_reader(
boost::bind(traverse, left_d, _1));
node::ptr_t right_b(create_right_tree_from("b"));
boost::coroutines::coroutine<std::string>::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<std::string>::pull_type left_d_reader(
boost::bind(traverse, left_d, _1));
node::ptr_t right_x(create_right_tree_from("x"));
boost::coroutines::coroutine<std::string>::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