annotate DEPENDENCIES/generic/include/boost/statechart/state_machine.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents c530137014c0
children
rev   line source
Chris@16 1 #ifndef BOOST_STATECHART_STATE_MACHINE_HPP_INCLUDED
Chris@16 2 #define BOOST_STATECHART_STATE_MACHINE_HPP_INCLUDED
Chris@16 3 //////////////////////////////////////////////////////////////////////////////
Chris@16 4 // Copyright 2002-2010 Andreas Huber Doenni
Chris@16 5 // Distributed under the Boost Software License, Version 1.0. (See accompany-
Chris@16 6 // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Chris@16 7 //////////////////////////////////////////////////////////////////////////////
Chris@16 8
Chris@16 9
Chris@16 10
Chris@16 11 #include <boost/statechart/event.hpp>
Chris@16 12 #include <boost/statechart/null_exception_translator.hpp>
Chris@16 13 #include <boost/statechart/result.hpp>
Chris@16 14
Chris@16 15 #include <boost/statechart/detail/rtti_policy.hpp>
Chris@16 16 #include <boost/statechart/detail/state_base.hpp>
Chris@16 17 #include <boost/statechart/detail/leaf_state.hpp>
Chris@16 18 #include <boost/statechart/detail/node_state.hpp>
Chris@16 19 #include <boost/statechart/detail/constructor.hpp>
Chris@16 20 #include <boost/statechart/detail/avoid_unused_warning.hpp>
Chris@16 21
Chris@16 22 #include <boost/mpl/list.hpp>
Chris@16 23 #include <boost/mpl/clear.hpp>
Chris@16 24 #include <boost/mpl/if.hpp>
Chris@16 25 #include <boost/mpl/at.hpp>
Chris@16 26 #include <boost/mpl/integral_c.hpp>
Chris@16 27 #include <boost/mpl/minus.hpp>
Chris@16 28 #include <boost/mpl/equal_to.hpp>
Chris@16 29
Chris@16 30 #include <boost/intrusive_ptr.hpp>
Chris@16 31 #include <boost/type_traits/is_pointer.hpp>
Chris@16 32 #include <boost/type_traits/remove_reference.hpp>
Chris@16 33 #include <boost/noncopyable.hpp>
Chris@16 34 #include <boost/assert.hpp>
Chris@16 35 #include <boost/static_assert.hpp>
Chris@101 36 #include <boost/polymorphic_cast.hpp> // boost::polymorphic_downcast
Chris@16 37 // BOOST_NO_EXCEPTIONS, BOOST_MSVC, BOOST_MSVC_STD_ITERATOR
Chris@16 38 #include <boost/config.hpp>
Chris@16 39
Chris@16 40 #include <boost/detail/allocator_utilities.hpp>
Chris@16 41
Chris@16 42 #ifdef BOOST_MSVC
Chris@16 43 # pragma warning( push )
Chris@16 44 # pragma warning( disable: 4702 ) // unreachable code (in release mode only)
Chris@16 45 #endif
Chris@16 46
Chris@16 47 #include <map>
Chris@16 48
Chris@16 49 #ifdef BOOST_MSVC
Chris@16 50 # pragma warning( pop )
Chris@16 51 #endif
Chris@16 52
Chris@16 53 #include <memory> // std::allocator
Chris@16 54 #include <typeinfo> // std::bad_cast
Chris@16 55 #include <functional> // std::less
Chris@16 56 #include <iterator>
Chris@16 57
Chris@16 58
Chris@16 59
Chris@16 60 namespace boost
Chris@16 61 {
Chris@16 62 namespace statechart
Chris@16 63 {
Chris@16 64 namespace detail
Chris@16 65 {
Chris@16 66
Chris@16 67
Chris@16 68
Chris@16 69 //////////////////////////////////////////////////////////////////////////////
Chris@16 70 template< class StateBaseType, class EventBaseType, class IdType >
Chris@16 71 class send_function
Chris@16 72 {
Chris@16 73 public:
Chris@16 74 //////////////////////////////////////////////////////////////////////////
Chris@16 75 send_function(
Chris@16 76 StateBaseType & toState,
Chris@16 77 const EventBaseType & evt,
Chris@16 78 IdType eventType
Chris@16 79 ) :
Chris@16 80 toState_( toState ), evt_( evt ), eventType_( eventType )
Chris@16 81 {
Chris@16 82 }
Chris@16 83
Chris@16 84 result operator()()
Chris@16 85 {
Chris@16 86 return detail::result_utility::make_result(
Chris@16 87 toState_.react_impl( evt_, eventType_ ) );
Chris@16 88 }
Chris@16 89
Chris@16 90 private:
Chris@16 91 //////////////////////////////////////////////////////////////////////////
Chris@16 92 // avoids C4512 (assignment operator could not be generated)
Chris@16 93 send_function & operator=( const send_function & );
Chris@16 94
Chris@16 95 StateBaseType & toState_;
Chris@16 96 const EventBaseType & evt_;
Chris@16 97 IdType eventType_;
Chris@16 98 };
Chris@16 99
Chris@16 100
Chris@16 101 //////////////////////////////////////////////////////////////////////////////
Chris@16 102 struct state_cast_impl_pointer_target
Chris@16 103 {
Chris@16 104 public:
Chris@16 105 //////////////////////////////////////////////////////////////////////////
Chris@16 106 template< class StateBaseType >
Chris@16 107 static const StateBaseType * deref_if_necessary(
Chris@16 108 const StateBaseType * pState )
Chris@16 109 {
Chris@16 110 return pState;
Chris@16 111 }
Chris@16 112
Chris@16 113 template< class Target, class IdType >
Chris@16 114 static IdType type_id()
Chris@16 115 {
Chris@16 116 Target p = 0;
Chris@16 117 return type_id_impl< IdType >( p );
Chris@16 118 }
Chris@16 119
Chris@16 120 static bool found( const void * pFound )
Chris@16 121 {
Chris@16 122 return pFound != 0;
Chris@16 123 }
Chris@16 124
Chris@16 125 template< class Target >
Chris@16 126 static Target not_found()
Chris@16 127 {
Chris@16 128 return 0;
Chris@16 129 }
Chris@16 130
Chris@16 131 private:
Chris@16 132 //////////////////////////////////////////////////////////////////////////
Chris@16 133 template< class IdType, class Type >
Chris@16 134 static IdType type_id_impl( const Type * )
Chris@16 135 {
Chris@16 136 return Type::static_type();
Chris@16 137 }
Chris@16 138 };
Chris@16 139
Chris@16 140 struct state_cast_impl_reference_target
Chris@16 141 {
Chris@16 142 template< class StateBaseType >
Chris@16 143 static const StateBaseType & deref_if_necessary(
Chris@16 144 const StateBaseType * pState )
Chris@16 145 {
Chris@16 146 return *pState;
Chris@16 147 }
Chris@16 148
Chris@16 149 template< class Target, class IdType >
Chris@16 150 static IdType type_id()
Chris@16 151 {
Chris@16 152 return remove_reference< Target >::type::static_type();
Chris@16 153 }
Chris@16 154
Chris@16 155 template< class Dummy >
Chris@16 156 static bool found( const Dummy & )
Chris@16 157 {
Chris@16 158 return true;
Chris@16 159 }
Chris@16 160
Chris@16 161 template< class Target >
Chris@16 162 static Target not_found()
Chris@16 163 {
Chris@16 164 throw std::bad_cast();
Chris@16 165 }
Chris@16 166 };
Chris@16 167
Chris@16 168 template< class Target >
Chris@16 169 struct state_cast_impl : public mpl::if_<
Chris@16 170 is_pointer< Target >,
Chris@16 171 state_cast_impl_pointer_target,
Chris@16 172 state_cast_impl_reference_target
Chris@16 173 >::type {};
Chris@16 174
Chris@16 175
Chris@16 176 //////////////////////////////////////////////////////////////////////////////
Chris@16 177 template< class RttiPolicy >
Chris@16 178 class history_key
Chris@16 179 {
Chris@16 180 public:
Chris@16 181 //////////////////////////////////////////////////////////////////////////
Chris@16 182 template< class HistorizedState >
Chris@16 183 static history_key make_history_key()
Chris@16 184 {
Chris@16 185 return history_key(
Chris@16 186 HistorizedState::context_type::static_type(),
Chris@16 187 HistorizedState::orthogonal_position::value );
Chris@16 188 }
Chris@16 189
Chris@16 190 typename RttiPolicy::id_type history_context_type() const
Chris@16 191 {
Chris@16 192 return historyContextType_;
Chris@16 193 }
Chris@16 194
Chris@16 195 friend bool operator<(
Chris@16 196 const history_key & left, const history_key & right )
Chris@16 197 {
Chris@16 198 return
Chris@16 199 std::less< typename RttiPolicy::id_type >()(
Chris@16 200 left.historyContextType_, right.historyContextType_ ) ||
Chris@16 201 ( ( left.historyContextType_ == right.historyContextType_ ) &&
Chris@16 202 ( left.historizedOrthogonalRegion_ <
Chris@16 203 right.historizedOrthogonalRegion_ ) );
Chris@16 204 }
Chris@16 205
Chris@16 206 private:
Chris@16 207 //////////////////////////////////////////////////////////////////////////
Chris@16 208 history_key(
Chris@16 209 typename RttiPolicy::id_type historyContextType,
Chris@16 210 orthogonal_position_type historizedOrthogonalRegion
Chris@16 211 ) :
Chris@16 212 historyContextType_( historyContextType ),
Chris@16 213 historizedOrthogonalRegion_( historizedOrthogonalRegion )
Chris@16 214 {
Chris@16 215 }
Chris@16 216
Chris@16 217 // avoids C4512 (assignment operator could not be generated)
Chris@16 218 history_key & operator=( const history_key & );
Chris@16 219
Chris@16 220 const typename RttiPolicy::id_type historyContextType_;
Chris@16 221 const orthogonal_position_type historizedOrthogonalRegion_;
Chris@16 222 };
Chris@16 223
Chris@16 224
Chris@16 225
Chris@16 226 } // namespace detail
Chris@16 227
Chris@16 228
Chris@16 229
Chris@16 230 //////////////////////////////////////////////////////////////////////////////
Chris@16 231 template< class MostDerived,
Chris@16 232 class InitialState,
Chris@16 233 class Allocator = std::allocator< void >,
Chris@16 234 class ExceptionTranslator = null_exception_translator >
Chris@16 235 class state_machine : noncopyable
Chris@16 236 {
Chris@16 237 public:
Chris@16 238 //////////////////////////////////////////////////////////////////////////
Chris@16 239 typedef Allocator allocator_type;
Chris@16 240 typedef detail::rtti_policy rtti_policy_type;
Chris@16 241 typedef event_base event_base_type;
Chris@16 242 typedef intrusive_ptr< const event_base_type > event_base_ptr_type;
Chris@16 243
Chris@16 244 void initiate()
Chris@16 245 {
Chris@16 246 terminate();
Chris@16 247
Chris@16 248 {
Chris@16 249 terminator guard( *this, 0 );
Chris@16 250 detail::result_utility::get_result( translator_(
Chris@16 251 initial_construct_function( *this ),
Chris@16 252 exception_event_handler( *this ) ) );
Chris@16 253 guard.dismiss();
Chris@16 254 }
Chris@16 255
Chris@16 256 process_queued_events();
Chris@16 257 }
Chris@16 258
Chris@16 259 void terminate()
Chris@16 260 {
Chris@16 261 terminator guard( *this, 0 );
Chris@16 262 detail::result_utility::get_result( translator_(
Chris@16 263 terminate_function( *this ),
Chris@16 264 exception_event_handler( *this ) ) );
Chris@16 265 guard.dismiss();
Chris@16 266 }
Chris@16 267
Chris@16 268 bool terminated() const
Chris@16 269 {
Chris@16 270 return pOutermostState_ == 0;
Chris@16 271 }
Chris@16 272
Chris@16 273 void process_event( const event_base_type & evt )
Chris@16 274 {
Chris@16 275 if ( send_event( evt ) == detail::do_defer_event )
Chris@16 276 {
Chris@16 277 deferredEventQueue_.push_back( evt.intrusive_from_this() );
Chris@16 278 }
Chris@16 279
Chris@16 280 process_queued_events();
Chris@16 281 }
Chris@16 282
Chris@16 283 template< class Target >
Chris@16 284 Target state_cast() const
Chris@16 285 {
Chris@16 286 typedef detail::state_cast_impl< Target > impl;
Chris@16 287
Chris@16 288 for ( typename state_list_type::const_iterator pCurrentLeafState =
Chris@16 289 currentStates_.begin();
Chris@16 290 pCurrentLeafState != currentStatesEnd_;
Chris@16 291 ++pCurrentLeafState )
Chris@16 292 {
Chris@16 293 const state_base_type * pCurrentState(
Chris@16 294 get_pointer( *pCurrentLeafState ) );
Chris@16 295
Chris@16 296 while ( pCurrentState != 0 )
Chris@16 297 {
Chris@16 298 // The unnecessary try/catch overhead for pointer targets is
Chris@16 299 // typically small compared to the cycles dynamic_cast needs
Chris@16 300 #ifndef BOOST_NO_EXCEPTIONS
Chris@16 301 try
Chris@16 302 #endif
Chris@16 303 {
Chris@16 304 Target result = dynamic_cast< Target >(
Chris@16 305 impl::deref_if_necessary( pCurrentState ) );
Chris@16 306
Chris@16 307 if ( impl::found( result ) )
Chris@16 308 {
Chris@16 309 return result;
Chris@16 310 }
Chris@16 311 }
Chris@16 312 #ifndef BOOST_NO_EXCEPTIONS
Chris@16 313 // Intentionally swallow std::bad_cast exceptions. We'll throw one
Chris@16 314 // ourselves when we fail to find a state that can be cast to Target
Chris@16 315 catch ( const std::bad_cast & ) {}
Chris@16 316 #endif
Chris@16 317
Chris@16 318 pCurrentState = pCurrentState->outer_state_ptr();
Chris@16 319 }
Chris@16 320 }
Chris@16 321
Chris@16 322 return impl::template not_found< Target >();
Chris@16 323 }
Chris@16 324
Chris@16 325 template< class Target >
Chris@16 326 Target state_downcast() const
Chris@16 327 {
Chris@16 328 typedef detail::state_cast_impl< Target > impl;
Chris@16 329
Chris@16 330 typename rtti_policy_type::id_type targetType =
Chris@16 331 impl::template type_id< Target, rtti_policy_type::id_type >();
Chris@16 332
Chris@16 333 for ( typename state_list_type::const_iterator pCurrentLeafState =
Chris@16 334 currentStates_.begin();
Chris@16 335 pCurrentLeafState != currentStatesEnd_;
Chris@16 336 ++pCurrentLeafState )
Chris@16 337 {
Chris@16 338 const state_base_type * pCurrentState(
Chris@16 339 get_pointer( *pCurrentLeafState ) );
Chris@16 340
Chris@16 341 while ( pCurrentState != 0 )
Chris@16 342 {
Chris@16 343 if ( pCurrentState->dynamic_type() == targetType )
Chris@16 344 {
Chris@16 345 return static_cast< Target >(
Chris@16 346 impl::deref_if_necessary( pCurrentState ) );
Chris@16 347 }
Chris@16 348
Chris@16 349 pCurrentState = pCurrentState->outer_state_ptr();
Chris@16 350 }
Chris@16 351 }
Chris@16 352
Chris@16 353 return impl::template not_found< Target >();
Chris@16 354 }
Chris@16 355
Chris@16 356 typedef detail::state_base< allocator_type, rtti_policy_type >
Chris@16 357 state_base_type;
Chris@16 358
Chris@16 359 class state_iterator : public std::iterator<
Chris@16 360 std::forward_iterator_tag,
Chris@16 361 state_base_type, std::ptrdiff_t
Chris@16 362 #ifndef BOOST_MSVC_STD_ITERATOR
Chris@16 363 , const state_base_type *, const state_base_type &
Chris@16 364 #endif
Chris@16 365 >
Chris@16 366 {
Chris@16 367 public:
Chris@16 368 //////////////////////////////////////////////////////////////////////
Chris@16 369 explicit state_iterator(
Chris@16 370 typename state_base_type::state_list_type::const_iterator
Chris@16 371 baseIterator
Chris@16 372 ) : baseIterator_( baseIterator ) {}
Chris@16 373
Chris@16 374 const state_base_type & operator*() const { return **baseIterator_; }
Chris@16 375 const state_base_type * operator->() const
Chris@16 376 {
Chris@16 377 return &**baseIterator_;
Chris@16 378 }
Chris@16 379
Chris@16 380 state_iterator & operator++() { ++baseIterator_; return *this; }
Chris@16 381 state_iterator operator++( int )
Chris@16 382 {
Chris@16 383 return state_iterator( baseIterator_++ );
Chris@16 384 }
Chris@16 385
Chris@16 386 bool operator==( const state_iterator & right ) const
Chris@16 387 {
Chris@16 388 return baseIterator_ == right.baseIterator_;
Chris@16 389 }
Chris@16 390 bool operator!=( const state_iterator & right ) const
Chris@16 391 {
Chris@16 392 return !( *this == right );
Chris@16 393 }
Chris@16 394
Chris@16 395 private:
Chris@16 396 typename state_base_type::state_list_type::const_iterator
Chris@16 397 baseIterator_;
Chris@16 398 };
Chris@16 399
Chris@16 400 state_iterator state_begin() const
Chris@16 401 {
Chris@16 402 return state_iterator( currentStates_.begin() );
Chris@16 403 }
Chris@16 404
Chris@16 405 state_iterator state_end() const
Chris@16 406 {
Chris@16 407 return state_iterator( currentStatesEnd_ );
Chris@16 408 }
Chris@16 409
Chris@16 410 void unconsumed_event( const event_base & ) {}
Chris@16 411
Chris@16 412 protected:
Chris@16 413 //////////////////////////////////////////////////////////////////////////
Chris@16 414 state_machine() :
Chris@16 415 currentStatesEnd_( currentStates_.end() ),
Chris@16 416 pOutermostState_( 0 ),
Chris@16 417 isInnermostCommonOuter_( false ),
Chris@16 418 performFullExit_( true ),
Chris@16 419 pTriggeringEvent_( 0 )
Chris@16 420 {
Chris@16 421 }
Chris@16 422
Chris@16 423 // This destructor was only made virtual so that that
Chris@16 424 // polymorphic_downcast can be used to cast to MostDerived.
Chris@16 425 virtual ~state_machine()
Chris@16 426 {
Chris@16 427 terminate_impl( false );
Chris@16 428 }
Chris@16 429
Chris@16 430 void post_event( const event_base_ptr_type & pEvent )
Chris@16 431 {
Chris@16 432 post_event_impl( pEvent );
Chris@16 433 }
Chris@16 434
Chris@16 435 void post_event( const event_base & evt )
Chris@16 436 {
Chris@16 437 post_event_impl( evt );
Chris@16 438 }
Chris@16 439
Chris@16 440 public:
Chris@16 441 //////////////////////////////////////////////////////////////////////////
Chris@16 442 // The following declarations should be protected.
Chris@16 443 // They are only public because many compilers lack template friends.
Chris@16 444 //////////////////////////////////////////////////////////////////////////
Chris@16 445 template<
Chris@16 446 class HistoryContext,
Chris@16 447 detail::orthogonal_position_type orthogonalPosition >
Chris@16 448 void clear_shallow_history()
Chris@16 449 {
Chris@16 450 // If you receive a
Chris@16 451 // "use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'" or
Chris@16 452 // similar compiler error here then you tried to clear shallow history
Chris@16 453 // for a state that does not have shallow history. That is, the state
Chris@16 454 // does not pass either statechart::has_shallow_history or
Chris@16 455 // statechart::has_full_history to its base class template.
Chris@16 456 BOOST_STATIC_ASSERT( HistoryContext::shallow_history::value );
Chris@16 457
Chris@16 458 typedef typename mpl::at_c<
Chris@16 459 typename HistoryContext::inner_initial_list,
Chris@16 460 orthogonalPosition >::type historized_state;
Chris@16 461
Chris@16 462 store_history_impl(
Chris@16 463 shallowHistoryMap_,
Chris@16 464 history_key_type::make_history_key< historized_state >(),
Chris@16 465 0 );
Chris@16 466 }
Chris@16 467
Chris@16 468 template<
Chris@16 469 class HistoryContext,
Chris@16 470 detail::orthogonal_position_type orthogonalPosition >
Chris@16 471 void clear_deep_history()
Chris@16 472 {
Chris@16 473 // If you receive a
Chris@16 474 // "use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'" or
Chris@16 475 // similar compiler error here then you tried to clear deep history for
Chris@16 476 // a state that does not have deep history. That is, the state does not
Chris@16 477 // pass either statechart::has_deep_history or
Chris@16 478 // statechart::has_full_history to its base class template
Chris@16 479 BOOST_STATIC_ASSERT( HistoryContext::deep_history::value );
Chris@16 480
Chris@16 481 typedef typename mpl::at_c<
Chris@16 482 typename HistoryContext::inner_initial_list,
Chris@16 483 orthogonalPosition >::type historized_state;
Chris@16 484
Chris@16 485 store_history_impl(
Chris@16 486 deepHistoryMap_,
Chris@16 487 history_key_type::make_history_key< historized_state >(),
Chris@16 488 0 );
Chris@16 489 }
Chris@16 490
Chris@16 491 const event_base_type * triggering_event() const
Chris@16 492 {
Chris@16 493 return pTriggeringEvent_;
Chris@16 494 }
Chris@16 495
Chris@16 496 public:
Chris@16 497 //////////////////////////////////////////////////////////////////////////
Chris@16 498 // The following declarations should be private.
Chris@16 499 // They are only public because many compilers lack template friends.
Chris@16 500 //////////////////////////////////////////////////////////////////////////
Chris@16 501 typedef MostDerived inner_context_type;
Chris@16 502 typedef mpl::integral_c< detail::orthogonal_position_type, 0 >
Chris@16 503 inner_orthogonal_position;
Chris@16 504 typedef mpl::integral_c< detail::orthogonal_position_type, 1 >
Chris@16 505 no_of_orthogonal_regions;
Chris@16 506
Chris@16 507 typedef MostDerived outermost_context_type;
Chris@16 508 typedef state_machine outermost_context_base_type;
Chris@16 509 typedef state_machine * inner_context_ptr_type;
Chris@16 510 typedef typename state_base_type::node_state_base_ptr_type
Chris@16 511 node_state_base_ptr_type;
Chris@16 512 typedef typename state_base_type::leaf_state_ptr_type leaf_state_ptr_type;
Chris@16 513 typedef typename state_base_type::state_list_type state_list_type;
Chris@16 514
Chris@16 515 typedef mpl::clear< mpl::list<> >::type context_type_list;
Chris@16 516
Chris@16 517 typedef mpl::bool_< false > shallow_history;
Chris@16 518 typedef mpl::bool_< false > deep_history;
Chris@16 519 typedef mpl::bool_< false > inherited_deep_history;
Chris@16 520
Chris@16 521 void post_event_impl( const event_base_ptr_type & pEvent )
Chris@16 522 {
Chris@16 523 BOOST_ASSERT( get_pointer( pEvent ) != 0 );
Chris@16 524 eventQueue_.push_back( pEvent );
Chris@16 525 }
Chris@16 526
Chris@16 527 void post_event_impl( const event_base & evt )
Chris@16 528 {
Chris@16 529 post_event_impl( evt.intrusive_from_this() );
Chris@16 530 }
Chris@16 531
Chris@16 532 detail::reaction_result react_impl(
Chris@16 533 const event_base_type &,
Chris@16 534 typename rtti_policy_type::id_type )
Chris@16 535 {
Chris@16 536 return detail::do_forward_event;
Chris@16 537 }
Chris@16 538
Chris@16 539 void exit_impl(
Chris@16 540 inner_context_ptr_type &,
Chris@16 541 typename state_base_type::node_state_base_ptr_type &,
Chris@16 542 bool ) {}
Chris@16 543
Chris@16 544 void set_outermost_unstable_state(
Chris@16 545 typename state_base_type::node_state_base_ptr_type &
Chris@16 546 pOutermostUnstableState )
Chris@16 547 {
Chris@16 548 pOutermostUnstableState = 0;
Chris@16 549 }
Chris@16 550
Chris@16 551 // Returns a reference to the context identified by the template
Chris@16 552 // parameter. This can either be _this_ object or one of its direct or
Chris@16 553 // indirect contexts.
Chris@16 554 template< class Context >
Chris@16 555 Context & context()
Chris@16 556 {
Chris@16 557 // As we are in the outermost context here, only this object can be
Chris@16 558 // returned.
Chris@16 559 return *polymorphic_downcast< MostDerived * >( this );
Chris@16 560 }
Chris@16 561
Chris@16 562 template< class Context >
Chris@16 563 const Context & context() const
Chris@16 564 {
Chris@16 565 // As we are in the outermost context here, only this object can be
Chris@16 566 // returned.
Chris@16 567 return *polymorphic_downcast< const MostDerived * >( this );
Chris@16 568 }
Chris@16 569
Chris@16 570 outermost_context_type & outermost_context()
Chris@16 571 {
Chris@16 572 return *polymorphic_downcast< MostDerived * >( this );
Chris@16 573 }
Chris@16 574
Chris@16 575 const outermost_context_type & outermost_context() const
Chris@16 576 {
Chris@16 577 return *polymorphic_downcast< const MostDerived * >( this );
Chris@16 578 }
Chris@16 579
Chris@16 580 outermost_context_base_type & outermost_context_base()
Chris@16 581 {
Chris@16 582 return *this;
Chris@16 583 }
Chris@16 584
Chris@16 585 const outermost_context_base_type & outermost_context_base() const
Chris@16 586 {
Chris@16 587 return *this;
Chris@16 588 }
Chris@16 589
Chris@16 590 void terminate_as_reaction( state_base_type & theState )
Chris@16 591 {
Chris@16 592 terminate_impl( theState, performFullExit_ );
Chris@16 593 pOutermostUnstableState_ = 0;
Chris@16 594 }
Chris@16 595
Chris@16 596 void terminate_as_part_of_transit( state_base_type & theState )
Chris@16 597 {
Chris@16 598 terminate_impl( theState, performFullExit_ );
Chris@16 599 isInnermostCommonOuter_ = true;
Chris@16 600 }
Chris@16 601
Chris@16 602 void terminate_as_part_of_transit( state_machine & )
Chris@16 603 {
Chris@16 604 terminate_impl( *pOutermostState_, performFullExit_ );
Chris@16 605 isInnermostCommonOuter_ = true;
Chris@16 606 }
Chris@16 607
Chris@16 608
Chris@16 609 template< class State >
Chris@16 610 void add( const intrusive_ptr< State > & pState )
Chris@16 611 {
Chris@16 612 // The second dummy argument is necessary because the call to the
Chris@16 613 // overloaded function add_impl would otherwise be ambiguous.
Chris@16 614 node_state_base_ptr_type pNewOutermostUnstableStateCandidate =
Chris@16 615 add_impl( pState, *pState );
Chris@16 616
Chris@16 617 if ( isInnermostCommonOuter_ ||
Chris@16 618 ( is_in_highest_orthogonal_region< State >() &&
Chris@16 619 ( get_pointer( pOutermostUnstableState_ ) ==
Chris@16 620 pState->State::outer_state_ptr() ) ) )
Chris@16 621 {
Chris@16 622 isInnermostCommonOuter_ = false;
Chris@16 623 pOutermostUnstableState_ = pNewOutermostUnstableStateCandidate;
Chris@16 624 }
Chris@16 625 }
Chris@16 626
Chris@16 627
Chris@16 628 void add_inner_state(
Chris@16 629 detail::orthogonal_position_type position,
Chris@16 630 state_base_type * pOutermostState )
Chris@16 631 {
Chris@16 632 BOOST_ASSERT( position == 0 );
Chris@16 633 detail::avoid_unused_warning( position );
Chris@16 634 pOutermostState_ = pOutermostState;
Chris@16 635 }
Chris@16 636
Chris@16 637 void remove_inner_state( detail::orthogonal_position_type position )
Chris@16 638 {
Chris@16 639 BOOST_ASSERT( position == 0 );
Chris@16 640 detail::avoid_unused_warning( position );
Chris@16 641 pOutermostState_ = 0;
Chris@16 642 }
Chris@16 643
Chris@16 644
Chris@16 645 void release_events()
Chris@16 646 {
Chris@16 647 eventQueue_.splice( eventQueue_.begin(), deferredEventQueue_ );
Chris@16 648 }
Chris@16 649
Chris@16 650
Chris@16 651 template< class HistorizedState >
Chris@16 652 void store_shallow_history()
Chris@16 653 {
Chris@16 654 // 5.2.10.6 declares that reinterpret_casting a function pointer to a
Chris@16 655 // different function pointer and back must yield the same value. The
Chris@16 656 // following reinterpret_cast is the first half of such a sequence.
Chris@16 657 store_history_impl(
Chris@16 658 shallowHistoryMap_,
Chris@16 659 history_key_type::make_history_key< HistorizedState >(),
Chris@16 660 reinterpret_cast< void (*)() >( &HistorizedState::deep_construct ) );
Chris@16 661 }
Chris@16 662
Chris@16 663 template< class DefaultState >
Chris@16 664 void construct_with_shallow_history(
Chris@16 665 const typename DefaultState::context_ptr_type & pContext )
Chris@16 666 {
Chris@16 667 construct_with_history_impl< DefaultState >(
Chris@16 668 shallowHistoryMap_, pContext );
Chris@16 669 }
Chris@16 670
Chris@16 671
Chris@16 672 template< class HistorizedState, class LeafState >
Chris@16 673 void store_deep_history()
Chris@16 674 {
Chris@16 675 typedef typename detail::make_context_list<
Chris@16 676 typename HistorizedState::context_type,
Chris@16 677 LeafState >::type history_context_list;
Chris@16 678 typedef detail::constructor<
Chris@16 679 history_context_list, outermost_context_base_type > constructor_type;
Chris@16 680 // 5.2.10.6 declares that reinterpret_casting a function pointer to a
Chris@16 681 // different function pointer and back must yield the same value. The
Chris@16 682 // following reinterpret_cast is the first half of such a sequence.
Chris@16 683 store_history_impl(
Chris@16 684 deepHistoryMap_,
Chris@16 685 history_key_type::make_history_key< HistorizedState >(),
Chris@16 686 reinterpret_cast< void (*)() >( &constructor_type::construct ) );
Chris@16 687 }
Chris@16 688
Chris@16 689 template< class DefaultState >
Chris@16 690 void construct_with_deep_history(
Chris@16 691 const typename DefaultState::context_ptr_type & pContext )
Chris@16 692 {
Chris@16 693 construct_with_history_impl< DefaultState >(
Chris@16 694 deepHistoryMap_, pContext );
Chris@16 695 }
Chris@16 696
Chris@16 697 private: // implementation
Chris@16 698 //////////////////////////////////////////////////////////////////////////
Chris@16 699 void initial_construct()
Chris@16 700 {
Chris@16 701 InitialState::initial_deep_construct(
Chris@16 702 *polymorphic_downcast< MostDerived * >( this ) );
Chris@16 703 }
Chris@16 704
Chris@16 705 class initial_construct_function
Chris@16 706 {
Chris@16 707 public:
Chris@16 708 //////////////////////////////////////////////////////////////////////
Chris@16 709 initial_construct_function( state_machine & machine ) :
Chris@16 710 machine_( machine )
Chris@16 711 {
Chris@16 712 }
Chris@16 713
Chris@16 714 result operator()()
Chris@16 715 {
Chris@16 716 machine_.initial_construct();
Chris@16 717 return detail::result_utility::make_result(
Chris@16 718 detail::do_discard_event ); // there is nothing to be consumed
Chris@16 719 }
Chris@16 720
Chris@16 721 private:
Chris@16 722 //////////////////////////////////////////////////////////////////////
Chris@16 723 // avoids C4512 (assignment operator could not be generated)
Chris@16 724 initial_construct_function & operator=(
Chris@16 725 const initial_construct_function & );
Chris@16 726
Chris@16 727 state_machine & machine_;
Chris@16 728 };
Chris@16 729 friend class initial_construct_function;
Chris@16 730
Chris@16 731 class terminate_function
Chris@16 732 {
Chris@16 733 public:
Chris@16 734 //////////////////////////////////////////////////////////////////////
Chris@16 735 terminate_function( state_machine & machine ) : machine_( machine ) {}
Chris@16 736
Chris@16 737 result operator()()
Chris@16 738 {
Chris@16 739 machine_.terminate_impl( true );
Chris@16 740 return detail::result_utility::make_result(
Chris@16 741 detail::do_discard_event ); // there is nothing to be consumed
Chris@16 742 }
Chris@16 743
Chris@16 744 private:
Chris@16 745 //////////////////////////////////////////////////////////////////////
Chris@16 746 // avoids C4512 (assignment operator could not be generated)
Chris@16 747 terminate_function & operator=( const terminate_function & );
Chris@16 748
Chris@16 749 state_machine & machine_;
Chris@16 750 };
Chris@16 751 friend class terminate_function;
Chris@16 752
Chris@16 753 template< class ExceptionEvent >
Chris@16 754 detail::reaction_result handle_exception_event(
Chris@16 755 const ExceptionEvent & exceptionEvent,
Chris@16 756 state_base_type * pCurrentState )
Chris@16 757 {
Chris@16 758 if ( terminated() )
Chris@16 759 {
Chris@16 760 // there is no state that could handle the exception -> bail out
Chris@16 761 throw;
Chris@16 762 }
Chris@16 763
Chris@16 764 // If we are stable, an event handler has thrown.
Chris@16 765 // Otherwise, either a state constructor, a transition action or an exit
Chris@16 766 // function has thrown and the state machine is now in an invalid state.
Chris@16 767 // This situation can be resolved by the exception event handler
Chris@16 768 // function by orderly transiting to another state or terminating.
Chris@16 769 // As a result of this, the machine must not be unstable when this
Chris@16 770 // function is left.
Chris@16 771 state_base_type * const pOutermostUnstableState =
Chris@16 772 get_pointer( pOutermostUnstableState_ );
Chris@16 773 state_base_type * const pHandlingState = pOutermostUnstableState == 0 ?
Chris@16 774 pCurrentState : pOutermostUnstableState;
Chris@16 775
Chris@16 776 BOOST_ASSERT( pHandlingState != 0 );
Chris@16 777 terminator guard( *this, &exceptionEvent );
Chris@16 778 // There is another scope guard up the call stack, which will terminate
Chris@16 779 // the machine. So this guard only sets the triggering event.
Chris@16 780 guard.dismiss();
Chris@16 781
Chris@16 782 // Setting a member variable to a special value for the duration of a
Chris@16 783 // call surely looks like a kludge (normally it should be a parameter of
Chris@16 784 // the call). However, in this case it is unavoidable because the call
Chris@16 785 // below could result in a call to user code where passing through an
Chris@16 786 // additional bool parameter is not acceptable.
Chris@16 787 performFullExit_ = false;
Chris@16 788 const detail::reaction_result reactionResult = pHandlingState->react_impl(
Chris@16 789 exceptionEvent, exceptionEvent.dynamic_type() );
Chris@16 790 // If the above call throws then performFullExit_ will obviously not be
Chris@16 791 // set back to true. In this case the termination triggered by the
Chris@16 792 // scope guard further up in the call stack will take care of this.
Chris@16 793 performFullExit_ = true;
Chris@16 794
Chris@16 795 if ( ( reactionResult != detail::do_discard_event ) ||
Chris@16 796 ( get_pointer( pOutermostUnstableState_ ) != 0 ) )
Chris@16 797 {
Chris@16 798 throw;
Chris@16 799 }
Chris@16 800
Chris@16 801 return detail::do_discard_event;
Chris@16 802 }
Chris@16 803
Chris@16 804 class exception_event_handler
Chris@16 805 {
Chris@16 806 public:
Chris@16 807 //////////////////////////////////////////////////////////////////////
Chris@16 808 exception_event_handler(
Chris@16 809 state_machine & machine,
Chris@16 810 state_base_type * pCurrentState = 0
Chris@16 811 ) :
Chris@16 812 machine_( machine ),
Chris@16 813 pCurrentState_( pCurrentState )
Chris@16 814 {
Chris@16 815 }
Chris@16 816
Chris@16 817 template< class ExceptionEvent >
Chris@16 818 result operator()(
Chris@16 819 const ExceptionEvent & exceptionEvent )
Chris@16 820 {
Chris@16 821 return detail::result_utility::make_result(
Chris@16 822 machine_.handle_exception_event(
Chris@16 823 exceptionEvent, pCurrentState_ ) );
Chris@16 824 }
Chris@16 825
Chris@16 826 private:
Chris@16 827 //////////////////////////////////////////////////////////////////////
Chris@16 828 // avoids C4512 (assignment operator could not be generated)
Chris@16 829 exception_event_handler & operator=(
Chris@16 830 const exception_event_handler & );
Chris@16 831
Chris@16 832 state_machine & machine_;
Chris@16 833 state_base_type * pCurrentState_;
Chris@16 834 };
Chris@16 835 friend class exception_event_handler;
Chris@16 836
Chris@16 837 class terminator
Chris@16 838 {
Chris@16 839 public:
Chris@16 840 //////////////////////////////////////////////////////////////////////
Chris@16 841 terminator(
Chris@16 842 state_machine & machine, const event_base * pNewTriggeringEvent ) :
Chris@16 843 machine_( machine ),
Chris@16 844 pOldTriggeringEvent_(machine_.pTriggeringEvent_),
Chris@16 845 dismissed_( false )
Chris@16 846 {
Chris@16 847 machine_.pTriggeringEvent_ = pNewTriggeringEvent;
Chris@16 848 }
Chris@16 849
Chris@16 850 ~terminator()
Chris@16 851 {
Chris@16 852 if ( !dismissed_ ) { machine_.terminate_impl( false ); }
Chris@16 853 machine_.pTriggeringEvent_ = pOldTriggeringEvent_;
Chris@16 854 }
Chris@16 855
Chris@16 856 void dismiss() { dismissed_ = true; }
Chris@16 857
Chris@16 858 private:
Chris@16 859 //////////////////////////////////////////////////////////////////////
Chris@16 860 // avoids C4512 (assignment operator could not be generated)
Chris@16 861 terminator & operator=( const terminator & );
Chris@16 862
Chris@16 863 state_machine & machine_;
Chris@16 864 const event_base_type * const pOldTriggeringEvent_;
Chris@16 865 bool dismissed_;
Chris@16 866 };
Chris@16 867 friend class terminator;
Chris@16 868
Chris@16 869
Chris@16 870 detail::reaction_result send_event( const event_base_type & evt )
Chris@16 871 {
Chris@16 872 terminator guard( *this, &evt );
Chris@16 873 BOOST_ASSERT( get_pointer( pOutermostUnstableState_ ) == 0 );
Chris@16 874 const typename rtti_policy_type::id_type eventType = evt.dynamic_type();
Chris@16 875 detail::reaction_result reactionResult = detail::do_forward_event;
Chris@16 876
Chris@16 877 for (
Chris@16 878 typename state_list_type::iterator pState = currentStates_.begin();
Chris@16 879 ( reactionResult == detail::do_forward_event ) &&
Chris@16 880 ( pState != currentStatesEnd_ );
Chris@16 881 ++pState )
Chris@16 882 {
Chris@16 883 // CAUTION: The following statement could modify our state list!
Chris@16 884 // We must not continue iterating if the event was consumed
Chris@16 885 reactionResult = detail::result_utility::get_result( translator_(
Chris@16 886 detail::send_function<
Chris@16 887 state_base_type, event_base_type, rtti_policy_type::id_type >(
Chris@16 888 **pState, evt, eventType ),
Chris@16 889 exception_event_handler( *this, get_pointer( *pState ) ) ) );
Chris@16 890 }
Chris@16 891
Chris@16 892 guard.dismiss();
Chris@16 893
Chris@16 894 if ( reactionResult == detail::do_forward_event )
Chris@16 895 {
Chris@16 896 polymorphic_downcast< MostDerived * >( this )->unconsumed_event( evt );
Chris@16 897 }
Chris@16 898
Chris@16 899 return reactionResult;
Chris@16 900 }
Chris@16 901
Chris@16 902
Chris@16 903 void process_queued_events()
Chris@16 904 {
Chris@16 905 while ( !eventQueue_.empty() )
Chris@16 906 {
Chris@16 907 event_base_ptr_type pEvent = eventQueue_.front();
Chris@16 908 eventQueue_.pop_front();
Chris@16 909
Chris@16 910 if ( send_event( *pEvent ) == detail::do_defer_event )
Chris@16 911 {
Chris@16 912 deferredEventQueue_.push_back( pEvent );
Chris@16 913 }
Chris@16 914 }
Chris@16 915 }
Chris@16 916
Chris@16 917
Chris@16 918 void terminate_impl( bool performFullExit )
Chris@16 919 {
Chris@16 920 performFullExit_ = true;
Chris@16 921
Chris@16 922 if ( !terminated() )
Chris@16 923 {
Chris@16 924 terminate_impl( *pOutermostState_, performFullExit );
Chris@16 925 }
Chris@16 926
Chris@16 927 eventQueue_.clear();
Chris@16 928 deferredEventQueue_.clear();
Chris@16 929 shallowHistoryMap_.clear();
Chris@16 930 deepHistoryMap_.clear();
Chris@16 931 }
Chris@16 932
Chris@16 933 void terminate_impl( state_base_type & theState, bool performFullExit )
Chris@16 934 {
Chris@16 935 isInnermostCommonOuter_ = false;
Chris@16 936
Chris@16 937 // If pOutermostUnstableState_ == 0, we know for sure that
Chris@16 938 // currentStates_.size() > 0, otherwise theState couldn't be alive any
Chris@16 939 // more
Chris@16 940 if ( get_pointer( pOutermostUnstableState_ ) != 0 )
Chris@16 941 {
Chris@16 942 theState.remove_from_state_list(
Chris@16 943 currentStatesEnd_, pOutermostUnstableState_, performFullExit );
Chris@16 944 }
Chris@16 945 // Optimization: We want to find out whether currentStates_ has size 1
Chris@16 946 // and if yes use the optimized implementation below. Since
Chris@16 947 // list<>::size() is implemented quite inefficiently in some std libs
Chris@16 948 // it is best to just decrement the currentStatesEnd_ here and
Chris@16 949 // increment it again, if the test failed.
Chris@16 950 else if ( currentStates_.begin() == --currentStatesEnd_ )
Chris@16 951 {
Chris@16 952 // The machine is stable and there is exactly one innermost state.
Chris@16 953 // The following optimization is only correct for a stable machine
Chris@16 954 // without orthogonal regions.
Chris@16 955 leaf_state_ptr_type & pState = *currentStatesEnd_;
Chris@16 956 pState->exit_impl(
Chris@16 957 pState, pOutermostUnstableState_, performFullExit );
Chris@16 958 }
Chris@16 959 else
Chris@16 960 {
Chris@16 961 BOOST_ASSERT( currentStates_.size() > 1 );
Chris@16 962 // The machine is stable and there are multiple innermost states
Chris@16 963 theState.remove_from_state_list(
Chris@16 964 ++currentStatesEnd_, pOutermostUnstableState_, performFullExit );
Chris@16 965 }
Chris@16 966 }
Chris@16 967
Chris@16 968
Chris@16 969 node_state_base_ptr_type add_impl(
Chris@16 970 const leaf_state_ptr_type & pState,
Chris@16 971 detail::leaf_state< allocator_type, rtti_policy_type > & )
Chris@16 972 {
Chris@16 973 if ( currentStatesEnd_ == currentStates_.end() )
Chris@16 974 {
Chris@16 975 pState->set_list_position(
Chris@16 976 currentStates_.insert( currentStatesEnd_, pState ) );
Chris@16 977 }
Chris@16 978 else
Chris@16 979 {
Chris@16 980 *currentStatesEnd_ = pState;
Chris@16 981 pState->set_list_position( currentStatesEnd_ );
Chris@16 982 ++currentStatesEnd_;
Chris@16 983 }
Chris@16 984
Chris@16 985 return 0;
Chris@16 986 }
Chris@16 987
Chris@16 988 node_state_base_ptr_type add_impl(
Chris@16 989 const node_state_base_ptr_type & pState,
Chris@16 990 state_base_type & )
Chris@16 991 {
Chris@16 992 return pState;
Chris@16 993 }
Chris@16 994
Chris@16 995 template< class State >
Chris@16 996 static bool is_in_highest_orthogonal_region()
Chris@16 997 {
Chris@16 998 return mpl::equal_to<
Chris@16 999 typename State::orthogonal_position,
Chris@16 1000 mpl::minus<
Chris@16 1001 typename State::context_type::no_of_orthogonal_regions,
Chris@16 1002 mpl::integral_c< detail::orthogonal_position_type, 1 > >
Chris@16 1003 >::value;
Chris@16 1004 }
Chris@16 1005
Chris@16 1006
Chris@16 1007 typedef detail::history_key< rtti_policy_type > history_key_type;
Chris@16 1008
Chris@16 1009 typedef std::map<
Chris@16 1010 history_key_type, void (*)(),
Chris@16 1011 std::less< history_key_type >,
Chris@16 1012 typename boost::detail::allocator::rebind_to<
Chris@16 1013 allocator_type, std::pair< const history_key_type, void (*)() >
Chris@16 1014 >::type
Chris@16 1015 > history_map_type;
Chris@16 1016
Chris@16 1017 void store_history_impl(
Chris@16 1018 history_map_type & historyMap,
Chris@16 1019 const history_key_type & historyId,
Chris@16 1020 void (*pConstructFunction)() )
Chris@16 1021 {
Chris@16 1022 historyMap[ historyId ] = pConstructFunction;
Chris@16 1023 }
Chris@16 1024
Chris@16 1025 template< class DefaultState >
Chris@16 1026 void construct_with_history_impl(
Chris@16 1027 history_map_type & historyMap,
Chris@16 1028 const typename DefaultState::context_ptr_type & pContext )
Chris@16 1029 {
Chris@16 1030 typename history_map_type::iterator pFoundSlot = historyMap.find(
Chris@16 1031 history_key_type::make_history_key< DefaultState >() );
Chris@16 1032
Chris@16 1033 if ( ( pFoundSlot == historyMap.end() ) || ( pFoundSlot->second == 0 ) )
Chris@16 1034 {
Chris@16 1035 // We have never entered this state before or history was cleared
Chris@16 1036 DefaultState::deep_construct(
Chris@16 1037 pContext, *polymorphic_downcast< MostDerived * >( this ) );
Chris@16 1038 }
Chris@16 1039 else
Chris@16 1040 {
Chris@16 1041 typedef void construct_function(
Chris@16 1042 const typename DefaultState::context_ptr_type &,
Chris@16 1043 typename DefaultState::outermost_context_base_type & );
Chris@16 1044 // 5.2.10.6 declares that reinterpret_casting a function pointer to a
Chris@16 1045 // different function pointer and back must yield the same value. The
Chris@16 1046 // following reinterpret_cast is the second half of such a sequence.
Chris@16 1047 construct_function * const pConstructFunction =
Chris@16 1048 reinterpret_cast< construct_function * >( pFoundSlot->second );
Chris@16 1049 (*pConstructFunction)(
Chris@16 1050 pContext, *polymorphic_downcast< MostDerived * >( this ) );
Chris@16 1051 }
Chris@16 1052 }
Chris@16 1053
Chris@16 1054 typedef std::list<
Chris@16 1055 event_base_ptr_type,
Chris@16 1056 typename boost::detail::allocator::rebind_to<
Chris@16 1057 allocator_type, event_base_ptr_type >::type
Chris@16 1058 > event_queue_type;
Chris@16 1059
Chris@16 1060 typedef std::map<
Chris@16 1061 const state_base_type *, event_queue_type,
Chris@16 1062 std::less< const state_base_type * >,
Chris@16 1063 typename boost::detail::allocator::rebind_to<
Chris@16 1064 allocator_type,
Chris@16 1065 std::pair< const state_base_type * const, event_queue_type >
Chris@16 1066 >::type
Chris@16 1067 > deferred_map_type;
Chris@16 1068
Chris@16 1069
Chris@16 1070 event_queue_type eventQueue_;
Chris@16 1071 event_queue_type deferredEventQueue_;
Chris@16 1072 state_list_type currentStates_;
Chris@16 1073 typename state_list_type::iterator currentStatesEnd_;
Chris@16 1074 state_base_type * pOutermostState_;
Chris@16 1075 bool isInnermostCommonOuter_;
Chris@16 1076 node_state_base_ptr_type pOutermostUnstableState_;
Chris@16 1077 ExceptionTranslator translator_;
Chris@16 1078 bool performFullExit_;
Chris@16 1079 history_map_type shallowHistoryMap_;
Chris@16 1080 history_map_type deepHistoryMap_;
Chris@16 1081 const event_base_type * pTriggeringEvent_;
Chris@16 1082 };
Chris@16 1083
Chris@16 1084
Chris@16 1085
Chris@16 1086 } // namespace statechart
Chris@16 1087 } // namespace boost
Chris@16 1088
Chris@16 1089
Chris@16 1090
Chris@16 1091 #endif