// Copyright Oliver Kowalke 2009. // 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 #include #include #include #include #include #include namespace coro = boost::coroutines; int value1 = 0; std::string value2 = ""; bool value3 = false; double value4 = .0; int * value5 = 0; int& value6 = value1; int& value7 = value1; int value8 = 0; int value9 = 0; struct X : private boost::noncopyable { X() { value1 = 7; } ~X() { value1 = 0; } }; class copyable { public: bool state; copyable() : state( false) {} copyable( int) : state( true) {} void operator()( coro::asymmetric_coroutine< int >::push_type &) { value3 = state; } }; class moveable { private: BOOST_MOVABLE_BUT_NOT_COPYABLE( moveable) public: bool state; moveable() : state( false) {} moveable( int) : state( true) {} moveable( BOOST_RV_REF( moveable) other) : state( false) { std::swap( state, other.state); } moveable & operator=( BOOST_RV_REF( moveable) other) { if ( this == & other) return * this; moveable tmp( boost::move( other) ); std::swap( state, tmp.state); return * this; } void operator()( coro::asymmetric_coroutine< int >::push_type &) { value3 = state; } }; struct my_exception {}; void f1( coro::asymmetric_coroutine< void >::push_type & c) { while ( c) c(); } void f2( coro::asymmetric_coroutine< void >::push_type &) { ++value1; } void f3( coro::asymmetric_coroutine< void >::push_type & c) { ++value1; c(); ++value1; } void f4( coro::asymmetric_coroutine< int >::push_type & c) { c( 3); c( 7); } void f5( coro::asymmetric_coroutine< std::string >::push_type & c) { std::string res("abc"); c( res); res = "xyz"; c( res); } void f6( coro::asymmetric_coroutine< int >::pull_type & c) { value1 = c.get(); } void f7( coro::asymmetric_coroutine< std::string >::pull_type & c) { value2 = c.get(); } void f8( coro::asymmetric_coroutine< boost::tuple< double, double > >::pull_type & c) { double x = 0, y = 0; boost::tie( x, y) = c.get(); value4 = x + y; c(); boost::tie( x, y) = c.get(); value4 = x + y; } void f9( coro::asymmetric_coroutine< int * >::pull_type & c) { value5 = c.get(); } void f91( coro::asymmetric_coroutine< int const* >::pull_type & c) { value5 = const_cast< int * >( c.get() ); } void f10( coro::asymmetric_coroutine< int & >::pull_type & c) { int const& i = c.get(); value5 = const_cast< int * >( & i); } void f101( coro::asymmetric_coroutine< int const& >::pull_type & c) { int const& i = c.get(); value5 = const_cast< int * >( & i); } void f11( coro::asymmetric_coroutine< boost::tuple< int, int > >::pull_type & c) { boost::tie( value8, value9) = c.get(); } void f12( coro::asymmetric_coroutine< void >::pull_type & c) { X x_; c(); c(); } template< typename E > void f14( coro::asymmetric_coroutine< void >::pull_type &, E const& e) { throw e; } void f16( coro::asymmetric_coroutine< int >::push_type & c) { c( 1); c( 2); c( 3); c( 4); c( 5); } void f17( coro::asymmetric_coroutine< int >::pull_type & c, std::vector< int > & vec) { int x = c.get(); while ( 5 > x) { vec.push_back( x); x = c().get(); } } void f19( coro::asymmetric_coroutine< int* >::push_type & c, std::vector< int * > & vec) { BOOST_FOREACH( int * ptr, vec) { c( ptr); } } void f20( coro::asymmetric_coroutine< int >::push_type &) {} void f21( coro::asymmetric_coroutine< int >::pull_type & c) { while ( c) { value1 = c.get(); c(); } } void test_move() { { coro::asymmetric_coroutine< void >::pull_type coro1; coro::asymmetric_coroutine< void >::pull_type coro2( f1); BOOST_CHECK( ! coro1); BOOST_CHECK( coro2); coro2(); coro1 = boost::move( coro2); BOOST_CHECK( coro1); coro1(); BOOST_CHECK( ! coro2); } { value3 = false; copyable cp( 3); BOOST_CHECK( cp.state); BOOST_CHECK( ! value3); coro::asymmetric_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::asymmetric_coroutine< int >::pull_type coro( boost::move( mv) ); BOOST_CHECK( ! mv.state); BOOST_CHECK( value3); } } void test_complete() { value1 = 0; coro::asymmetric_coroutine< void >::pull_type coro( f2); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( ( int)1, value1); } void test_jump() { value1 = 0; coro::asymmetric_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::asymmetric_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::asymmetric_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::asymmetric_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::asymmetric_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::asymmetric_coroutine< boost::tuple< double, double > >::push_type coro( f8); BOOST_CHECK( coro); coro( boost::make_tuple( 7.35, 3.14) ); BOOST_CHECK( coro); BOOST_CHECK_EQUAL( ( double) 10.49, value4); value4 = 0; coro( boost::make_tuple( 1.15, 3.14) ); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( ( double) 4.29, value4); } void test_ptr() { value5 = 0; int a = 3; coro::asymmetric_coroutine< int * >::push_type coro( f9); BOOST_CHECK( coro); coro( & a); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( & a, value5); } void test_const_ptr() { value5 = 0; int a = 3; coro::asymmetric_coroutine< int const* >::push_type coro( f91); BOOST_CHECK( coro); coro( & a); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( & a, value5); } void test_ref() { value5 = 0; int a = 3; coro::asymmetric_coroutine< int & >::push_type coro( f10); BOOST_CHECK( coro); coro( a); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( & a, value5); } void test_const_ref() { value5 = 0; int a = 3; coro::asymmetric_coroutine< int const& >::push_type coro( f101); BOOST_CHECK( coro); coro( a); BOOST_CHECK( ! coro); BOOST_CHECK_EQUAL( & a, value5); } void test_tuple() { value8 = 0; value9 = 0; int a = 3, b = 7; boost::tuple< int, int > tpl( a, b); BOOST_CHECK_EQUAL( a, tpl.get< 0 >() ); BOOST_CHECK_EQUAL( b, tpl.get< 1 >() ); coro::asymmetric_coroutine< boost::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::asymmetric_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); } void test_no_unwind() { value1 = 0; { coro::asymmetric_coroutine< void >::push_type coro( f12, coro::attributes( coro::stack_allocator::traits_type::default_size(), coro::no_stack_unwind) ); 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) 7, value1); } void test_exceptions() { bool thrown = false; std::runtime_error ex("abc"); try { coro::asymmetric_coroutine< void >::push_type coro( boost::bind( f14< std::runtime_error >, _1, ex) ); BOOST_CHECK( coro); coro(); BOOST_CHECK( ! coro); BOOST_CHECK( false); } catch ( std::runtime_error const&) { thrown = true; } catch ( std::exception const&) {} catch (...) {} BOOST_CHECK( thrown); } void test_input_iterator() { { std::vector< int > vec; coro::asymmetric_coroutine< int >::pull_type coro( f16); BOOST_FOREACH( int 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] ); } { std::vector< int > vec; coro::asymmetric_coroutine< int >::pull_type coro( f16); coro::asymmetric_coroutine< int >::pull_type::iterator e = boost::end( coro); for ( coro::asymmetric_coroutine< int >::pull_type::iterator i = boost::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] ); } { int i1 = 1, i2 = 2, i3 = 3; std::vector< int* > vec_in; vec_in.push_back( & i1); vec_in.push_back( & i2); vec_in.push_back( & i3); std::vector< int* > vec_out; coro::asymmetric_coroutine< int* >::pull_type coro( boost::bind( f19, _1, boost::ref( vec_in) ) ); coro::asymmetric_coroutine< int* >::pull_type::iterator e = boost::end( coro); for ( coro::asymmetric_coroutine< int* >::pull_type::iterator i = boost::begin( coro); i != e; ++i) { int * p = * i; vec_out.push_back( p); } BOOST_CHECK_EQUAL( ( std::size_t)3, vec_out.size() ); BOOST_CHECK_EQUAL( & i1, vec_out[0] ); BOOST_CHECK_EQUAL( & i2, vec_out[1] ); BOOST_CHECK_EQUAL( & i3, vec_out[2] ); } } void test_output_iterator() { int counter = 0; std::vector< int > vec; coro::asymmetric_coroutine< int >::push_type coro( boost::bind( f17, _1, boost::ref( vec) ) ); coro::asymmetric_coroutine< int >::push_type::iterator e( boost::end( coro) ); for ( coro::asymmetric_coroutine< int >::push_type::iterator i( boost::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] ); } void test_invalid_result() { bool catched = false; coro::asymmetric_coroutine< int >::pull_type coro( f20); BOOST_CHECK( ! coro); try { int i = coro.get(); (void)i; } catch ( coro::invalid_result const&) { catched = true; } BOOST_CHECK( catched); } void test_move_coro() { value1 = 0; coro::asymmetric_coroutine< int >::push_type coro1( f21); coro::asymmetric_coroutine< int >::push_type coro2; BOOST_CHECK( coro1); BOOST_CHECK( ! coro2); coro1( 1); BOOST_CHECK_EQUAL( ( int)1, value1); coro2 = boost::move( coro1); BOOST_CHECK( ! coro1); BOOST_CHECK( coro2); coro2( 2); BOOST_CHECK_EQUAL( ( int)2, value1); coro1 = boost::move( coro2); BOOST_CHECK( coro1); BOOST_CHECK( ! coro2); coro1( 3); BOOST_CHECK_EQUAL( ( int)3, value1); coro2 = boost::move( coro1); BOOST_CHECK( ! coro1); BOOST_CHECK( coro2); coro2( 4); BOOST_CHECK_EQUAL( ( int)4, value1); } void foo( coro::asymmetric_coroutine< int >::push_type & yield) { yield( 1); } coro::asymmetric_coroutine< int >::pull_type make_range() { return coro::asymmetric_coroutine< int >::pull_type( foo); } template< typename Range > void const_func( Range const& r) { begin( r); } void test_range() { const_func( make_range() ); } boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { boost::unit_test::test_suite * test = BOOST_TEST_SUITE("Boost.coroutine: asymmetric coroutine test suite"); test->add( BOOST_TEST_CASE( & test_move) ); test->add( BOOST_TEST_CASE( & test_complete) ); 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_invalid_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_no_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_range) ); return test; }