3bc66cbb94
This helps ensure that library inclusion is self sustainable
605 lines
14 KiB
C++
605 lines
14 KiB
C++
|
|
// 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 <boost/coroutine/symmetric_coroutine.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <boost/assert.hpp>
|
|
#include <boost/bind.hpp>
|
|
#include <boost/foreach.hpp>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/move/move.hpp>
|
|
#include <boost/range.hpp>
|
|
#include <boost/ref.hpp>
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <boost/tuple/tuple.hpp>
|
|
#include <boost/utility.hpp>
|
|
|
|
namespace coro = boost::coroutines;
|
|
|
|
bool value1 = false;
|
|
int value2 = 0;
|
|
std::string value3;
|
|
|
|
typedef void( * coro_fn_void)(coro::symmetric_coroutine< void* >::yield_type &);
|
|
|
|
coro::symmetric_coroutine< void >::call_type * term_coro = 0;
|
|
|
|
struct X
|
|
{
|
|
int i;
|
|
|
|
X() :
|
|
i( 0)
|
|
{}
|
|
|
|
X( int i_) :
|
|
i( i_)
|
|
{}
|
|
};
|
|
|
|
X * p = 0;
|
|
|
|
struct Y
|
|
{
|
|
Y()
|
|
{ value2 = 7; }
|
|
|
|
~Y()
|
|
{ value2 = 0; }
|
|
};
|
|
|
|
template< typename X, typename I >
|
|
void trampoline( coro::symmetric_coroutine< void* >::yield_type & yield)
|
|
{
|
|
void * vp = yield.get();
|
|
X * x = static_cast< X * >( vp);
|
|
I i( yield);
|
|
x->d = & i;
|
|
i.suspend();
|
|
i.run();
|
|
}
|
|
|
|
struct B
|
|
{
|
|
virtual ~B() {}
|
|
|
|
virtual void foo() = 0;
|
|
};
|
|
|
|
class D : public B
|
|
{
|
|
public:
|
|
int count;
|
|
coro::symmetric_coroutine< void* >::call_type call;
|
|
coro::symmetric_coroutine< void* >::yield_type * yield;
|
|
|
|
D( coro::symmetric_coroutine< void* >::yield_type & yield_) :
|
|
B(),
|
|
count( 0),
|
|
call(),
|
|
yield( & yield_)
|
|
{}
|
|
|
|
void foo() {}
|
|
|
|
void resume()
|
|
{ call( 0); }
|
|
|
|
void suspend()
|
|
{ ( *yield)(); }
|
|
|
|
void run()
|
|
{
|
|
while ( yield && * yield)
|
|
{
|
|
++count;
|
|
suspend();
|
|
}
|
|
}
|
|
};
|
|
|
|
struct T
|
|
{
|
|
D * d;
|
|
|
|
T() :
|
|
d( 0)
|
|
{}
|
|
};
|
|
|
|
class copyable
|
|
{
|
|
public:
|
|
bool state;
|
|
|
|
copyable() :
|
|
state( false)
|
|
{}
|
|
|
|
copyable( int) :
|
|
state( true)
|
|
{}
|
|
|
|
void operator()( coro::symmetric_coroutine< bool >::yield_type & yield)
|
|
{
|
|
if ( yield)
|
|
value1 = yield.get();
|
|
}
|
|
};
|
|
|
|
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::symmetric_coroutine< int >::yield_type &)
|
|
{ value1 = state; }
|
|
};
|
|
|
|
void empty( coro::symmetric_coroutine< void >::yield_type &) {}
|
|
|
|
void f2( coro::symmetric_coroutine< void >::yield_type &)
|
|
{ ++value2; }
|
|
|
|
void f3( coro::symmetric_coroutine< X >::yield_type & yield)
|
|
{ value2 = yield.get().i; }
|
|
|
|
void f4( coro::symmetric_coroutine< X& >::yield_type & yield)
|
|
{
|
|
X & x = yield.get();
|
|
p = & x;
|
|
}
|
|
|
|
void f5( coro::symmetric_coroutine< X* >::yield_type & yield)
|
|
{ p = yield.get(); }
|
|
|
|
void f6( coro::symmetric_coroutine< void >::yield_type & yield)
|
|
{
|
|
Y y;
|
|
yield( *term_coro);
|
|
}
|
|
|
|
void f7( coro::symmetric_coroutine< int >::yield_type & yield)
|
|
{
|
|
value2 = yield.get();
|
|
yield( *term_coro);
|
|
value2 = yield.get();
|
|
}
|
|
|
|
template< typename E >
|
|
void f9( coro::symmetric_coroutine< void >::yield_type &, E const& e)
|
|
{ throw e; }
|
|
|
|
void f10( coro::symmetric_coroutine< int >::yield_type & yield,
|
|
coro::symmetric_coroutine< int >::call_type & other)
|
|
{
|
|
int i = yield.get();
|
|
yield( other, i);
|
|
value2 = yield.get();
|
|
}
|
|
|
|
void f101( coro::symmetric_coroutine< int >::yield_type & yield)
|
|
{ value2 = yield.get(); }
|
|
|
|
void f11( coro::symmetric_coroutine< void >::yield_type & yield,
|
|
coro::symmetric_coroutine< void >::call_type & other)
|
|
{
|
|
yield( other);
|
|
value2 = 7;
|
|
}
|
|
|
|
void f111( coro::symmetric_coroutine< void >::yield_type &)
|
|
{ value2 = 3; }
|
|
|
|
void f12( coro::symmetric_coroutine< X& >::yield_type & yield,
|
|
coro::symmetric_coroutine< X& >::call_type & other)
|
|
{
|
|
yield( other, yield.get());
|
|
p = & yield.get();
|
|
}
|
|
|
|
void f121( coro::symmetric_coroutine< X& >::yield_type & yield)
|
|
{ p = & yield.get(); }
|
|
|
|
void f14( coro::symmetric_coroutine< int >::yield_type & yield,
|
|
coro::symmetric_coroutine< std::string >::call_type & other)
|
|
{
|
|
std::string str( boost::lexical_cast< std::string >( yield.get() ) );
|
|
yield( other, str);
|
|
value2 = yield.get();
|
|
}
|
|
|
|
void f141( coro::symmetric_coroutine< std::string >::yield_type & yield)
|
|
{ value3 = yield.get(); }
|
|
|
|
void f15( coro::symmetric_coroutine< int >::yield_type & yield,
|
|
int offset,
|
|
coro::symmetric_coroutine< int >::call_type & other)
|
|
{
|
|
int x = yield.get();
|
|
value2 += x + offset;
|
|
yield( other, x);
|
|
x = yield.get();
|
|
value2 += x + offset;
|
|
yield( other, x);
|
|
}
|
|
|
|
void f151( coro::symmetric_coroutine< int >::yield_type & yield,
|
|
int offset)
|
|
{
|
|
int x = yield.get();
|
|
value2 += x + offset;
|
|
yield();
|
|
x = yield.get();
|
|
value2 += x + offset;
|
|
}
|
|
|
|
void f16( coro::symmetric_coroutine< int >::yield_type & yield)
|
|
{
|
|
while ( yield)
|
|
{
|
|
value2 = yield.get();
|
|
yield();
|
|
}
|
|
}
|
|
|
|
void test_move()
|
|
{
|
|
{
|
|
coro::symmetric_coroutine< void >::call_type coro1;
|
|
coro::symmetric_coroutine< void >::call_type coro2( empty);
|
|
BOOST_CHECK( ! coro1);
|
|
BOOST_CHECK( coro2);
|
|
coro1 = boost::move( coro2);
|
|
BOOST_CHECK( coro1);
|
|
BOOST_CHECK( ! coro2);
|
|
}
|
|
|
|
{
|
|
value1 = false;
|
|
copyable cp( 3);
|
|
BOOST_CHECK( cp.state);
|
|
BOOST_CHECK( ! value1);
|
|
coro::symmetric_coroutine< bool >::call_type coro( cp);
|
|
coro( true);
|
|
BOOST_CHECK( cp.state);
|
|
BOOST_CHECK( value1);
|
|
}
|
|
|
|
{
|
|
value1 = false;
|
|
moveable mv( 7);
|
|
BOOST_CHECK( mv.state);
|
|
BOOST_CHECK( ! value1);
|
|
coro::symmetric_coroutine< int >::call_type coro( boost::move( mv) );
|
|
coro( 7);
|
|
BOOST_CHECK( ! mv.state);
|
|
BOOST_CHECK( value1);
|
|
}
|
|
}
|
|
|
|
void test_complete()
|
|
{
|
|
value2 = 0;
|
|
|
|
coro::symmetric_coroutine< void >::call_type coro( f2);
|
|
BOOST_CHECK( coro);
|
|
coro();
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK_EQUAL( ( int)1, value2);
|
|
}
|
|
|
|
void test_yield()
|
|
{
|
|
value2 = 0;
|
|
|
|
coro::symmetric_coroutine< int >::call_type coro3(
|
|
boost::bind( f151, _1, 3) );
|
|
BOOST_CHECK( coro3);
|
|
coro::symmetric_coroutine< int >::call_type coro2(
|
|
boost::bind( f15, _1, 2, boost::ref( coro3) ) );
|
|
BOOST_CHECK( coro2);
|
|
coro::symmetric_coroutine< int >::call_type coro1(
|
|
boost::bind( f15, _1, 1, boost::ref( coro2) ) );
|
|
BOOST_CHECK( coro1);
|
|
|
|
BOOST_CHECK_EQUAL( ( int)0, value2);
|
|
coro1( 1);
|
|
BOOST_CHECK( coro3);
|
|
BOOST_CHECK( coro2);
|
|
BOOST_CHECK( coro1);
|
|
BOOST_CHECK_EQUAL( ( int)9, value2);
|
|
coro1( 2);
|
|
BOOST_CHECK( ! coro3);
|
|
BOOST_CHECK( coro2);
|
|
BOOST_CHECK( coro1);
|
|
BOOST_CHECK_EQUAL( ( int)21, value2);
|
|
}
|
|
|
|
void test_pass_value()
|
|
{
|
|
value2 = 0;
|
|
|
|
X x(7);
|
|
BOOST_CHECK_EQUAL( ( int)7, x.i);
|
|
BOOST_CHECK_EQUAL( 0, value2);
|
|
coro::symmetric_coroutine< X >::call_type coro( f3);
|
|
BOOST_CHECK( coro);
|
|
coro(7);
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK_EQUAL( ( int)7, x.i);
|
|
BOOST_CHECK_EQUAL( 7, value2);
|
|
}
|
|
|
|
void test_pass_reference()
|
|
{
|
|
p = 0;
|
|
|
|
X x;
|
|
coro::symmetric_coroutine< X& >::call_type coro( f4);
|
|
BOOST_CHECK( coro);
|
|
coro( x);
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK( p == & x);
|
|
}
|
|
|
|
void test_pass_pointer()
|
|
{
|
|
p = 0;
|
|
|
|
X x;
|
|
coro::symmetric_coroutine< X* >::call_type coro( f5);
|
|
BOOST_CHECK( coro);
|
|
coro( & x);
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK( p == & x);
|
|
}
|
|
|
|
void test_unwind()
|
|
{
|
|
value2 = 0;
|
|
{
|
|
coro::symmetric_coroutine< void >::call_type coro( f6);
|
|
coro::symmetric_coroutine< void >::call_type coro_e( empty);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK( coro_e);
|
|
term_coro = & coro_e;
|
|
BOOST_CHECK_EQUAL( ( int) 0, value2);
|
|
coro();
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( ( int) 7, value2);
|
|
}
|
|
BOOST_CHECK_EQUAL( ( int) 0, value2);
|
|
}
|
|
|
|
void test_no_unwind()
|
|
{
|
|
value2 = 0;
|
|
{
|
|
coro::symmetric_coroutine< void >::call_type coro( f6,
|
|
coro::attributes(
|
|
coro::stack_allocator::traits_type::default_size(),
|
|
coro::no_stack_unwind) );
|
|
coro::symmetric_coroutine< void >::call_type coro_e( empty);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK( coro_e);
|
|
term_coro = & coro_e;
|
|
BOOST_CHECK_EQUAL( ( int) 0, value2);
|
|
coro();
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( ( int) 7, value2);
|
|
}
|
|
BOOST_CHECK_EQUAL( ( int) 7, value2);
|
|
}
|
|
|
|
void test_termination()
|
|
{
|
|
value2 = 0;
|
|
|
|
coro::symmetric_coroutine< int >::call_type coro( f7);
|
|
coro::symmetric_coroutine< void >::call_type coro_e( empty);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK( coro_e);
|
|
term_coro = & coro_e;
|
|
BOOST_CHECK_EQUAL( ( int) 0, value2);
|
|
coro(3);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( ( int) 3, value2);
|
|
coro(7);
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK_EQUAL( ( int) 7, value2);
|
|
}
|
|
|
|
void test_yield_to_void()
|
|
{
|
|
value2 = 0;
|
|
|
|
coro::symmetric_coroutine< void >::call_type coro_other( f111);
|
|
coro::symmetric_coroutine< void >::call_type coro( boost::bind( f11, _1, boost::ref( coro_other) ) );
|
|
BOOST_CHECK( coro_other);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( ( int) 0, value2);
|
|
coro();
|
|
BOOST_CHECK( ! coro_other);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( ( int) 3, value2);
|
|
coro();
|
|
BOOST_CHECK( ! coro_other);
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK_EQUAL( ( int) 7, value2);
|
|
}
|
|
|
|
void test_yield_to_int()
|
|
{
|
|
value2 = 0;
|
|
|
|
coro::symmetric_coroutine< int >::call_type coro_other( f101);
|
|
coro::symmetric_coroutine< int >::call_type coro( boost::bind( f10, _1, boost::ref( coro_other) ) );
|
|
BOOST_CHECK( coro_other);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( ( int) 0, value2);
|
|
coro(3);
|
|
BOOST_CHECK( ! coro_other);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( ( int) 3, value2);
|
|
coro(7);
|
|
BOOST_CHECK( ! coro_other);
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK_EQUAL( ( int) 7, value2);
|
|
}
|
|
|
|
void test_yield_to_ref()
|
|
{
|
|
p = 0;
|
|
|
|
coro::symmetric_coroutine< X& >::call_type coro_other( f121);
|
|
coro::symmetric_coroutine< X& >::call_type coro( boost::bind( f12, _1, boost::ref( coro_other) ) );
|
|
BOOST_CHECK( coro_other);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK( 0 == p);
|
|
X x1(3);
|
|
coro( x1);
|
|
BOOST_CHECK( ! coro_other);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( p->i, x1.i);
|
|
BOOST_CHECK( p == & x1);
|
|
X x2(7);
|
|
coro(x2);
|
|
BOOST_CHECK( ! coro_other);
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK_EQUAL( p->i, x2.i);
|
|
BOOST_CHECK( p == & x2);
|
|
}
|
|
|
|
void test_yield_to_different()
|
|
{
|
|
value2 = 0;
|
|
value3 = "";
|
|
|
|
coro::symmetric_coroutine< std::string >::call_type coro_other( f141);
|
|
coro::symmetric_coroutine< int >::call_type coro( boost::bind( f14, _1, boost::ref( coro_other) ) );
|
|
BOOST_CHECK( coro_other);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( ( int) 0, value2);
|
|
BOOST_CHECK( value3.empty() );
|
|
coro(3);
|
|
BOOST_CHECK( ! coro_other);
|
|
BOOST_CHECK( coro);
|
|
BOOST_CHECK_EQUAL( "3", value3);
|
|
coro(7);
|
|
BOOST_CHECK( ! coro_other);
|
|
BOOST_CHECK( ! coro);
|
|
BOOST_CHECK_EQUAL( ( int) 7, value2);
|
|
}
|
|
|
|
void test_move_coro()
|
|
{
|
|
value2 = 0;
|
|
|
|
coro::symmetric_coroutine< int >::call_type coro1( f16);
|
|
coro::symmetric_coroutine< int >::call_type coro2;
|
|
BOOST_CHECK( coro1);
|
|
BOOST_CHECK( ! coro2);
|
|
|
|
coro1( 1);
|
|
BOOST_CHECK_EQUAL( ( int)1, value2);
|
|
|
|
coro2 = boost::move( coro1);
|
|
BOOST_CHECK( ! coro1);
|
|
BOOST_CHECK( coro2);
|
|
|
|
coro2( 2);
|
|
BOOST_CHECK_EQUAL( ( int)2, value2);
|
|
|
|
coro1 = boost::move( coro2);
|
|
BOOST_CHECK( coro1);
|
|
BOOST_CHECK( ! coro2);
|
|
|
|
coro1( 3);
|
|
BOOST_CHECK_EQUAL( ( int)3, value2);
|
|
|
|
coro2 = boost::move( coro1);
|
|
BOOST_CHECK( ! coro1);
|
|
BOOST_CHECK( coro2);
|
|
|
|
coro2( 4);
|
|
BOOST_CHECK_EQUAL( ( int)4, value2);
|
|
}
|
|
|
|
void test_vptr()
|
|
{
|
|
D * d = 0;
|
|
T t;
|
|
coro_fn_void fn = trampoline< T, D >;
|
|
coro::symmetric_coroutine< void* >::call_type call( fn);
|
|
call( & t);
|
|
d = t.d;
|
|
BOOST_CHECK( 0 != d);
|
|
d->call = boost::move( call);
|
|
|
|
BOOST_CHECK_EQUAL( ( int) 0, d->count);
|
|
d->resume();
|
|
BOOST_CHECK_EQUAL( ( int) 1, d->count);
|
|
d->resume();
|
|
BOOST_CHECK_EQUAL( ( int) 2, d->count);
|
|
}
|
|
|
|
boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
|
|
{
|
|
boost::unit_test::test_suite * test =
|
|
BOOST_TEST_SUITE("Boost.coroutine: symmetric coroutine test suite");
|
|
|
|
test->add( BOOST_TEST_CASE( & test_move) );
|
|
test->add( BOOST_TEST_CASE( & test_complete) );
|
|
test->add( BOOST_TEST_CASE( & test_yield) );
|
|
test->add( BOOST_TEST_CASE( & test_pass_value) );
|
|
test->add( BOOST_TEST_CASE( & test_pass_reference) );
|
|
test->add( BOOST_TEST_CASE( & test_pass_pointer) );
|
|
test->add( BOOST_TEST_CASE( & test_termination) );
|
|
test->add( BOOST_TEST_CASE( & test_unwind) );
|
|
test->add( BOOST_TEST_CASE( & test_no_unwind) );
|
|
test->add( BOOST_TEST_CASE( & test_yield_to_void) );
|
|
test->add( BOOST_TEST_CASE( & test_yield_to_int) );
|
|
test->add( BOOST_TEST_CASE( & test_yield_to_ref) );
|
|
test->add( BOOST_TEST_CASE( & test_yield_to_different) );
|
|
test->add( BOOST_TEST_CASE( & test_move_coro) );
|
|
test->add( BOOST_TEST_CASE( & test_vptr) );
|
|
|
|
return test;
|
|
}
|