Boost logo

Boost :

From: Maxim Yegorushkin (maxim.yegorushkin_at_[hidden])
Date: 2005-12-31 15:01:00


Existing practice have shown the need for intrusive containers. Widely used C++
   libraries, such as C++ Standard Library and Boost have long missed such
   facilities so that users had to refrain from using those due to mandatory
   element copying or fall back to techniques like two phase construction to
   insert logically non-copyable elements in containers and initialize them after
   that or insert pointers to dynamically allocated objects which is rather
   inelegant and inefficient since containers also dynamically allocate nodes.

The most common use cases for intrusive containers are:

* when one would like to store non-copyable elements in a container
* when it's beneficial to avoid needless data copy in performance critical

The most prominent example of both the use cases, although written for the most
   part in C rather than C++, is Linux kernel built solely upon intrusive lists,
   hash tables and trees.

Basic usage

An intrusive container is not a container in a stl sense, since it does not own
   its elements. The sole purpose of an intrusive container is to establish
   ordering between elements. It's a user responsibility to keep an intrusive
   container up-to-date by calling replace() to alter fields upon which an order
   is established as well as erase the nodes from the container prior to node's
   destruction. Aside from that the look and feel are similar to those of an
   non-intrusive container.

Here is how one would declare an intrusive container, an intrusive node and
   insert removes nodes:

   struct record;

   typedef multi_index_container<intrusive<record> > container;

   struct record : container::node_type {};

   bool operator<(record const& a, record const& b);

   void insert_sample(container* c)
       std::auto_ptr<record> p(new record);

   void erase_sample(container* c, container::iterator pos)
       std::auto_ptr<record const> p(&*pos);

Here is how one would make a multi-node, i.e. a node that can be enlisted into
   several containers simultaneously:

   struct record;

   struct tag1;
   struct tag2;

   typedef multi_index_container<intrusive<record, tag1> > container1;
   typedef multi_index_container<intrusive<record, tag2> > container2;

   struct record : container1::node_type, container2::node_type {};

   inline bool operator<(record const& a, record const& b);

   void insert_sample(container1* c1, container2* c2)
       struct guard
           container1* c1;
           container2* c2;
           std::pair<container1::iterator, bool> i1;
           std::pair<container2::iterator, bool> i2;
           std::auto_ptr<record> p;
              if(i1.second && i2.second) { p.release(); }
              else { c1->erase(i1.first); c2->erase(i2.first); }
           guard(container1* c1, container2* c2, std::auto_ptr<record> p)
               : c1(c1), c2(c2), i1(c1->end(), 0), i2(c2->end(), 0), p(p)
       } guard(c1, c2, std::auto_ptr<record>(new record));

       guard.i1 = c1->insert(*guard.p);
       guard.i2 = c2->insert(*guard.p);

   void erase_sample(container1* c1, container2* c2, record* r)
       delete r;

The examples are included in test_intrusive.cpp.

Current implementation

Current implementation although efficient and mostly functional is a proof of
   concept only. Further design work is required which we rather unclear about
   and therefore would like to poll for interest and suggestions.

Known limitations:
   * partial template specialization support is required
   * dereferencing an intrusive container iterator yields a constant, should
     probably return non-constant
   * safe mode is not implemented for make_iterator() exposed api

Boost list run by bdawes at, gregod at, cpdaniel at, john at