1 ///////////////////////////////////////////////////////////////////////////// 2 // 3 // (C) Copyright Olaf Krzikalla 2004-2006. 4 // (C) Copyright Ion Gaztanaga 2006-2013 5 // 6 // Distributed under the Boost Software License, Version 1.0. 7 // (See accompanying file LICENSE_1_0.txt or copy at 8 // http://www.boost.org/LICENSE_1_0.txt) 9 // 10 // See http://www.boost.org/libs/intrusive for documentation. 11 // 12 ///////////////////////////////////////////////////////////////////////////// 13 14 #ifndef BOOST_INTRUSIVE_UNORDERED_SET_HOOK_HPP 15 #define BOOST_INTRUSIVE_UNORDERED_SET_HOOK_HPP 16 17 #include <boost/intrusive/detail/config_begin.hpp> 18 #include <boost/intrusive/intrusive_fwd.hpp> 19 20 #include <boost/intrusive/pointer_traits.hpp> 21 #include <boost/intrusive/slist_hook.hpp> 22 #include <boost/intrusive/options.hpp> 23 #include <boost/intrusive/detail/generic_hook.hpp> 24 25 #if defined(BOOST_HAS_PRAGMA_ONCE) 26 # pragma once 27 #endif 28 29 namespace boost { 30 namespace intrusive { 31 32 /// @cond 33 34 template<class VoidPointer, bool StoreHash, bool OptimizeMultiKey> 35 struct unordered_node 36 : public slist_node<VoidPointer> 37 { 38 typedef typename pointer_traits 39 <VoidPointer>::template rebind_pointer 40 < unordered_node<VoidPointer, StoreHash, OptimizeMultiKey> >::type 41 node_ptr; 42 node_ptr prev_in_group_; 43 std::size_t hash_; 44 }; 45 46 template<class VoidPointer> 47 struct unordered_node<VoidPointer, false, true> 48 : public slist_node<VoidPointer> 49 { 50 typedef typename pointer_traits 51 <VoidPointer>::template rebind_pointer 52 < unordered_node<VoidPointer, false, true> >::type 53 node_ptr; 54 node_ptr prev_in_group_; 55 }; 56 57 template<class VoidPointer> 58 struct unordered_node<VoidPointer, true, false> 59 : public slist_node<VoidPointer> 60 { 61 typedef typename pointer_traits 62 <VoidPointer>::template rebind_pointer 63 < unordered_node<VoidPointer, true, false> >::type 64 node_ptr; 65 std::size_t hash_; 66 }; 67 68 template<class VoidPointer, bool StoreHash, bool OptimizeMultiKey> 69 struct unordered_node_traits 70 : public slist_node_traits<VoidPointer> 71 { 72 typedef slist_node_traits<VoidPointer> reduced_slist_node_traits; 73 typedef unordered_node<VoidPointer, StoreHash, OptimizeMultiKey> node; 74 75 typedef typename pointer_traits 76 <VoidPointer>::template rebind_pointer 77 < node >::type node_ptr; 78 typedef typename pointer_traits 79 <VoidPointer>::template rebind_pointer 80 < const node >::type const_node_ptr; 81 82 static const bool store_hash = StoreHash; 83 static const bool optimize_multikey = OptimizeMultiKey; 84 get_nextboost::intrusive::unordered_node_traits85 BOOST_INTRUSIVE_FORCEINLINE static node_ptr get_next(const const_node_ptr & n) 86 { return pointer_traits<node_ptr>::static_cast_from(n->next_); } 87 set_nextboost::intrusive::unordered_node_traits88 BOOST_INTRUSIVE_FORCEINLINE static void set_next(node_ptr n, node_ptr next) 89 { n->next_ = next; } 90 get_prev_in_groupboost::intrusive::unordered_node_traits91 BOOST_INTRUSIVE_FORCEINLINE static node_ptr get_prev_in_group(const const_node_ptr & n) 92 { return n->prev_in_group_; } 93 set_prev_in_groupboost::intrusive::unordered_node_traits94 BOOST_INTRUSIVE_FORCEINLINE static void set_prev_in_group(node_ptr n, node_ptr prev) 95 { n->prev_in_group_ = prev; } 96 get_hashboost::intrusive::unordered_node_traits97 BOOST_INTRUSIVE_FORCEINLINE static std::size_t get_hash(const const_node_ptr & n) 98 { return n->hash_; } 99 set_hashboost::intrusive::unordered_node_traits100 BOOST_INTRUSIVE_FORCEINLINE static void set_hash(const node_ptr & n, std::size_t h) 101 { n->hash_ = h; } 102 }; 103 104 template<class NodeTraits> 105 struct unordered_group_adapter 106 { 107 typedef typename NodeTraits::node node; 108 typedef typename NodeTraits::node_ptr node_ptr; 109 typedef typename NodeTraits::const_node_ptr const_node_ptr; 110 get_nextboost::intrusive::unordered_group_adapter111 static node_ptr get_next(const const_node_ptr & n) 112 { return NodeTraits::get_prev_in_group(n); } 113 set_nextboost::intrusive::unordered_group_adapter114 static void set_next(node_ptr n, node_ptr next) 115 { NodeTraits::set_prev_in_group(n, next); } 116 }; 117 118 template<class NodeTraits> 119 struct unordered_algorithms 120 : public circular_slist_algorithms<NodeTraits> 121 { 122 typedef circular_slist_algorithms<NodeTraits> base_type; 123 typedef unordered_group_adapter<NodeTraits> group_traits; 124 typedef circular_slist_algorithms<group_traits> group_algorithms; 125 typedef NodeTraits node_traits; 126 typedef typename NodeTraits::node node; 127 typedef typename NodeTraits::node_ptr node_ptr; 128 typedef typename NodeTraits::const_node_ptr const_node_ptr; 129 initboost::intrusive::unordered_algorithms130 BOOST_INTRUSIVE_FORCEINLINE static void init(typename base_type::node_ptr n) 131 { 132 base_type::init(n); 133 group_algorithms::init(n); 134 } 135 init_headerboost::intrusive::unordered_algorithms136 BOOST_INTRUSIVE_FORCEINLINE static void init_header(typename base_type::node_ptr n) 137 { 138 base_type::init_header(n); 139 group_algorithms::init_header(n); 140 } 141 unlinkboost::intrusive::unordered_algorithms142 BOOST_INTRUSIVE_FORCEINLINE static void unlink(typename base_type::node_ptr n) 143 { 144 base_type::unlink(n); 145 group_algorithms::unlink(n); 146 } 147 }; 148 149 //Class to avoid defining the same algo as a circular list, as hooks would be ambiguous between them 150 template<class Algo> 151 struct uset_algo_wrapper : public Algo 152 {}; 153 154 template<class VoidPointer, bool StoreHash, bool OptimizeMultiKey> 155 struct get_uset_node_traits 156 { 157 typedef typename detail::if_c 158 < (StoreHash || OptimizeMultiKey) 159 , unordered_node_traits<VoidPointer, StoreHash, OptimizeMultiKey> 160 , slist_node_traits<VoidPointer> 161 >::type type; 162 }; 163 164 template<bool OptimizeMultiKey> 165 struct get_uset_algo_type 166 { 167 static const algo_types value = OptimizeMultiKey ? UnorderedAlgorithms : UnorderedCircularSlistAlgorithms; 168 }; 169 170 template<class NodeTraits> 171 struct get_algo<UnorderedAlgorithms, NodeTraits> 172 { 173 typedef unordered_algorithms<NodeTraits> type; 174 }; 175 176 template<class NodeTraits> 177 struct get_algo<UnorderedCircularSlistAlgorithms, NodeTraits> 178 { 179 typedef uset_algo_wrapper< circular_slist_algorithms<NodeTraits> > type; 180 }; 181 182 /// @endcond 183 184 //! Helper metafunction to define a \c unordered_set_base_hook that yields to the same 185 //! type when the same options (either explicitly or implicitly) are used. 186 #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) || defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 187 template<class ...Options> 188 #else 189 template<class O1 = void, class O2 = void, class O3 = void, class O4 = void> 190 #endif 191 struct make_unordered_set_base_hook 192 { 193 /// @cond 194 typedef typename pack_options 195 < hook_defaults, 196 #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 197 O1, O2, O3, O4 198 #else 199 Options... 200 #endif 201 >::type packed_options; 202 203 typedef generic_hook 204 < get_uset_algo_type <packed_options::optimize_multikey>::value 205 , typename get_uset_node_traits < typename packed_options::void_pointer 206 , packed_options::store_hash 207 , packed_options::optimize_multikey 208 >::type 209 , typename packed_options::tag 210 , packed_options::link_mode 211 , HashBaseHookId 212 > implementation_defined; 213 /// @endcond 214 typedef implementation_defined type; 215 }; 216 217 //! Derive a class from unordered_set_base_hook in order to store objects in 218 //! in an unordered_set/unordered_multi_set. unordered_set_base_hook holds the data necessary to maintain 219 //! the unordered_set/unordered_multi_set and provides an appropriate value_traits class for unordered_set/unordered_multi_set. 220 //! 221 //! The hook admits the following options: \c tag<>, \c void_pointer<>, 222 //! \c link_mode<>, \c store_hash<> and \c optimize_multikey<>. 223 //! 224 //! \c tag<> defines a tag to identify the node. 225 //! The same tag value can be used in different classes, but if a class is 226 //! derived from more than one \c list_base_hook, then each \c list_base_hook needs its 227 //! unique tag. 228 //! 229 //! \c void_pointer<> is the pointer type that will be used internally in the hook 230 //! and the container configured to use this hook. 231 //! 232 //! \c link_mode<> will specify the linking mode of the hook (\c normal_link, 233 //! \c auto_unlink or \c safe_link). 234 //! 235 //! \c store_hash<> will tell the hook to store the hash of the value 236 //! to speed up rehashings. 237 //! 238 //! \c optimize_multikey<> will tell the hook to store a link to form a group 239 //! with other value with the same value to speed up searches and insertions 240 //! in unordered_multisets with a great number of with equivalent keys. 241 #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) || defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 242 template<class ...Options> 243 #else 244 template<class O1, class O2, class O3, class O4> 245 #endif 246 class unordered_set_base_hook 247 : public make_unordered_set_base_hook< 248 #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 249 O1, O2, O3, O4 250 #else 251 Options... 252 #endif 253 >::type 254 { 255 #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) 256 public: 257 //! <b>Effects</b>: If link_mode is \c auto_unlink or \c safe_link 258 //! initializes the node to an unlinked state. 259 //! 260 //! <b>Throws</b>: Nothing. 261 unordered_set_base_hook(); 262 263 //! <b>Effects</b>: If link_mode is \c auto_unlink or \c safe_link 264 //! initializes the node to an unlinked state. The argument is ignored. 265 //! 266 //! <b>Throws</b>: Nothing. 267 //! 268 //! <b>Rationale</b>: Providing a copy-constructor 269 //! makes classes using the hook STL-compliant without forcing the 270 //! user to do some additional work. \c swap can be used to emulate 271 //! move-semantics. 272 unordered_set_base_hook(const unordered_set_base_hook& ); 273 274 //! <b>Effects</b>: Empty function. The argument is ignored. 275 //! 276 //! <b>Throws</b>: Nothing. 277 //! 278 //! <b>Rationale</b>: Providing an assignment operator 279 //! makes classes using the hook STL-compliant without forcing the 280 //! user to do some additional work. \c swap can be used to emulate 281 //! move-semantics. 282 unordered_set_base_hook& operator=(const unordered_set_base_hook& ); 283 284 //! <b>Effects</b>: If link_mode is \c normal_link, the destructor does 285 //! nothing (ie. no code is generated). If link_mode is \c safe_link and the 286 //! object is stored in an unordered_set an assertion is raised. If link_mode is 287 //! \c auto_unlink and \c is_linked() is true, the node is unlinked. 288 //! 289 //! <b>Throws</b>: Nothing. 290 ~unordered_set_base_hook(); 291 292 //! <b>Effects</b>: Swapping two nodes swaps the position of the elements 293 //! related to those nodes in one or two containers. That is, if the node 294 //! this is part of the element e1, the node x is part of the element e2 295 //! and both elements are included in the containers s1 and s2, then after 296 //! the swap-operation e1 is in s2 at the position of e2 and e2 is in s1 297 //! at the position of e1. If one element is not in a container, then 298 //! after the swap-operation the other element is not in a container. 299 //! Iterators to e1 and e2 related to those nodes are invalidated. 300 //! 301 //! <b>Complexity</b>: Constant 302 //! 303 //! <b>Throws</b>: Nothing. 304 void swap_nodes(unordered_set_base_hook &other); 305 306 //! <b>Precondition</b>: link_mode must be \c safe_link or \c auto_unlink. 307 //! 308 //! <b>Returns</b>: true, if the node belongs to a container, false 309 //! otherwise. This function can be used to test whether \c unordered_set::iterator_to 310 //! will return a valid iterator. 311 //! 312 //! <b>Complexity</b>: Constant 313 bool is_linked() const; 314 315 //! <b>Effects</b>: Removes the node if it's inserted in a container. 316 //! This function is only allowed if link_mode is \c auto_unlink. 317 //! 318 //! <b>Throws</b>: Nothing. 319 void unlink(); 320 #endif 321 }; 322 323 324 //! Helper metafunction to define a \c unordered_set_member_hook that yields to the same 325 //! type when the same options (either explicitly or implicitly) are used. 326 #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) || defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 327 template<class ...Options> 328 #else 329 template<class O1 = void, class O2 = void, class O3 = void, class O4 = void> 330 #endif 331 struct make_unordered_set_member_hook 332 { 333 /// @cond 334 typedef typename pack_options 335 < hook_defaults, 336 #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 337 O1, O2, O3, O4 338 #else 339 Options... 340 #endif 341 >::type packed_options; 342 343 typedef generic_hook 344 < get_uset_algo_type <packed_options::optimize_multikey>::value 345 , typename get_uset_node_traits < typename packed_options::void_pointer 346 , packed_options::store_hash 347 , packed_options::optimize_multikey 348 >::type 349 , member_tag 350 , packed_options::link_mode 351 , NoBaseHookId 352 > implementation_defined; 353 /// @endcond 354 typedef implementation_defined type; 355 }; 356 357 //! Put a public data member unordered_set_member_hook in order to store objects of this class in 358 //! an unordered_set/unordered_multi_set. unordered_set_member_hook holds the data necessary for maintaining the 359 //! unordered_set/unordered_multi_set and provides an appropriate value_traits class for unordered_set/unordered_multi_set. 360 //! 361 //! The hook admits the following options: \c void_pointer<>, 362 //! \c link_mode<> and \c store_hash<>. 363 //! 364 //! \c void_pointer<> is the pointer type that will be used internally in the hook 365 //! and the container configured to use this hook. 366 //! 367 //! \c link_mode<> will specify the linking mode of the hook (\c normal_link, 368 //! \c auto_unlink or \c safe_link). 369 //! 370 //! \c store_hash<> will tell the hook to store the hash of the value 371 //! to speed up rehashings. 372 #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) || defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 373 template<class ...Options> 374 #else 375 template<class O1, class O2, class O3, class O4> 376 #endif 377 class unordered_set_member_hook 378 : public make_unordered_set_member_hook< 379 #if !defined(BOOST_INTRUSIVE_VARIADIC_TEMPLATES) 380 O1, O2, O3, O4 381 #else 382 Options... 383 #endif 384 >::type 385 { 386 #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) 387 public: 388 //! <b>Effects</b>: If link_mode is \c auto_unlink or \c safe_link 389 //! initializes the node to an unlinked state. 390 //! 391 //! <b>Throws</b>: Nothing. 392 unordered_set_member_hook(); 393 394 //! <b>Effects</b>: If link_mode is \c auto_unlink or \c safe_link 395 //! initializes the node to an unlinked state. The argument is ignored. 396 //! 397 //! <b>Throws</b>: Nothing. 398 //! 399 //! <b>Rationale</b>: Providing a copy-constructor 400 //! makes classes using the hook STL-compliant without forcing the 401 //! user to do some additional work. \c swap can be used to emulate 402 //! move-semantics. 403 unordered_set_member_hook(const unordered_set_member_hook& ); 404 405 //! <b>Effects</b>: Empty function. The argument is ignored. 406 //! 407 //! <b>Throws</b>: Nothing. 408 //! 409 //! <b>Rationale</b>: Providing an assignment operator 410 //! makes classes using the hook STL-compliant without forcing the 411 //! user to do some additional work. \c swap can be used to emulate 412 //! move-semantics. 413 unordered_set_member_hook& operator=(const unordered_set_member_hook& ); 414 415 //! <b>Effects</b>: If link_mode is \c normal_link, the destructor does 416 //! nothing (ie. no code is generated). If link_mode is \c safe_link and the 417 //! object is stored in an unordered_set an assertion is raised. If link_mode is 418 //! \c auto_unlink and \c is_linked() is true, the node is unlinked. 419 //! 420 //! <b>Throws</b>: Nothing. 421 ~unordered_set_member_hook(); 422 423 //! <b>Effects</b>: Swapping two nodes swaps the position of the elements 424 //! related to those nodes in one or two containers. That is, if the node 425 //! this is part of the element e1, the node x is part of the element e2 426 //! and both elements are included in the containers s1 and s2, then after 427 //! the swap-operation e1 is in s2 at the position of e2 and e2 is in s1 428 //! at the position of e1. If one element is not in a container, then 429 //! after the swap-operation the other element is not in a container. 430 //! Iterators to e1 and e2 related to those nodes are invalidated. 431 //! 432 //! <b>Complexity</b>: Constant 433 //! 434 //! <b>Throws</b>: Nothing. 435 void swap_nodes(unordered_set_member_hook &other); 436 437 //! <b>Precondition</b>: link_mode must be \c safe_link or \c auto_unlink. 438 //! 439 //! <b>Returns</b>: true, if the node belongs to a container, false 440 //! otherwise. This function can be used to test whether \c unordered_set::iterator_to 441 //! will return a valid iterator. 442 //! 443 //! <b>Complexity</b>: Constant 444 bool is_linked() const; 445 446 //! <b>Effects</b>: Removes the node if it's inserted in a container. 447 //! This function is only allowed if link_mode is \c auto_unlink. 448 //! 449 //! <b>Throws</b>: Nothing. 450 void unlink(); 451 #endif 452 }; 453 454 } //namespace intrusive 455 } //namespace boost 456 457 #include <boost/intrusive/detail/config_end.hpp> 458 459 #endif //BOOST_INTRUSIVE_UNORDERED_SET_HOOK_HPP 460