Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Intrusive associative containers: set, multiset, rbtree

set, multiset and rbtree hooks
set, multiset and rbtree containers
Example

Boost.Intrusive also offers associative containers that can be very useful when creating more complex associative containers, like containers maintaining one or more indices with different sorting semantics. Boost.Intrusive associative containers, like most STL associative container implementations are based on red-black trees.

The memory overhead of these containers is usually 3 pointers and a bit (with alignment issues, this means 3 pointers and an integer). This size can be reduced to 3 pointers if pointers have even alignment (which is usually true in most systems).

An empty, non constant-time size set, multiset or rbtree has also the size of 3 pointers and an integer (3 pointers when optimized for size). These containers have logarithmic complexity in many operations like searches, insertions, erasures, etc. set and multiset are the intrusive equivalents of standard std::set and std::multiset containers.

rbtree is a superset of set and multiset containers that offers functions to insert unique and multiple keys.

set, multiset and rbtree share the same hooks. This is an advantage, because the same user type can be inserted first in a multiset and after that in set without changing the definition of the user class.

template <class ...Options>
class set_base_hook;
template <class ...Options>
class set_member_hook;

set_base_hook and set_member_hook receive the same options explained in the section How to use Boost.Intrusive plus a size optimization option:

  • tag<class Tag> (for base hooks only): This argument serves as a tag, so you can derive from more than one base hook. Default: tag<default_tag>.
  • link_mode<link_mode_type LinkMode>: The linking policy. Default: link_mode<safe_link>.
  • void_pointer<class VoidPointer>: The pointer type to be used internally in the hook and propagated to the container. Default: void_pointer<void*>.
  • optimize_size<bool Enable>: The hook will be optimized for size instead of speed. The hook will embed the color bit of the red-black tree node in the parent pointer if pointer alignment is even. In some platforms, optimizing the size might reduce speed performance a bit since masking operations will be needed to access parent pointer and color attributes, in other platforms this option improves performance due to improved memory locality. Default: optimize_size<false>.
template <class T, class ...Options>
class set;

template <class T, class ...Options>
class multiset;

template <class T, class ...Options>
class rbtree;

These containers receive the same options explained in the section How to use Boost.Intrusive:

  • base_hook<class Hook> / member_hook<class T, class Hook, Hook T::* PtrToMember> / value_traits<class ValueTraits>: To specify the hook type or value traits used to configure the container. (To learn about value traits go to the section Containers with custom ValueTraits.)
  • constant_time_size<bool Enabled>: To activate the constant-time size() operation. Default: constant_time_size<true>
  • size_type<typename SizeType>: To specify the type that will be used to store the size of the container. Default: size_type<std::size_t>

And they also can receive an additional option:

  • compare<class Compare>: Comparison function for the objects to be inserted in containers. The comparison functor must induce a strict weak ordering. Default: compare< std::less<key_type> >
  • key_of_value<class KeyOfValueFunctionObject>: A function object that will define the key_type of the value type to be stored. This type will allow a map-like interface. See Map and multimap-like interface with set and multiset for details. Default: key_type is equal to value_type (set-like interface).

Now let's see a small example using both hooks and both containers:

#include <boost/intrusive/set.hpp>
#include <vector>
#include <functional>
#include <cassert>

using namespace boost::intrusive;

                  //This is a base hook optimized for size
class MyClass : public set_base_hook<optimize_size<true> >
{
   int int_;

   public:
   //This is a member hook
   set_member_hook<> member_hook_;

   MyClass(int i)
      :  int_(i)
      {}
   friend bool operator< (const MyClass &a, const MyClass &b)
      {  return a.int_ < b.int_;  }
   friend bool operator> (const MyClass &a, const MyClass &b)
      {  return a.int_ > b.int_;  }
   friend bool operator== (const MyClass &a, const MyClass &b)
      {  return a.int_ == b.int_;  }
};

//Define a set using the base hook that will store values in reverse order
typedef set< MyClass, compare<std::greater<MyClass> > >     BaseSet;

//Define an multiset using the member hook
typedef member_hook<MyClass, set_member_hook<>, &MyClass::member_hook_> MemberOption;
typedef multiset< MyClass, MemberOption>   MemberMultiset;

int main()
{
   typedef std::vector<MyClass>::iterator VectIt;

   //Create several MyClass objects, each one with a different value
   std::vector<MyClass> values;
   for(int i = 0; i < 100; ++i)  values.push_back(MyClass(i));

   BaseSet baseset;
   MemberMultiset membermultiset;

   //Check that size optimization is activated in the base hook
   assert(sizeof(set_base_hook<optimize_size<true> >) == 3*sizeof(void*));
   //Check that size optimization is deactivated in the member hook
   assert(sizeof(set_member_hook<>) > 3*sizeof(void*));

   //Now insert them in the reverse order in the base hook set
   for(VectIt it(values.begin()), itend(values.end()); it != itend; ++it){
      baseset.insert(*it);
      membermultiset.insert(*it);
   }

   //Now test sets
   {
      BaseSet::reverse_iterator rbit(baseset.rbegin());
      MemberMultiset::iterator mit(membermultiset.begin());
      VectIt it(values.begin()), itend(values.end());

      //Test the objects inserted in the base hook set
      for(; it != itend; ++it, ++rbit)
         if(&*rbit != &*it)   return 1;

      //Test the objects inserted in the member hook set
      for(it = values.begin(); it != itend; ++it, ++mit)
         if(&*mit != &*it) return 1;
   }
   return 0;
}

PrevUpHomeNext