| 1 | // Copyright 2008 Christophe Henry |
| 2 | // henry UNDERSCORE christophe AT hotmail DOT com |
| 3 | // This is an extended version of the state machine available in the boost::mpl library |
| 4 | // Distributed under the same license as the original. |
| 5 | // Copyright for the original version: |
| 6 | // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed |
| 7 | // under the Boost Software License, Version 1.0. (See accompanying |
| 8 | // file LICENSE_1_0.txt or copy at |
| 9 | // http://www.boost.org/LICENSE_1_0.txt) |
| 10 | |
| 11 | #ifndef BOOST_MSM_BACK11_METAFUNCTIONS_H |
| 12 | #define BOOST_MSM_BACK11_METAFUNCTIONS_H |
| 13 | |
| 14 | #include <algorithm> |
| 15 | |
| 16 | #include <boost/mpl/set.hpp> |
| 17 | #include <boost/mpl/at.hpp> |
| 18 | #include <boost/mpl/pair.hpp> |
| 19 | #include <boost/mpl/map.hpp> |
| 20 | #include <boost/mpl/int.hpp> |
| 21 | #include <boost/mpl/has_xxx.hpp> |
| 22 | #include <boost/mpl/find.hpp> |
| 23 | #include <boost/mpl/count_if.hpp> |
| 24 | #include <boost/mpl/fold.hpp> |
| 25 | #include <boost/mpl/if.hpp> |
| 26 | #include <boost/mpl/has_key.hpp> |
| 27 | #include <boost/mpl/insert.hpp> |
| 28 | #include <boost/mpl/next_prior.hpp> |
| 29 | #include <boost/mpl/map.hpp> |
| 30 | #include <boost/mpl/push_back.hpp> |
| 31 | #include <boost/mpl/is_sequence.hpp> |
| 32 | #include <boost/mpl/size.hpp> |
| 33 | #include <boost/mpl/transform.hpp> |
| 34 | #include <boost/mpl/begin_end.hpp> |
| 35 | #include <boost/mpl/bool.hpp> |
| 36 | #include <boost/mpl/empty.hpp> |
| 37 | #include <boost/mpl/identity.hpp> |
| 38 | #include <boost/mpl/eval_if.hpp> |
| 39 | #include <boost/mpl/insert_range.hpp> |
| 40 | #include <boost/mpl/front.hpp> |
| 41 | #include <boost/mpl/logical.hpp> |
| 42 | #include <boost/mpl/plus.hpp> |
| 43 | #include <boost/mpl/copy_if.hpp> |
| 44 | #include <boost/mpl/back_inserter.hpp> |
| 45 | #include <boost/mpl/transform.hpp> |
| 46 | |
| 47 | #include <boost/fusion/include/mpl.hpp> |
| 48 | #include <boost/fusion/container/vector/convert.hpp> |
| 49 | #include <boost/fusion/include/as_vector.hpp> |
| 50 | #include <boost/fusion/include/as_set.hpp> |
| 51 | #include <boost/fusion/container/vector.hpp> |
| 52 | #include <boost/fusion/include/set.hpp> |
| 53 | #include <boost/fusion/include/map.hpp> |
| 54 | #include <boost/fusion/include/pair.hpp> |
| 55 | #include <boost/fusion/include/at_key.hpp> |
| 56 | #include <boost/fusion/include/for_each.hpp> |
| 57 | #include <boost/fusion/include/make_vector.hpp> |
| 58 | #include <boost/fusion/include/has_key.hpp> |
| 59 | #include <boost/fusion/include/make_set.hpp> |
| 60 | #include <boost/fusion/include/value_at_key.hpp> |
| 61 | #include <boost/fusion/include/front.hpp> |
| 62 | |
| 63 | #include <boost/type_traits/is_same.hpp> |
| 64 | #include <boost/utility/enable_if.hpp> |
| 65 | |
| 66 | #include <boost/msm/row_tags.hpp> |
| 67 | |
| 68 | // mpl_graph graph implementation and depth first search |
| 69 | #include <boost/msm/mpl_graph/incidence_list_graph.hpp> |
| 70 | #include <boost/msm/mpl_graph/depth_first_search.hpp> |
| 71 | |
| 72 | BOOST_MPL_HAS_XXX_TRAIT_DEF(explicit_creation) |
| 73 | BOOST_MPL_HAS_XXX_TRAIT_DEF(pseudo_entry) |
| 74 | BOOST_MPL_HAS_XXX_TRAIT_DEF(pseudo_exit) |
| 75 | BOOST_MPL_HAS_XXX_TRAIT_DEF(concrete_exit_state) |
| 76 | BOOST_MPL_HAS_XXX_TRAIT_DEF(composite_tag) |
| 77 | BOOST_MPL_HAS_XXX_TRAIT_DEF(not_real_row_tag) |
| 78 | BOOST_MPL_HAS_XXX_TRAIT_DEF(event_blocking_flag) |
| 79 | BOOST_MPL_HAS_XXX_TRAIT_DEF(explicit_entry_state) |
| 80 | BOOST_MPL_HAS_XXX_TRAIT_DEF(completion_event) |
| 81 | BOOST_MPL_HAS_XXX_TRAIT_DEF(no_exception_thrown) |
| 82 | BOOST_MPL_HAS_XXX_TRAIT_DEF(no_message_queue) |
| 83 | BOOST_MPL_HAS_XXX_TRAIT_DEF(activate_deferred_events) |
| 84 | BOOST_MPL_HAS_XXX_TRAIT_DEF(wrapped_entry) |
| 85 | BOOST_MPL_HAS_XXX_TRAIT_DEF(active_state_switch_policy) |
| 86 | |
| 87 | namespace boost { namespace msm { namespace back11 |
| 88 | { |
| 89 | template <typename Sequence, typename Range> |
| 90 | struct set_insert_range |
| 91 | { |
| 92 | typedef typename ::boost::mpl::fold< |
| 93 | Range,Sequence, |
| 94 | ::boost::mpl::insert< ::boost::mpl::placeholders::_1, ::boost::mpl::placeholders::_2 > |
| 95 | >::type type; |
| 96 | }; |
| 97 | |
| 98 | // returns the current state type of a transition |
| 99 | template <class Transition> |
| 100 | struct transition_source_type |
| 101 | { |
| 102 | typedef typename Transition::current_state_type type; |
| 103 | }; |
| 104 | |
| 105 | // returns the target state type of a transition |
| 106 | template <class Transition> |
| 107 | struct transition_target_type |
| 108 | { |
| 109 | typedef typename Transition::next_state_type type; |
| 110 | }; |
| 111 | |
| 112 | // helper functions for generate_state_ids |
| 113 | // create a pair of a state and a passed id for source and target states |
| 114 | template <class Id,class Transition> |
| 115 | struct make_pair_source_state_id |
| 116 | { |
| 117 | typedef typename ::boost::fusion::pair<typename Transition::current_state_type,Id> type; |
| 118 | }; |
| 119 | template <class Id,class Transition> |
| 120 | struct make_pair_target_state_id |
| 121 | { |
| 122 | typedef typename ::boost::fusion::pair<typename Transition::next_state_type,Id> type; |
| 123 | }; |
| 124 | // iterates through a transition table and automatically generates ids starting at 0 |
| 125 | // first the source states, transition up to down |
| 126 | // then the target states, up to down |
| 127 | template <class stt> |
| 128 | struct generate_state_ids |
| 129 | { |
| 130 | typedef typename |
| 131 | ::boost::mpl::fold< |
| 132 | stt,::boost::mpl::pair< ::boost::fusion::map< >, ::boost::mpl::int_<0> >, |
| 133 | ::boost::mpl::pair< |
| 134 | ::boost::mpl::if_< |
| 135 | ::boost::mpl::has_key< ::boost::mpl::first< ::boost::mpl::placeholders::_1>, |
| 136 | transition_source_type< ::boost::mpl::placeholders::_2> >, |
| 137 | ::boost::mpl::first< ::boost::mpl::placeholders::_1>, |
| 138 | boost::fusion::result_of::as_map<boost::fusion::result_of::insert< |
| 139 | ::boost::mpl::first<mpl::placeholders::_1>, |
| 140 | ::boost::fusion::result_of::end<::boost::mpl::first<mpl::placeholders::_1> >, |
| 141 | make_pair_source_state_id< ::boost::mpl::second< ::boost::mpl::placeholders::_1 >, |
| 142 | ::boost::mpl::placeholders::_2> > > |
| 143 | >, |
| 144 | ::boost::mpl::if_< |
| 145 | ::boost::mpl::has_key< ::boost::mpl::first< ::boost::mpl::placeholders::_1>, |
| 146 | transition_source_type< ::boost::mpl::placeholders::_2> >, |
| 147 | ::boost::mpl::second< ::boost::mpl::placeholders::_1 >, |
| 148 | ::boost::mpl::next< ::boost::mpl::second<mpl::placeholders::_1 > > |
| 149 | > |
| 150 | > //pair |
| 151 | >::type source_state_ids; |
| 152 | |
| 153 | typedef typename ::boost::mpl::first<source_state_ids>::type source_state_map; |
| 154 | typedef typename ::boost::mpl::second<source_state_ids>::type highest_state_id; |
| 155 | |
| 156 | |
| 157 | typedef typename |
| 158 | ::boost::mpl::fold< |
| 159 | stt,::boost::mpl::pair< source_state_map, highest_state_id >, |
| 160 | ::boost::mpl::pair< |
| 161 | ::boost::mpl::if_< |
| 162 | ::boost::mpl::has_key< ::boost::mpl::first< ::boost::mpl::placeholders::_1>, |
| 163 | transition_target_type< ::boost::mpl::placeholders::_2> >, |
| 164 | ::boost::mpl::first< ::boost::mpl::placeholders::_1>, |
| 165 | boost::fusion::result_of::as_map<boost::fusion::result_of::insert< |
| 166 | ::boost::mpl::first<mpl::placeholders::_1>, |
| 167 | ::boost::fusion::result_of::end<::boost::mpl::first<mpl::placeholders::_1> >, |
| 168 | make_pair_target_state_id< ::boost::mpl::second< ::boost::mpl::placeholders::_1 >, |
| 169 | ::boost::mpl::placeholders::_2> > > |
| 170 | >, |
| 171 | ::boost::mpl::if_< |
| 172 | ::boost::mpl::has_key< ::boost::mpl::first< ::boost::mpl::placeholders::_1>, |
| 173 | transition_target_type< ::boost::mpl::placeholders::_2> >, |
| 174 | ::boost::mpl::second< ::boost::mpl::placeholders::_1 >, |
| 175 | ::boost::mpl::next< ::boost::mpl::second<mpl::placeholders::_1 > > |
| 176 | > |
| 177 | > //pair |
| 178 | >::type all_state_ids; |
| 179 | typedef typename ::boost::mpl::first<all_state_ids>::type type; |
| 180 | |
| 181 | }; |
| 182 | |
| 183 | template <class Fsm> |
| 184 | struct get_active_state_switch_policy_helper |
| 185 | { |
| 186 | typedef typename Fsm::active_state_switch_policy type; |
| 187 | }; |
| 188 | template <class Iter> |
| 189 | struct get_active_state_switch_policy_helper2 |
| 190 | { |
| 191 | typedef typename boost::mpl::deref<Iter>::type Fsm; |
| 192 | typedef typename Fsm::active_state_switch_policy type; |
| 193 | }; |
| 194 | // returns the active state switching policy |
| 195 | template <class Fsm> |
| 196 | struct get_active_state_switch_policy |
| 197 | { |
| 198 | typedef typename ::boost::mpl::find_if< |
| 199 | typename Fsm::configuration, |
| 200 | has_active_state_switch_policy< ::boost::mpl::placeholders::_1 > >::type iter; |
| 201 | |
| 202 | typedef typename ::boost::mpl::eval_if< |
| 203 | typename ::boost::is_same< |
| 204 | iter, |
| 205 | typename ::boost::mpl::end<typename Fsm::configuration>::type |
| 206 | >::type, |
| 207 | get_active_state_switch_policy_helper<Fsm>, |
| 208 | get_active_state_switch_policy_helper2< iter > |
| 209 | >::type type; |
| 210 | }; |
| 211 | |
| 212 | // returns the id of a given state |
| 213 | template <class stt,class State> |
| 214 | struct get_state_id |
| 215 | { |
| 216 | typedef typename ::boost::fusion::result_of::value_at_key<typename generate_state_ids<stt>::type,State>::type type; |
| 217 | enum {value = type::value}; |
| 218 | }; |
| 219 | |
| 220 | // returns a fusion::vector containing the init states of a state machine |
| 221 | template <class States> |
| 222 | struct get_initial_states |
| 223 | { |
| 224 | typedef typename ::boost::mpl::if_< |
| 225 | ::boost::mpl::is_sequence<States>, |
| 226 | States, |
| 227 | typename ::boost::mpl::push_back< ::boost::fusion::vector<>,States>::type >::type type; |
| 228 | }; |
| 229 | // returns a mpl::int_ containing the size of a region. If the argument is not a sequence, returns 1 |
| 230 | template <class region> |
| 231 | struct get_number_of_regions |
| 232 | { |
| 233 | typedef typename mpl::if_< |
| 234 | ::boost::mpl::is_sequence<region>, |
| 235 | ::boost::mpl::size<region>, |
| 236 | ::boost::mpl::int_<1> >::type type; |
| 237 | }; |
| 238 | |
| 239 | // builds a fusion::vector of initial states |
| 240 | //TODO remove duplicate from get_initial_states |
| 241 | template <class region> |
| 242 | struct get_regions_as_sequence |
| 243 | { |
| 244 | typedef typename ::boost::mpl::if_< |
| 245 | ::boost::mpl::is_sequence<region>, |
| 246 | region, |
| 247 | typename ::boost::mpl::push_back< ::boost::fusion::vector0<>,region>::type >::type type; |
| 248 | }; |
| 249 | |
| 250 | template <class ToCreateSeq> |
| 251 | struct get_explicit_creation_as_sequence |
| 252 | { |
| 253 | typedef typename ::boost::mpl::if_< |
| 254 | ::boost::mpl::is_sequence<ToCreateSeq>, |
| 255 | ToCreateSeq, |
| 256 | typename ::boost::mpl::push_back< ::boost::fusion::vector0<>,ToCreateSeq>::type >::type type; |
| 257 | }; |
| 258 | |
| 259 | // returns true if 2 transitions have the same source (used to remove duplicates in search of composite states) |
| 260 | template <class stt,class Transition1,class Transition2> |
| 261 | struct have_same_source |
| 262 | { |
| 263 | enum {current_state1 = get_state_id<stt,typename Transition1::current_state_type >::type::value}; |
| 264 | enum {current_state2 = get_state_id<stt,typename Transition2::current_state_type >::type::value}; |
| 265 | enum {value = ((int)current_state1 == (int)current_state2) }; |
| 266 | }; |
| 267 | |
| 268 | |
| 269 | // A metafunction that returns the Event associated with a transition. |
| 270 | template <class Transition> |
| 271 | struct transition_event |
| 272 | { |
| 273 | typedef typename Transition::transition_event type; |
| 274 | }; |
| 275 | |
| 276 | // returns true for composite states |
| 277 | template <class State> |
| 278 | struct is_composite_state |
| 279 | { |
| 280 | enum {value = has_composite_tag<State>::type::value}; |
| 281 | typedef typename has_composite_tag<State>::type type; |
| 282 | }; |
| 283 | |
| 284 | // transform a transition table in a container of source states |
| 285 | template <class stt> |
| 286 | struct keep_source_names |
| 287 | { |
| 288 | // instead of the rows we want only the names of the states (from source) |
| 289 | typedef typename |
| 290 | ::boost::mpl::transform< |
| 291 | stt,transition_source_type< ::boost::mpl::placeholders::_1> >::type type; |
| 292 | }; |
| 293 | |
| 294 | // transform a transition table in a container of target states |
| 295 | template <class stt> |
| 296 | struct keep_target_names |
| 297 | { |
| 298 | // instead of the rows we want only the names of the states (from source) |
| 299 | typedef typename |
| 300 | ::boost::mpl::transform< |
| 301 | stt,transition_target_type< ::boost::mpl::placeholders::_1> >::type type; |
| 302 | }; |
| 303 | |
| 304 | // fusion sets do not guarantee uniqueness so we need to check this before insertion |
| 305 | template <class Set, class T> |
| 306 | struct set_unique_insert |
| 307 | { |
| 308 | typedef typename ::boost::mpl::if_< |
| 309 | ::boost::fusion::result_of::has_key<Set, T>, |
| 310 | Set, |
| 311 | typename ::boost::mpl::push_back<Set, T>::type |
| 312 | >::type type; |
| 313 | }; |
| 314 | |
| 315 | template <class stt> |
| 316 | struct generate_state_set |
| 317 | { |
| 318 | // keep in the original transition table only the source/target state types |
| 319 | typedef typename keep_source_names<stt>::type sources; |
| 320 | typedef typename keep_target_names<stt>::type targets; |
| 321 | |
| 322 | typedef typename |
| 323 | ::boost::mpl::fold< |
| 324 | sources, |
| 325 | boost::fusion::set<>, |
| 326 | boost::msm::back11::set_unique_insert< ::boost::mpl::placeholders::_1, ::boost::mpl::placeholders::_2> |
| 327 | >::type source_set; |
| 328 | |
| 329 | typedef typename |
| 330 | ::boost::mpl::fold< |
| 331 | targets, |
| 332 | source_set, |
| 333 | boost::msm::back11::set_unique_insert< ::boost::mpl::placeholders::_1, ::boost::mpl::placeholders::_2> |
| 334 | >::type type; |
| 335 | }; |
| 336 | |
| 337 | // iterates through the transition table and generate a fusion::set<> containing all the events |
| 338 | template <class stt> |
| 339 | struct generate_event_set |
| 340 | { |
| 341 | typedef typename |
| 342 | ::boost::mpl::fold< |
| 343 | stt, ::boost::fusion::set<>, |
| 344 | ::boost::mpl::if_< |
| 345 | ::boost::mpl::has_key< ::boost::mpl::placeholders::_1, |
| 346 | transition_event< ::boost::mpl::placeholders::_2> >, |
| 347 | ::boost::mpl::placeholders::_1, |
| 348 | ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, |
| 349 | transition_event< ::boost::mpl::placeholders::_2> > > |
| 350 | >::type type; |
| 351 | }; |
| 352 | |
| 353 | // returns a mpl::bool_<true> if State has Event as deferred event |
| 354 | template <class State, class Event> |
| 355 | struct has_state_delayed_event |
| 356 | { |
| 357 | typedef typename ::boost::mpl::find<typename State::deferred_events,Event>::type found; |
| 358 | typedef typename ::boost::mpl::if_< |
| 359 | ::boost::is_same<found,typename ::boost::mpl::end<typename State::deferred_events>::type >, |
| 360 | ::boost::mpl::bool_<false>, |
| 361 | ::boost::mpl::bool_<true> >::type type; |
| 362 | }; |
| 363 | // returns a mpl::bool_<true> if State has any deferred event |
| 364 | template <class State> |
| 365 | struct has_state_delayed_events |
| 366 | { |
| 367 | typedef typename ::boost::mpl::if_< |
| 368 | ::boost::mpl::empty<typename State::deferred_events>, |
| 369 | ::boost::mpl::bool_<false>, |
| 370 | ::boost::mpl::bool_<true> >::type type; |
| 371 | }; |
| 372 | |
| 373 | // Template used to create dummy entries for initial states not found in the stt. |
| 374 | template< typename T1 > |
| 375 | struct not_a_row |
| 376 | { |
| 377 | typedef int not_real_row_tag; |
| 378 | struct dummy_event |
| 379 | { |
| 380 | }; |
| 381 | typedef T1 current_state_type; |
| 382 | typedef T1 next_state_type; |
| 383 | typedef dummy_event transition_event; |
| 384 | }; |
| 385 | |
| 386 | // metafunctions used to find out if a state is entry, exit or something else |
| 387 | template <class State> |
| 388 | struct is_pseudo_entry |
| 389 | { |
| 390 | typedef typename ::boost::mpl::if_< typename has_pseudo_entry<State>::type, |
| 391 | ::boost::mpl::bool_<true>,::boost::mpl::bool_<false> |
| 392 | >::type type; |
| 393 | }; |
| 394 | // says if a state is an exit pseudo state |
| 395 | template <class State> |
| 396 | struct is_pseudo_exit |
| 397 | { |
| 398 | typedef typename ::boost::mpl::if_< typename has_pseudo_exit<State>::type, |
| 399 | ::boost::mpl::bool_<true>, ::boost::mpl::bool_<false> |
| 400 | >::type type; |
| 401 | }; |
| 402 | // says if a state is an entry pseudo state or an explicit entry |
| 403 | template <class State> |
| 404 | struct is_direct_entry |
| 405 | { |
| 406 | typedef typename ::boost::mpl::if_< typename has_explicit_entry_state<State>::type, |
| 407 | ::boost::mpl::bool_<true>, ::boost::mpl::bool_<false> |
| 408 | >::type type; |
| 409 | }; |
| 410 | |
| 411 | //converts a "fake" (simulated in a state_machine_ description )state into one which will really get created |
| 412 | template <class StateType,class CompositeType> |
| 413 | struct convert_fake_state |
| 414 | { |
| 415 | // converts a state (explicit entry) into the state we really are going to create (explicit<>) |
| 416 | typedef typename ::boost::mpl::if_< |
| 417 | typename is_direct_entry<StateType>::type, |
| 418 | typename CompositeType::template direct<StateType>, |
| 419 | typename ::boost::mpl::identity<StateType>::type |
| 420 | >::type type; |
| 421 | }; |
| 422 | |
| 423 | template <class StateType> |
| 424 | struct get_explicit_creation |
| 425 | { |
| 426 | typedef typename StateType::explicit_creation type; |
| 427 | }; |
| 428 | |
| 429 | template <class StateType> |
| 430 | struct get_wrapped_entry |
| 431 | { |
| 432 | typedef typename StateType::wrapped_entry type; |
| 433 | }; |
| 434 | // used for states created with explicit_creation |
| 435 | // if the state is an explicit entry, we reach for the wrapped state |
| 436 | // otherwise, this returns the state itself |
| 437 | template <class StateType> |
| 438 | struct get_wrapped_state |
| 439 | { |
| 440 | typedef typename ::boost::mpl::eval_if< |
| 441 | typename has_wrapped_entry<StateType>::type, |
| 442 | get_wrapped_entry<StateType>, |
| 443 | ::boost::mpl::identity<StateType> >::type type; |
| 444 | }; |
| 445 | |
| 446 | template <class Derived> |
| 447 | struct create_stt |
| 448 | { |
| 449 | //typedef typename Derived::transition_table stt; |
| 450 | typedef typename Derived::real_transition_table Stt; |
| 451 | // get the state set |
| 452 | typedef typename generate_state_set<Stt>::type states; |
| 453 | // transform the initial region(s) in a sequence |
| 454 | typedef typename get_regions_as_sequence<typename Derived::initial_state>::type init_states; |
| 455 | // iterate through the initial states and add them in the stt if not already there |
| 456 | typedef typename |
| 457 | ::boost::mpl::fold< |
| 458 | init_states,Stt, |
| 459 | ::boost::mpl::if_< |
| 460 | ::boost::mpl::has_key<states, ::boost::mpl::placeholders::_2>, |
| 461 | ::boost::mpl::placeholders::_1, |
| 462 | ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, |
| 463 | not_a_row< get_wrapped_state< ::boost::mpl::placeholders::_2> > > |
| 464 | > |
| 465 | >::type with_init; |
| 466 | // do the same for states marked as explicitly created |
| 467 | typedef typename get_explicit_creation_as_sequence< |
| 468 | typename ::boost::mpl::eval_if< |
| 469 | typename has_explicit_creation<Derived>::type, |
| 470 | get_explicit_creation<Derived>, |
| 471 | boost::fusion::result_of::make_vector<> >::type |
| 472 | >::type fake_explicit_created; |
| 473 | |
| 474 | typedef typename |
| 475 | ::boost::mpl::transform< |
| 476 | fake_explicit_created,convert_fake_state< ::boost::mpl::placeholders::_1,Derived> >::type explicit_created; |
| 477 | |
| 478 | typedef typename |
| 479 | ::boost::mpl::fold< |
| 480 | explicit_created,with_init, |
| 481 | ::boost::mpl::if_< |
| 482 | ::boost::mpl::has_key<states, ::boost::mpl::placeholders::_2>, |
| 483 | ::boost::mpl::placeholders::_1, |
| 484 | ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, |
| 485 | not_a_row< get_wrapped_state< ::boost::mpl::placeholders::_2> > > |
| 486 | > |
| 487 | >::type type; |
| 488 | }; |
| 489 | |
| 490 | // returns the transition table of a Composite state |
| 491 | template <class Composite> |
| 492 | struct get_transition_table |
| 493 | { |
| 494 | typedef typename create_stt<Composite>::type type; |
| 495 | }; |
| 496 | |
| 497 | // recursively builds an internal table including those of substates, sub-substates etc. |
| 498 | // variant for submachines |
| 499 | template <class StateType,class IsComposite> |
| 500 | struct recursive_get_internal_transition_table |
| 501 | { |
| 502 | // get the composite's internal table |
| 503 | typedef typename StateType::internal_transition_table11 composite_table; |
| 504 | // and for every substate (state of submachine), recursively get the internal transition table |
| 505 | typedef typename generate_state_set<typename StateType::stt>::type composite_states; |
| 506 | typedef typename ::boost::mpl::fold< |
| 507 | composite_states, composite_table, |
| 508 | ::boost::fusion::result_of::as_vector< |
| 509 | ::boost::fusion::result_of::insert_range< |
| 510 | ::boost::mpl::placeholders::_1, |
| 511 | ::boost::fusion::result_of::end< ::boost::mpl::placeholders::_1>, |
| 512 | recursive_get_internal_transition_table< ::boost::mpl::placeholders::_2, is_composite_state< ::boost::mpl::placeholders::_2> > |
| 513 | > |
| 514 | > |
| 515 | >::type type; |
| 516 | }; |
| 517 | // stop iterating on leafs (simple states) |
| 518 | template <class StateType> |
| 519 | struct recursive_get_internal_transition_table<StateType, ::boost::mpl::false_ > |
| 520 | { |
| 521 | typedef typename StateType::internal_transition_table11 type; |
| 522 | }; |
| 523 | // recursively get a transition table for a given composite state. |
| 524 | // returns the transition table for this state + the tables of all composite sub states recursively |
| 525 | template <class Composite> |
| 526 | struct recursive_get_transition_table |
| 527 | { |
| 528 | // get the transition table of the state if it's a state machine |
| 529 | typedef typename ::boost::mpl::eval_if<typename is_composite_state<Composite>::type, |
| 530 | get_transition_table<Composite>, |
| 531 | boost::fusion::result_of::make_vector<> |
| 532 | >::type org_table; |
| 533 | |
| 534 | typedef typename generate_state_set<org_table>::type states; |
| 535 | |
| 536 | // and for every substate, recursively get the transition table if it's a state machine |
| 537 | // typedef typename ::boost::mpl::fold< |
| 538 | // states,org_table, |
| 539 | // ::boost::mpl::insert_range< ::boost::mpl::placeholders::_1, ::boost::mpl::end<mpl::placeholders::_1>, |
| 540 | // recursive_get_transition_table< ::boost::mpl::placeholders::_2 > > |
| 541 | // >::type type; |
| 542 | typedef typename ::boost::mpl::fold< |
| 543 | states,org_table, |
| 544 | ::boost::fusion::result_of::as_vector< |
| 545 | ::boost::fusion::result_of::insert_range< |
| 546 | ::boost::mpl::placeholders::_1, |
| 547 | ::boost::fusion::result_of::end<mpl::placeholders::_1>, |
| 548 | recursive_get_transition_table< ::boost::mpl::placeholders::_2 > |
| 549 | > |
| 550 | > |
| 551 | >::type type; |
| 552 | }; |
| 553 | |
| 554 | // metafunction used to say if a SM has pseudo exit states |
| 555 | template <class Derived> |
| 556 | struct has_fsm_deferred_events |
| 557 | { |
| 558 | typedef typename create_stt<Derived>::type Stt; |
| 559 | typedef typename generate_state_set<Stt>::type state_list; |
| 560 | |
| 561 | typedef typename ::boost::mpl::or_< |
| 562 | typename has_activate_deferred_events<Derived>::type, |
| 563 | ::boost::mpl::bool_< ::boost::mpl::count_if< |
| 564 | typename Derived::configuration, |
| 565 | has_activate_deferred_events< ::boost::mpl::placeholders::_1 > >::value != 0> |
| 566 | >::type found_in_fsm; |
| 567 | |
| 568 | typedef typename ::boost::mpl::or_< |
| 569 | found_in_fsm, |
| 570 | ::boost::mpl::bool_< ::boost::mpl::count_if< |
| 571 | state_list,has_state_delayed_events< |
| 572 | ::boost::mpl::placeholders::_1 > >::value != 0> |
| 573 | >::type type; |
| 574 | }; |
| 575 | |
| 576 | // returns a mpl::bool_<true> if State has any delayed event |
| 577 | template <class Event> |
| 578 | struct is_completion_event |
| 579 | { |
| 580 | typedef typename ::boost::mpl::if_< |
| 581 | has_completion_event<Event>, |
| 582 | ::boost::mpl::bool_<true>, |
| 583 | ::boost::mpl::bool_<false> >::type type; |
| 584 | }; |
| 585 | // metafunction used to say if a SM has eventless transitions |
| 586 | template <class Derived> |
| 587 | struct has_fsm_eventless_transition |
| 588 | { |
| 589 | typedef typename create_stt<Derived>::type Stt; |
| 590 | typedef typename generate_event_set<Stt>::type event_list; |
| 591 | |
| 592 | typedef ::boost::mpl::bool_< ::boost::mpl::count_if< |
| 593 | event_list,is_completion_event< ::boost::mpl::placeholders::_1 > >::value != 0> type; |
| 594 | }; |
| 595 | template <class Derived> |
| 596 | struct find_completion_events |
| 597 | { |
| 598 | typedef typename create_stt<Derived>::type Stt; |
| 599 | typedef typename generate_event_set<Stt>::type event_list; |
| 600 | |
| 601 | typedef typename ::boost::mpl::fold< |
| 602 | event_list, ::boost::fusion::set<>, |
| 603 | ::boost::mpl::if_< |
| 604 | is_completion_event< ::boost::mpl::placeholders::_2>, |
| 605 | boost::msm::back11::set_unique_insert< ::boost::mpl::placeholders::_1, ::boost::mpl::placeholders::_2 >, |
| 606 | ::boost::mpl::placeholders::_1 > |
| 607 | >::type type; |
| 608 | }; |
| 609 | |
| 610 | template <class Transition> |
| 611 | struct make_vector |
| 612 | { |
| 613 | typedef ::boost::fusion::vector<Transition> type; |
| 614 | }; |
| 615 | template< typename Entry > |
| 616 | struct get_first_element_pair_second |
| 617 | { |
| 618 | typedef typename ::boost::mpl::front<typename Entry::second>::type type; |
| 619 | }; |
| 620 | template< typename Entry > |
| 621 | struct get_first_element_pair_second2 |
| 622 | { |
| 623 | //typedef typename ::boost::mpl::front<typename Entry::second>::type type; |
| 624 | typedef typename ::boost::remove_reference<typename ::boost::fusion::result_of::front< |
| 625 | typename ::boost::fusion::result_of::second<Entry>::type >::type>::type type; |
| 626 | }; |
| 627 | |
| 628 | //returns the owner of an explicit_entry state |
| 629 | //which is the containing SM if the transition originates from outside the containing SM |
| 630 | //or else the explicit_entry state itself |
| 631 | template <class State,class ContainingSM> |
| 632 | struct get_owner |
| 633 | { |
| 634 | typedef typename ::boost::mpl::if_< |
| 635 | typename ::boost::mpl::not_<typename ::boost::is_same<typename State::owner, |
| 636 | ContainingSM >::type>::type, |
| 637 | typename State::owner, |
| 638 | State >::type type; |
| 639 | }; |
| 640 | |
| 641 | template <class Sequence,class ContainingSM> |
| 642 | struct get_fork_owner |
| 643 | { |
| 644 | typedef typename ::boost::mpl::front<Sequence>::type seq_front; |
| 645 | typedef typename ::boost::mpl::if_< |
| 646 | typename ::boost::mpl::not_< |
| 647 | typename ::boost::is_same<typename seq_front::owner,ContainingSM>::type>::type, |
| 648 | typename seq_front::owner, |
| 649 | seq_front >::type type; |
| 650 | }; |
| 651 | |
| 652 | template <class StateType,class ContainingSM> |
| 653 | struct make_exit |
| 654 | { |
| 655 | typedef typename ::boost::mpl::if_< |
| 656 | typename is_pseudo_exit<StateType>::type , |
| 657 | typename ContainingSM::template exit_pt<StateType>, |
| 658 | typename ::boost::mpl::identity<StateType>::type |
| 659 | >::type type; |
| 660 | }; |
| 661 | |
| 662 | template <class StateType,class ContainingSM> |
| 663 | struct make_entry |
| 664 | { |
| 665 | typedef typename ::boost::mpl::if_< |
| 666 | typename is_pseudo_entry<StateType>::type , |
| 667 | typename ContainingSM::template entry_pt<StateType>, |
| 668 | typename ::boost::mpl::if_< |
| 669 | typename is_direct_entry<StateType>::type, |
| 670 | typename ContainingSM::template direct<StateType>, |
| 671 | typename ::boost::mpl::identity<StateType>::type |
| 672 | >::type |
| 673 | >::type type; |
| 674 | }; |
| 675 | // metafunction used to say if a SM has pseudo exit states |
| 676 | template <class StateType> |
| 677 | struct has_exit_pseudo_states_helper |
| 678 | { |
| 679 | typedef typename StateType::stt Stt; |
| 680 | typedef typename generate_state_set<Stt>::type state_list; |
| 681 | |
| 682 | typedef ::boost::mpl::bool_< ::boost::mpl::count_if< |
| 683 | state_list,is_pseudo_exit< ::boost::mpl::placeholders::_1> >::value != 0> type; |
| 684 | }; |
| 685 | template <class StateType> |
| 686 | struct has_exit_pseudo_states |
| 687 | { |
| 688 | typedef typename ::boost::mpl::eval_if<typename is_composite_state<StateType>::type, |
| 689 | has_exit_pseudo_states_helper<StateType>, |
| 690 | ::boost::mpl::bool_<false> >::type type; |
| 691 | }; |
| 692 | |
| 693 | // builds flags (add internal_flag_list and flag_list). internal_flag_list is used for terminate/interrupt states |
| 694 | template <class StateType> |
| 695 | struct get_flag_list |
| 696 | { |
| 697 | typedef typename ::boost::fusion::result_of::as_vector< |
| 698 | typename ::boost::fusion::result_of::insert_range< |
| 699 | typename StateType::flag_list, |
| 700 | typename ::boost::fusion::result_of::end< typename StateType::flag_list >::type, |
| 701 | typename StateType::internal_flag_list |
| 702 | >::type |
| 703 | >::type type; |
| 704 | }; |
| 705 | |
| 706 | template <class StateType> |
| 707 | struct is_state_blocking |
| 708 | { |
| 709 | typedef typename ::boost::mpl::fold< |
| 710 | typename get_flag_list<StateType>::type, ::boost::fusion::set<>, |
| 711 | ::boost::mpl::if_< |
| 712 | has_event_blocking_flag< ::boost::mpl::placeholders::_2>, |
| 713 | boost::msm::back11::set_unique_insert< ::boost::mpl::placeholders::_1, ::boost::mpl::placeholders::_2 >, |
| 714 | ::boost::mpl::placeholders::_1 > |
| 715 | >::type blocking_flags; |
| 716 | |
| 717 | typedef typename ::boost::mpl::if_< |
| 718 | ::boost::mpl::empty<blocking_flags>, |
| 719 | ::boost::mpl::bool_<false>, |
| 720 | ::boost::mpl::bool_<true> >::type type; |
| 721 | }; |
| 722 | // returns a mpl::bool_<true> if fsm has an event blocking flag in one of its substates |
| 723 | template <class StateType> |
| 724 | struct has_fsm_blocking_states |
| 725 | { |
| 726 | typedef typename create_stt<StateType>::type Stt; |
| 727 | typedef typename generate_state_set<Stt>::type state_list; |
| 728 | |
| 729 | typedef typename ::boost::mpl::fold< |
| 730 | state_list, ::boost::fusion::set<>, |
| 731 | ::boost::mpl::if_< |
| 732 | is_state_blocking< ::boost::mpl::placeholders::_2>, |
| 733 | ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, ::boost::mpl::placeholders::_2 >, |
| 734 | ::boost::mpl::placeholders::_1 > |
| 735 | >::type blocking_states; |
| 736 | |
| 737 | typedef typename ::boost::mpl::if_< |
| 738 | ::boost::mpl::empty<blocking_states>, |
| 739 | ::boost::mpl::bool_<false>, |
| 740 | ::boost::mpl::bool_<true> >::type type; |
| 741 | }; |
| 742 | |
| 743 | template <class StateType> |
| 744 | struct is_no_exception_thrown |
| 745 | { |
| 746 | typedef ::boost::mpl::bool_< ::boost::mpl::count_if< |
| 747 | typename StateType::configuration, |
| 748 | has_no_exception_thrown< ::boost::mpl::placeholders::_1 > >::value != 0> found; |
| 749 | |
| 750 | typedef typename ::boost::mpl::or_< |
| 751 | typename has_no_exception_thrown<StateType>::type, |
| 752 | found |
| 753 | >::type type; |
| 754 | }; |
| 755 | |
| 756 | template <class StateType> |
| 757 | struct is_no_message_queue |
| 758 | { |
| 759 | typedef ::boost::mpl::bool_< ::boost::mpl::count_if< |
| 760 | typename StateType::configuration, |
| 761 | has_no_message_queue< ::boost::mpl::placeholders::_1 > >::value != 0> found; |
| 762 | |
| 763 | typedef typename ::boost::mpl::or_< |
| 764 | typename has_no_message_queue<StateType>::type, |
| 765 | found |
| 766 | >::type type; |
| 767 | }; |
| 768 | |
| 769 | template <class StateType> |
| 770 | struct is_active_state_switch_policy |
| 771 | { |
| 772 | typedef ::boost::mpl::bool_< ::boost::mpl::count_if< |
| 773 | typename StateType::configuration, |
| 774 | has_active_state_switch_policy< ::boost::mpl::placeholders::_1 > >::value != 0> found; |
| 775 | |
| 776 | typedef typename ::boost::mpl::or_< |
| 777 | typename has_active_state_switch_policy<StateType>::type, |
| 778 | found |
| 779 | >::type type; |
| 780 | }; |
| 781 | |
| 782 | template <class StateType> |
| 783 | struct get_initial_event |
| 784 | { |
| 785 | typedef typename StateType::initial_event type; |
| 786 | }; |
| 787 | |
| 788 | template <class StateType> |
| 789 | struct get_final_event |
| 790 | { |
| 791 | typedef typename StateType::final_event type; |
| 792 | }; |
| 793 | |
| 794 | template <class TransitionTable, class InitState> |
| 795 | struct build_one_orthogonal_region |
| 796 | { |
| 797 | template<typename Row> |
| 798 | struct row_to_incidence : |
| 799 | ::boost::fusion::vector< |
| 800 | ::boost::mpl::pair< |
| 801 | typename Row::next_state_type, |
| 802 | typename Row::transition_event>, |
| 803 | typename Row::current_state_type, |
| 804 | typename Row::next_state_type |
| 805 | > {}; |
| 806 | |
| 807 | template <class Seq, class Elt> |
| 808 | struct transition_incidence_list_helper |
| 809 | { |
| 810 | typedef typename ::boost::mpl::push_back< Seq, row_to_incidence< Elt > >::type type; |
| 811 | }; |
| 812 | |
| 813 | typedef typename ::boost::mpl::fold< |
| 814 | TransitionTable, |
| 815 | ::boost::fusion::vector<>, |
| 816 | transition_incidence_list_helper< ::boost::mpl::placeholders::_1, ::boost::mpl::placeholders::_2> |
| 817 | >::type transition_incidence_list; |
| 818 | |
| 819 | typedef ::boost::msm::mpl_graph::incidence_list_graph<transition_incidence_list> |
| 820 | transition_graph; |
| 821 | |
| 822 | struct preordering_dfs_visitor : |
| 823 | ::boost::msm::mpl_graph::dfs_default_visitor_operations |
| 824 | { |
| 825 | template<typename Node, typename Graph, typename State> |
| 826 | struct discover_vertex : |
| 827 | ::boost::mpl::insert<State, Node> |
| 828 | {}; |
| 829 | }; |
| 830 | |
| 831 | typedef typename mpl::first< |
| 832 | typename ::boost::msm::mpl_graph::depth_first_search< |
| 833 | transition_graph, |
| 834 | preordering_dfs_visitor, |
| 835 | ::boost::fusion::set<>, |
| 836 | InitState |
| 837 | >::type |
| 838 | >::type type; |
| 839 | }; |
| 840 | |
| 841 | template <class Fsm> |
| 842 | struct find_entry_states |
| 843 | { |
| 844 | typedef typename ::boost::mpl::copy< |
| 845 | typename Fsm::substate_list, |
| 846 | ::boost::mpl::inserter< |
| 847 | ::boost::fusion::set<>, |
| 848 | ::boost::mpl::if_< |
| 849 | has_explicit_entry_state< ::boost::mpl::placeholders::_2 >, |
| 850 | boost::msm::back11::set_unique_insert< ::boost::mpl::placeholders::_1, ::boost::mpl::placeholders::_2>, |
| 851 | ::boost::mpl::placeholders::_1 |
| 852 | > |
| 853 | > |
| 854 | >::type type; |
| 855 | }; |
| 856 | |
| 857 | template <class Set1, class Set2> |
| 858 | struct is_common_element |
| 859 | { |
| 860 | typedef typename ::boost::mpl::fold< |
| 861 | Set1, ::boost::mpl::false_, |
| 862 | ::boost::mpl::if_< |
| 863 | ::boost::mpl::has_key< |
| 864 | Set2, |
| 865 | ::boost::mpl::placeholders::_2 |
| 866 | >, |
| 867 | ::boost::mpl::true_, |
| 868 | ::boost::mpl::placeholders::_1 |
| 869 | > |
| 870 | >::type type; |
| 871 | }; |
| 872 | |
| 873 | template <class EntryRegion, class AllRegions> |
| 874 | struct add_entry_region |
| 875 | { |
| 876 | typedef typename ::boost::mpl::transform< |
| 877 | AllRegions, |
| 878 | ::boost::mpl::if_< |
| 879 | is_common_element<EntryRegion, ::boost::mpl::placeholders::_1>, |
| 880 | set_insert_range< ::boost::mpl::placeholders::_1, EntryRegion>, |
| 881 | ::boost::mpl::placeholders::_1 |
| 882 | > |
| 883 | >::type type; |
| 884 | }; |
| 885 | |
| 886 | // build a vector of regions states (as a set) |
| 887 | // one set of states for every region |
| 888 | template <class Fsm, class InitStates> |
| 889 | struct build_orthogonal_regions |
| 890 | { |
| 891 | typedef typename |
| 892 | ::boost::mpl::fold< |
| 893 | InitStates, ::boost::fusion::vector0<>, |
| 894 | ::boost::mpl::push_back< |
| 895 | ::boost::mpl::placeholders::_1, |
| 896 | build_one_orthogonal_region< typename Fsm::stt, ::boost::mpl::placeholders::_2 > > |
| 897 | >::type without_entries; |
| 898 | |
| 899 | typedef typename |
| 900 | ::boost::mpl::fold< |
| 901 | typename find_entry_states<Fsm>::type, ::boost::fusion::vector0<>, |
| 902 | ::boost::mpl::push_back< |
| 903 | ::boost::mpl::placeholders::_1, |
| 904 | build_one_orthogonal_region< typename Fsm::stt, ::boost::mpl::placeholders::_2 > > |
| 905 | >::type only_entries; |
| 906 | |
| 907 | typedef typename ::boost::mpl::fold< |
| 908 | only_entries , without_entries, |
| 909 | add_entry_region< ::boost::mpl::placeholders::_2, ::boost::mpl::placeholders::_1> |
| 910 | >::type type; |
| 911 | }; |
| 912 | |
| 913 | template <class GraphAsSeqOfSets, class StateType> |
| 914 | struct find_region_index |
| 915 | { |
| 916 | typedef typename |
| 917 | ::boost::mpl::fold< |
| 918 | GraphAsSeqOfSets, ::boost::mpl::pair< ::boost::mpl::int_< -1 > /*res*/, ::boost::mpl::int_<0> /*counter*/ >, |
| 919 | ::boost::mpl::if_< |
| 920 | ::boost::mpl::has_key< ::boost::mpl::placeholders::_2, StateType >, |
| 921 | ::boost::mpl::pair< |
| 922 | ::boost::mpl::second< ::boost::mpl::placeholders::_1 >, |
| 923 | ::boost::mpl::next< ::boost::mpl::second< ::boost::mpl::placeholders::_1 > > |
| 924 | >, |
| 925 | ::boost::mpl::pair< |
| 926 | ::boost::mpl::first< ::boost::mpl::placeholders::_1 >, |
| 927 | ::boost::mpl::next< ::boost::mpl::second< ::boost::mpl::placeholders::_1 > > |
| 928 | > |
| 929 | > |
| 930 | >::type result_pair; |
| 931 | typedef typename ::boost::mpl::first<result_pair>::type type; |
| 932 | enum {value = type::value}; |
| 933 | }; |
| 934 | |
| 935 | template <class Fsm> |
| 936 | struct check_regions_orthogonality |
| 937 | { |
| 938 | typedef typename build_orthogonal_regions< Fsm,typename Fsm::initial_states>::type regions; |
| 939 | |
| 940 | typedef typename ::boost::mpl::fold< |
| 941 | regions, ::boost::mpl::int_<0>, |
| 942 | ::boost::mpl::plus< ::boost::mpl::placeholders::_1 , ::boost::mpl::size< ::boost::mpl::placeholders::_2> > |
| 943 | >::type number_of_states_in_regions; |
| 944 | |
| 945 | typedef typename ::boost::mpl::fold< |
| 946 | regions,mpl::set0<>, |
| 947 | set_insert_range< |
| 948 | ::boost::mpl::placeholders::_1, |
| 949 | ::boost::mpl::placeholders::_2 > |
| 950 | >::type one_big_states_set; |
| 951 | |
| 952 | enum {states_in_regions_raw = number_of_states_in_regions::value}; |
| 953 | enum {cumulated_states_in_regions_raw = ::boost::mpl::size<one_big_states_set>::value}; |
| 954 | }; |
| 955 | |
| 956 | template <class Fsm> |
| 957 | struct check_no_unreachable_state |
| 958 | { |
| 959 | typedef typename check_regions_orthogonality<Fsm>::one_big_states_set states_in_regions; |
| 960 | |
| 961 | typedef typename set_insert_range< |
| 962 | states_in_regions, |
| 963 | typename ::boost::mpl::eval_if< |
| 964 | typename has_explicit_creation<Fsm>::type, |
| 965 | get_explicit_creation<Fsm>, |
| 966 | ::boost::fusion::result_of::make_vector<> |
| 967 | >::type |
| 968 | >::type with_explicit_creation; |
| 969 | |
| 970 | enum {states_in_fsm = ::boost::mpl::size< typename Fsm::substate_list >::value}; |
| 971 | enum {cumulated_states_in_regions = ::boost::mpl::size< with_explicit_creation >::value}; |
| 972 | }; |
| 973 | |
| 974 | // helper to find out if a SM has an active exit state and is therefore waiting for exiting |
| 975 | template <class StateType,class OwnerFct,class FSM> |
| 976 | inline |
| 977 | typename ::boost::enable_if<typename ::boost::mpl::and_<typename is_composite_state<FSM>::type, |
| 978 | typename is_pseudo_exit<StateType>::type>,bool >::type |
| 979 | is_exit_state_active(FSM& fsm) |
| 980 | { |
| 981 | typedef typename OwnerFct::type Composite; |
| 982 | //typedef typename create_stt<Composite>::type stt; |
| 983 | typedef typename Composite::stt stt; |
| 984 | int state_id = get_state_id<stt,StateType>::type::value; |
| 985 | Composite& comp = fsm.template get_state<Composite&>(); |
| 986 | return (std::find(comp.current_state(),comp.current_state()+Composite::nr_regions::value,state_id) |
| 987 | !=comp.current_state()+Composite::nr_regions::value); |
| 988 | } |
| 989 | template <class StateType,class OwnerFct,class FSM> |
| 990 | inline |
| 991 | typename ::boost::disable_if<typename ::boost::mpl::and_<typename is_composite_state<FSM>::type, |
| 992 | typename is_pseudo_exit<StateType>::type>,bool >::type |
| 993 | is_exit_state_active(FSM&) |
| 994 | { |
| 995 | return false; |
| 996 | } |
| 997 | |
| 998 | |
| 999 | } } }//boost::msm::back11 |
| 1000 | |
| 1001 | #endif // BOOST_MSM_BACK11_METAFUNCTIONS_H |
| 1002 | |
| 1003 | |