// Copyright Oliver Kowalke 2014. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include namespace coro = boost::coroutines2; int value1 = 0; std::string value2 = ""; bool value3 = false; double value4 = .0; int * value5 = nullptr; int& value6 = value1; int& value7 = value1; int value8 = 0; int value9 = 0; struct X { X() { value1 = 7; } ~X() { value1 = 0; } X( X const&) = delete; X & operator=( X const&) = delete; }; class copyable { public: bool state; copyable() : state( false) {} copyable( int) : state( true) {} void operator()( coro::coroutine< int >::push_type &) { value3 = state; } }; class moveable { public: bool state; moveable() : state( false) {} moveable( int) : state( true) {} moveable( moveable const&) = delete; moveable & operator=( moveable const&) = delete; moveable( moveable && other) : state( false) { std::swap( state, other.state); } moveable & operator=( moveable && other) { if ( this != & other) { state = other.state; other.state = false; } return * this; } void operator()( coro::coroutine< int >::push_type &) { value3 = state; } }; class movedata { public: int i; movedata( int i_) : i( i_) {} movedata( movedata const&) = delete; movedata & operator=( movedata const&) = delete; movedata( movedata && other) : i( 0) { std::swap( i, other.i); } movedata & operator=( movedata && other) { if ( this != & other) { i = other.i; other.i = 0; } return * this; } }; struct my_exception {}; void f1( coro::coroutine< void >::push_type & c) { while ( c) c(); } void f2( coro::coroutine< void >::push_type &) { ++value1; } void f3( coro::coroutine< void >::push_type & c) { ++value1; c(); ++value1; } void f4( coro::coroutine< int >::push_type & c) { c( 3); c( 7); } void f5( coro::coroutine< std::string >::push_type & c) { std::string res("abc"); c( res); res = "xyz"; c( res); } void f6( coro::coroutine< int >::pull_type & c) { value1 = c.get(); } void f7( coro::coroutine< std::string >::pull_type & c) { value2 = c.get(); } void f8( coro::coroutine< std::tuple< double, double > >::pull_type & c) { double x = 0, y = 0; std::tie( x, y) = c.get(); value4 = x + y; c(); std::tie( x, y) = c.get(); value4 = x + y; } void f9( coro::coroutine< int * >::pull_type & c) { value5 = c.get(); } void f91( coro::coroutine< int const* >::pull_type & c) { value5 = const_cast< int * >( c.get() ); } void f10( coro::coroutine< int & >::pull_type & c) { int & i = c.get(); value5 = const_cast< int * >( & i); } void f101( coro::coroutine< int const& >::pull_type & c) { int const& i = c.get(); value5 = const_cast< int * >( & i); } void f11( coro::coroutine< std::tuple< int, int > >::pull_type & c) { std::tie( value8, value9) = c.get(); } void f12( coro::coroutine< void >::pull_type & c) { value1 = 7; X x_; c(); c(); } void f16( coro::coroutine< int >::push_type & c) { c( 1); c( 2); c( 3); c( 4); c( 5); } void f17( coro::coroutine< int >::pull_type & c, std::vector< int > & vec) { int x = c.get(); while ( 5 > x) { vec.push_back( x); x = c().get(); } } void f20( coro::coroutine< int >::push_type &) {} void f21( coro::coroutine< int >::pull_type & c) { while ( c) { value1 = c.get(); c(); } } void f22( coro::coroutine< movedata >::pull_type & c) { movedata mv( c.get() ); value1 = mv.i; } void test_move() { { coro::coroutine< int >::pull_type coro1( f20); coro::coroutine< int >::pull_type coro2( f16); BOOST_CHECK( ! coro1); BOOST_CHECK( coro2); BOOST_CHECK_EQUAL( 1, coro2.get() ); coro2(); BOOST_CHECK_EQUAL( 2, coro2.get() ); coro1 = std::move( coro2); BOOST_CHECK( coro1); BOOST_CHECK( ! coro2); coro1(); BOOST_CHECK_EQUAL( 3, coro1.get() ); BOOST_CHECK( ! coro2); } { value3 = false; copyable cp( 3); BOOST_CHECK( cp.state); BOOST_CHECK( ! value3); coro::coroutine< int >::pull_type coro( cp); BOOST_CHECK( cp.state); BOOST_CHECK( value3); } { value3 = false; moveable mv( 7); BOOST_CHECK( mv.state); BOOST_CHECK( ! value3); coro::coroutine< int >::pull_type coro( std::move( mv) ); BOOST_CHECK( ! mv.state); BOOST_CHECK( value3); } { value1 = 0; movedata mv( 7); BOOST_CHECK_EQUAL( 0, value1); BOOST_CHECK_EQUAL( 7, mv.i); coro::coroutine< movedata >::push_type coro( f22); coro( std::move( mv) ); BOOST_CHECK_EQUAL( 7, value1); BOOST_CHECK_EQUAL( 0, mv.i); } } void test_complete() { value1 = 0; coro::coroutine< void >::pull_type coro( f2); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( ( int)1, value1); } void test_bind() { value1 = 0; coro::coroutine< void >::pull_type coro( std::bind( f2, std::placeholders::_1) ); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( ( int)1, value1); } void test_jump() { value1 = 0; coro::coroutine< void >::pull_type coro( f3); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( ( int)1, value1); coro(); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( ( int)2, value1); } void test_result_int() { coro::coroutine< int >::pull_type coro( f4); BOOST_CHECK( coro); int result = coro.get(); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( 3, result); result = coro().get(); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( 7, result); coro(); BOOST_CHECK( ! coro); } void test_result_string() { coro::coroutine< std::string >::pull_type coro( f5); BOOST_CHECK( coro); std::string result = coro.get(); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( std::string("abc"), result); result = coro().get(); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( std::string("xyz"), result); coro(); BOOST_CHECK( ! coro); } void test_arg_int() { value1 = 0; coro::coroutine< int >::push_type coro( f6); BOOST_CHECK( coro); coro( 3); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( 3, value1); } void test_arg_string() { value2 = ""; coro::coroutine< std::string >::push_type coro( f7); BOOST_CHECK( coro); coro( std::string("abc") ); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( std::string("abc"), value2); } void test_fp() { value4 = 0; coro::coroutine< std::tuple< double, double > >::push_type coro( f8); BOOST_CHECK( coro); coro( std::make_tuple( 7.35, 3.14) ); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( ( double) 10.49, value4); value4 = 0; coro( std::make_tuple( 1.15, 3.14) ); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( ( double) 4.29, value4); } void test_ptr() { value5 = nullptr; int a = 3; coro::coroutine< int * >::push_type coro( f9); BOOST_CHECK( coro); coro( & a); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( & a, value5); } void test_const_ptr() { value5 = nullptr; int a = 3; coro::coroutine< int const* >::push_type coro( f91); BOOST_CHECK( coro); coro( & a); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( & a, value5); } void test_ref() { value5 = nullptr; int a_ = 3; int & a = a_; coro::coroutine< int & >::push_type coro( f10); BOOST_CHECK( coro); coro( std::ref( a) ); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( & a, value5); } void test_const_ref() { value5 = nullptr; int a = 3; coro::coroutine< int const& >::push_type coro( f101); BOOST_CHECK( coro); coro( a); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( & a, value5); } void test_no_result() { coro::coroutine< int >::pull_type coro( f20); BOOST_CHECK( ! coro); } void test_tuple() { value8 = 0; value9 = 0; int a = 3, b = 7; std::tuple< int, int > tpl( a, b); BOOST_CHECK_EQUAL( a, std::get< 0 >( tpl) ); BOOST_CHECK_EQUAL( b, std::get< 1 >( tpl) ); coro::coroutine< std::tuple< int, int > >::push_type coro( f11); BOOST_CHECK( coro); coro( tpl); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( a, value8); BOOST_CHECK_EQUAL( b, value9); } void test_unwind() { value1 = 0; { coro::coroutine< void >::push_type coro( f12); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( ( int) 0, value1); coro(); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( ( int) 7, value1); coro(); BOOST_CHECK_EQUAL( ( int) 7, value1); } BOOST_CHECK_EQUAL( ( int) 0, value1); int i = 0; { coro::coroutine< void >::push_type coro( [&i](coro::coroutine< void >::pull_type &) mutable { i = 7; }); } { BOOST_CHECK_EQUAL( ( int) 0, value1); auto * coro = new coro::coroutine< void >::pull_type( [](coro::coroutine< void >::push_type & coro) mutable { X x; coro(); }); BOOST_CHECK_EQUAL( ( int) 7, value1); delete coro; BOOST_CHECK_EQUAL( ( int) 0, value1); } { BOOST_CHECK_EQUAL( ( int) 0, value1); auto * coro = new coro::coroutine< void >::push_type( [](coro::coroutine< void >::pull_type & coro) mutable { X x; coro(); }); ( * coro)(); BOOST_CHECK_EQUAL( ( int) 7, value1); delete coro; BOOST_CHECK_EQUAL( ( int) 0, value1); } } void test_exceptions() { std::string msg("abc"), value; std::runtime_error ex( msg); try { coro::coroutine< void >::push_type coro( [&msg]( coro::coroutine< void >::pull_type &) { throw std::runtime_error( msg); }); BOOST_CHECK( coro); coro(); BOOST_CHECK( ! coro); BOOST_CHECK( false); } catch ( std::runtime_error const& ex) { value = ex.what(); } BOOST_CHECK_EQUAL( value, msg); } void test_input_iterator() { { using std::begin; using std::end; std::vector< int > vec; coro::coroutine< int >::pull_type coro( f16); coro::coroutine< int >::pull_type::iterator e = end( coro); for ( coro::coroutine< int >::pull_type::iterator i = begin( coro); i != e; ++i) { vec.push_back( * i); } BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() ); BOOST_CHECK_EQUAL( ( int)1, vec[0] ); BOOST_CHECK_EQUAL( ( int)2, vec[1] ); BOOST_CHECK_EQUAL( ( int)3, vec[2] ); BOOST_CHECK_EQUAL( ( int)4, vec[3] ); BOOST_CHECK_EQUAL( ( int)5, vec[4] ); } { std::vector< int > vec; coro::coroutine< int >::pull_type coro( f16); for ( auto i : coro) { vec.push_back( i); } BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() ); BOOST_CHECK_EQUAL( ( int)1, vec[0] ); BOOST_CHECK_EQUAL( ( int)2, vec[1] ); BOOST_CHECK_EQUAL( ( int)3, vec[2] ); BOOST_CHECK_EQUAL( ( int)4, vec[3] ); BOOST_CHECK_EQUAL( ( int)5, vec[4] ); } { int i1 = 1, i2 = 2, i3 = 3; coro::coroutine< int& >::pull_type coro( [&i1,&i2,&i3](coro::coroutine< int& >::push_type & c){ c( i1); c( i2); c( i3); }); int counter = 1; for ( int & i : coro) { switch ( counter) { case 1: BOOST_CHECK_EQUAL( & i1, & i); break; case 2: BOOST_CHECK_EQUAL( & i2, & i); break; case 3: BOOST_CHECK_EQUAL( & i3, & i); break; default: BOOST_ASSERT( false); } ++counter; } } } void test_output_iterator() { using std::begin; using std::end; int counter = 0; std::vector< int > vec; coro::coroutine< int >::push_type coro( [&vec]( coro::coroutine< int >::pull_type & c) { int x = c.get(); while ( 5 > x) { vec.push_back( x); x = c().get(); } }); coro::coroutine< int >::push_type::iterator e( end( coro) ); for ( coro::coroutine< int >::push_type::iterator i( begin( coro) ); i != e; ++i) { i = ++counter; } BOOST_CHECK_EQUAL( ( std::size_t)4, vec.size() ); BOOST_CHECK_EQUAL( ( int)1, vec[0] ); BOOST_CHECK_EQUAL( ( int)2, vec[1] ); BOOST_CHECK_EQUAL( ( int)3, vec[2] ); BOOST_CHECK_EQUAL( ( int)4, vec[3] ); } std::vector< int > vec; coro::coroutine< void >::pull_type * child = nullptr; void start_child_coroutine() { child = new coro::coroutine< void >::pull_type([](coro::coroutine< void >::push_type & yield) { vec.push_back( 2); yield(); vec.push_back( 2); yield(); vec.push_back( 2); yield(); vec.push_back( 2); yield(); vec.push_back( 2); yield(); vec.push_back( 2); }); } coro::coroutine< void >::pull_type start_parent_coroutine() { return coro::coroutine< void >::pull_type([=](coro::coroutine< void >::push_type & yield) { vec.push_back( 1); start_child_coroutine(); yield(); vec.push_back( 1); }); } void test_chaining() { auto parent = start_parent_coroutine(); while ( * child) { ( * child)(); } BOOST_CHECK_EQUAL( 7, vec.size() ); BOOST_CHECK_EQUAL( 1, vec[0]); BOOST_CHECK_EQUAL( 2, vec[1]); BOOST_CHECK_EQUAL( 2, vec[2]); BOOST_CHECK_EQUAL( 2, vec[3]); BOOST_CHECK_EQUAL( 2, vec[4]); BOOST_CHECK_EQUAL( 2, vec[5]); BOOST_CHECK_EQUAL( 2, vec[6]); } boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { boost::unit_test::test_suite * test = BOOST_TEST_SUITE("Boost.Coroutine2: coroutine test suite"); test->add( BOOST_TEST_CASE( & test_move) ); test->add( BOOST_TEST_CASE( & test_complete) ); test->add( BOOST_TEST_CASE( & test_bind) ); test->add( BOOST_TEST_CASE( & test_jump) ); test->add( BOOST_TEST_CASE( & test_result_int) ); test->add( BOOST_TEST_CASE( & test_result_string) ); test->add( BOOST_TEST_CASE( & test_arg_int) ); test->add( BOOST_TEST_CASE( & test_arg_string) ); test->add( BOOST_TEST_CASE( & test_fp) ); test->add( BOOST_TEST_CASE( & test_ptr) ); test->add( BOOST_TEST_CASE( & test_const_ptr) ); test->add( BOOST_TEST_CASE( & test_no_result) ); test->add( BOOST_TEST_CASE( & test_ref) ); test->add( BOOST_TEST_CASE( & test_const_ref) ); test->add( BOOST_TEST_CASE( & test_tuple) ); test->add( BOOST_TEST_CASE( & test_unwind) ); test->add( BOOST_TEST_CASE( & test_exceptions) ); test->add( BOOST_TEST_CASE( & test_input_iterator) ); test->add( BOOST_TEST_CASE( & test_output_iterator) ); test->add( BOOST_TEST_CASE( & test_chaining) ); return test; }