////////////////////////////////////////////////////////////////////////////// // Copyright 2002-2006 Andreas Huber Doenni // Distributed under the Boost Software License, Version 1.0. (See accompany- // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// #define NO_OF_BITS 3 ////////////////////////////////////////////////////////////////////////////// // This program demonstrates the fact that measures must be taken to hide some // of the complexity (e.g. in separate .cpp file) of a Boost.Statechart state // machine once a certain size is reached. // For this purpose, a state machine with exactly 2^NO_OF_BITS states (i.e. // BitState< 0 > .. BitState< 2^NO_OF_BITS - 1 >) is generated. For the events // EvFlipBit< 0 > .. EvFlipBit< NO_OF_BITS - 1 > there is a transition from // each state to the state with the corresponding bit toggled. That is, there // is a total of 2^NO_OF_BITS * NO_OF_BITS transitions. // E.g. if the state machine is currently in state BitState< 5 > and receives // EvFlipBit< 2 >, it transitions to state BitState< 1 >. If it is in // BitState< 15 > and receives EvFlipBit< 4 > it transitions to BitState< 31 > // etc. // The maximum size of such a state machine depends on your compiler. The // following table gives upper limits for NO_OF_BITS. From this, rough // estimates for the maximum size of any "naively" implemented Boost.Statechart // machine (i.e. no attempt is made to hide inner state implementation in a // .cpp file) can be deduced. // // NOTE: Due to the fact that the amount of generated code more than // *doubles* each time NO_OF_BITS is *incremented*, build times on most // compilers soar when NO_OF_BITS > 6. // // Compiler | max. NO_OF_BITS b | max. states s | // --------------|-------------------|----------------| // MSVC 7.1 | b < 6 | 32 < s < 64 | // GCC 3.4.2 (1) | b < 8 | 128 < s < 256 | // // (1) This is a practical rather than a hard limit, caused by a compiler // memory footprint that was significantly larger than the 1GB physical // memory installed in the test machine. The resulting frequent swapping // led to compilation times of hours rather than minutes. ////////////////////////////////////////////////////////////////////////////// #include "UniqueObject.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // size_t #ifdef BOOST_INTEL # pragma warning( disable: 304 ) // access control not specified # pragma warning( disable: 444 ) // destructor for base is not virtual # pragma warning( disable: 981 ) // operands are evaluated in unspecified order #endif namespace sc = boost::statechart; namespace mpl = boost::mpl; ////////////////////////////////////////////////////////////////////////////// struct IDisplay { virtual void Display() const = 0; }; ////////////////////////////////////////////////////////////////////////////// template< class BitNo > struct EvFlipBit : sc::event< EvFlipBit< BitNo > > {}; template< class StateNo > struct BitState; struct BitMachine : sc::state_machine< BitMachine, BitState< mpl::integral_c< unsigned int, 0 > > > {}; template< class BitNo, class StateNo > struct FlipTransition { private: typedef typename mpl::bitxor_< StateNo, mpl::shift_left< mpl::integral_c< unsigned int, 1 >, BitNo > >::type NextStateNo; public: typedef typename sc::transition< EvFlipBit< BitNo >, BitState< NextStateNo > > type; BOOST_MPL_AUX_LAMBDA_SUPPORT( 2, FlipTransition, (BitNo, StateNo) ); }; ////////////////////////////////////////////////////////////////////////////// void DisplayBits( unsigned int number ) { char buffer[ NO_OF_BITS + 1 ]; buffer[ NO_OF_BITS ] = 0; for ( unsigned int bit = 0; bit < NO_OF_BITS; ++bit ) { buffer[ bit ] = number & ( 1 << ( NO_OF_BITS - bit - 1 ) ) ? '1' : '0'; } std::cout << "Current state: " << std::setw( 4 ) << number << " (" << buffer << ")" << std::endl; } template< class StateNo > struct BitState : sc::simple_state< BitState< StateNo >, BitMachine >, UniqueObject< BitState< StateNo > >, IDisplay { void * operator new( std::size_t size ) { return UniqueObject< BitState< StateNo > >::operator new( size ); } void operator delete( void * p, std::size_t size ) { UniqueObject< BitState< StateNo > >::operator delete( p, size ); } typedef typename mpl::copy< typename mpl::transform_view< mpl::range_c< unsigned int, 0, NO_OF_BITS >, FlipTransition< mpl::placeholders::_, StateNo > >::type, mpl::front_inserter< mpl::list<> > >::type reactions; virtual void Display() const { DisplayBits( StateNo::value ); } }; void DisplayMachineState( const BitMachine & bitMachine ) { bitMachine.state_cast< const IDisplay & >().Display(); } ////////////////////////////////////////////////////////////////////////////// boost::intrusive_ptr< const sc::event_base > pFlipBitEvents[ NO_OF_BITS ]; struct EventInserter { template< class BitNo > void operator()( const BitNo & ) { pFlipBitEvents[ BitNo::value ] = new EvFlipBit< BitNo >(); } }; void FillEventArray() { mpl::for_each< mpl::range_c< unsigned int, 0, NO_OF_BITS > >( EventInserter() ); } ////////////////////////////////////////////////////////////////////////////// void VisitAllStates( BitMachine & bitMachine, unsigned int msb ) { if ( msb > 0 ) { VisitAllStates( bitMachine, msb - 1 ); } bitMachine.process_event( *pFlipBitEvents[ msb ] ); DisplayMachineState( bitMachine ); if ( msb > 0 ) { VisitAllStates( bitMachine, msb - 1 ); } } ////////////////////////////////////////////////////////////////////////////// char GetKey() { char key; std::cin >> key; return key; } ////////////////////////////////////////////////////////////////////////////// int main() { FillEventArray(); const unsigned int noOfStates = 1 << NO_OF_BITS; std::cout << "Boost.Statechart BitMachine example\n"; std::cout << "Machine configuration: " << noOfStates << " states interconnected with " << noOfStates * NO_OF_BITS << " transitions.\n\n"; for ( unsigned int bit = 0; bit < NO_OF_BITS; ++bit ) { std::cout << bit - 0 << ": Flips bit " << bit - 0 << "\n"; } std::cout << "a: Goes through all states automatically\n"; std::cout << "e: Exits the program\n\n"; std::cout << "You may chain commands, e.g. 31 flips bits 3 and 1\n\n"; BitMachine bitMachine; bitMachine.initiate(); char key = GetKey(); while ( key != 'e' ) { if ( ( key >= '0' ) && ( key < static_cast< char >( '0' + NO_OF_BITS ) ) ) { bitMachine.process_event( *pFlipBitEvents[ key - '0' ] ); DisplayMachineState( bitMachine ); } else { switch( key ) { case 'a': { VisitAllStates( bitMachine, NO_OF_BITS - 1 ); } break; default: { std::cout << "Invalid key!\n"; } } } key = GetKey(); } return 0; }