# Pointer Container Library

## Examples

Some examples are given here and in the accompanying test files:

- 1. Null pointers cannot be stored in the containers
- 2. Iterators and other operations return indirected values
- 3. Copy-semantics of pointer containers
- 4. Making a non-copyable type Cloneable
- 5. Objects are cloned before insertion, inserted pointers are owned by the container
- 6. Transferring ownership of a single element
- 7. Transferring ownership of pointers between different pointer containers
- 8. Selected test files
- 9. A large example

# 1. Null pointers cannot be stored in the containers

    my_container.push_back( 0 );            // throws bad_ptr
    my_container.replace( an_iterator, 0 ); // throws bad_ptr
    my_container.insert( an_iterator, 0 );  // throws bad_ptr
    std::auto_ptr<T> p( 0 );
    my_container.push_back( p );            // throws bad_ptr

# 2. Iterators and other operations return indirected values

    ptr_vector<X> pvec;
    std::vector<X*> vec;
    *vec.begin()  = new X;   // fine, memory leak
    *pvec.begin() = new X;   // compile time error
    ( *vec.begin() )->foo(); // call X::foo(), a bit clumsy
    pvec.begin()->foo();     // no indirection needed
    *vec.front()  = X();     // overwrite first element
    pvec.front()  = X();     // no indirection needed

# 3. Copy-semantics of pointer containers

    ptr_vector<T> vec1;
    ...
    ptr_vector<T> vec2( vec1.clone() ); // deep copy objects of 'vec1' and use them to construct 'vec2', could be very expensive
    vec2 = vec1.release();              // give up ownership of pointers in 'vec1' and pass the ownership to 'vec2', rather cheap
    vec2.release();                     // give up ownership; the objects will be deallocated if not assigned to another container
    vec1 = vec2;                        // deep copy objects of 'vec2' and assign them to 'vec1', could be very expensive
    ptr_vector<T> vec3( vec1 );         // deep copy objects of 'vec1', could be very expensive

# 4. Making a non-copyable type Cloneable

     // a class that has no normal copy semantics
    class X : boost::noncopyable { public: X* clone() const; ... };

    // this will be found by the library by argument dependent lookup (ADL)
    X* new_clone( const X& x )
    { return x.clone(); }

    // we can now use the interface that requires cloneability
    ptr_vector<X> vec1, vec2;
    ...
    vec2 = vec1.clone();                                 // 'clone()' requires cloning <g>
    vec2.insert( vec2.end(), vec1.begin(), vec1.end() ); // inserting always means inserting clones

# 5. Objects are cloned before insertion, inserted pointers are owned by the container

    class X { ... };                     // assume 'X' is Cloneable
    X x;                                 // and 'X' can be stack-allocated
    ptr_list<X> list;
    list.push_back( new_clone( x ) );    // insert a clone
    list.push_back( new X );             // always give the pointer directly to the container to avoid leaks
    list.push_back( &x );                // don't do this!!!
    std::auto_ptr<X> p( new X );
    list.push_back( p );                 // give up ownership
    BOOST_ASSERT( p.get() == 0 );

# 6. Transferring ownership of a single element

    ptr_deque<T>                    deq;
    typedef ptr_deque<T>::auto_type auto_type;

    // ... fill the container somehow

    auto_type ptr  = deq.release_back();             // remove back element from container and give up ownership
    auto_type ptr2 = deq.release( deq.begin() + 2 ); // use an iterator to determine the element to release
    ptr            = deq.release_front();            // supported for 'ptr_list' and 'ptr_deque'

    deq.push_back( ptr.release() );                  // give ownership back to the container

# 7. Transferring ownership of pointers between different pointer containers

    ptr_list<X> list; ptr_vector<X> vec;
    ...
    //
    // note: no cloning happens in these examples
    //
    list.transfer( list.begin(), vec.begin(), vec );           // make the first element of 'vec' the first element of 'list'
    vec.transfer( vec.end(), list.begin(), list.end(), list ); // put all the lists element into the vector

We can also transfer objects from `ptr_container<Derived>` to `ptr_container<Base>` without any problems.

# 8. Selected test files

incomplete_type_test.cpp:
| Shows how to implement the Composite pattern.
simple_test.cpp:
| Shows how the usage of pointer container compares with a container of smart pointers
view_example.cpp:
| Shows how to use a pointer container as a view into other container
tree_test.cpp: | Shows how to make a tree-structure
array_test.cpp: | Shows how to make an n-ary tree

# 9. A large example

This examples shows many of the most common features at work. The example provide lots of comments.

    //
    // Boost.Pointer Container
    //
    //  Copyright Thorsten Ottosen 2003-2005. Use, modification and
    //  distribution is subject to the Boost Software License, Version
    //  1.0. (See accompanying file LICENSE_1_0.txt or copy at
    //  http://www.boost.org/LICENSE_1_0.txt)
    //
    // For more information, see http://www.boost.org/libs/ptr_container/
    //

    //
    // This example is intended to get you started.
    // Notice how the smart container
    //
    // 1. takes ownership of objects
    // 2. transfers ownership
    // 3. applies indirection to iterators
    // 4. clones objects from other smart containers
    //

    //
    // First we select which container to use.
    //
    #include <boost/ptr_container/ptr_deque.hpp>

    //
    // we need these later in the example
    //
    #include <boost/assert.hpp>
    #include <string>
    #include <exception>

    //
    // Then we define a small polymorphic class
    // hierarchy.
    //

    class animal : boost::noncopyable
    {
        virtual std::string do_speak() const = 0;
        std::string name_;

    protected:
        //
        // Animals cannot be copied...
        //
        animal( const animal& r ) : name_( r.name_ )           { }
        void operator=( const animal& );

    private:
        //
        // ...but due to advances in genetics, we can clone them!
        //

        virtual animal* do_clone() const = 0;

    public:
        animal( const std::string& name ) : name_(name)        { }
        virtual ~animal() throw()                              { }

        std::string speak() const
        {
            return do_speak();
        }

        std::string name() const
        {
            return name_;
        }

        animal* clone() const
        {
            return do_clone();
        }
    };

    //
    // An animal is still not Clonable. We need this last hook.
    //
    // Notice that we pass the animal by const reference
    // and return by pointer.
    //

    animal* new_clone( const animal& a )
    {
        return a.clone();
    }

    //
    // We do not need to define 'delete_clone()' since
    // since the default is to call the default 'operator delete()'.
    //

    const std::string muuuh = "Muuuh!";
    const std::string oiink = "Oiiink";

    class cow : public animal
    {
        virtual std::string do_speak() const
        {
            return muuuh;
        }

        virtual animal* do_clone() const
        {
            return new cow( *this );
        }

    public:
        cow( const std::string& name ) : animal(name)          { }
    };

    class pig : public animal
    {
        virtual std::string do_speak() const
        {
            return oiink;
        }

        virtual animal* do_clone() const
        {
            return new pig( *this );
        }

    public:
        pig( const std::string& name ) : animal(name)          { }
    };

    //
    // Then we, of course, need a place to put all
    // those animals.
    //

    class farm
    {
        //
        // This is where the smart containers are handy
        //
        typedef boost::ptr_deque<animal> barn_type;
        barn_type                        barn;

        //
        // An error type
        //
        struct farm_trouble : public std::exception           { };

    public:
        //
        // We would like to make it possible to
        // iterate over the animals in the farm
        //
        typedef barn_type::iterator  animal_iterator;

        //
        // We also need to count the farm's size...
        //
        typedef barn_type::size_type size_type;

        //
        // And we also want to transfer an animal
        // safely around. The easiest way to think
        // about '::auto_type' is to imagine a simplified
        // 'std::auto_ptr<T>' ... this means you can expect
        //
        //   T* operator->()
        //   T* release()
        //   deleting destructor
        //
        // but not more.
        //
        typedef barn_type::auto_type  animal_transport;

        //
        // Create an empty farm.
        //
        farm()                                                 { }

        //
        // We need a constructor that can make a new
        // farm by cloning a range of animals.
        //
        farm( animal_iterator begin, animal_iterator end )
         :
            //
            // Objects are always cloned before insertion
            // unless we explicitly add a pointer or
            // use 'release()'. Therefore we actually
            // clone all animals in the range
            //
            barn( begin, end )                               { }

        //
        // ... so we need some other function too
        //

        animal_iterator begin()
        {
            return barn.begin();
        }

        animal_iterator end()
        {
            return barn.end();
        }

        //
        // Here it is quite ok to have an 'animal*' argument.
        // The smart container will handle all ownership
        // issues.
        //
        void buy_animal( animal* a )
        {
            barn.push_back( a );
        }

        //
        // The farm can also be in economical trouble and
        // therefore be in the need to sell animals.
        //
        animal_transport sell_animal( animal_iterator to_sell )
        {
            if( to_sell == end() )
                throw farm_trouble();

            //
            // Here we remove the animal from the barn,
            // but the animal is not deleted yet...it's
            // up to the buyer to decide what
            // to do with it.
            //
            return barn.release( to_sell );
        }

        //
        // How big a farm do we have?
        //
        size_type size() const
        {
            return barn.size();
        }

        //
        // If things are bad, we might choose to sell all animals :-(
        //
        std::auto_ptr<barn_type> sell_farm()
        {
            return barn.release();
        }

        //
        // However, if things are good, we might buy somebody
        // else's farm :-)
        //

        void buy_farm( std::auto_ptr<barn_type> other )
        {
            //
            // This line inserts all the animals from 'other'
            // and is guaranteed either to succeed or to have no
            // effect
            //
            barn.transfer( barn.end(), // insert new animals at the end
                             *other );     // we want to transfer all animals,
                                           // so we use the whole container as argument
            //
            // You might think you would have to do
            //
            // other.release();
            //
            // but '*other' is empty and can go out of scope as it wants
            //
            BOOST_ASSERT( other->empty() );
        }

    }; // class 'farm'.

    int main()
    {
        //
        // First we make a farm
        //
        farm animal_farm;
        BOOST_ASSERT( animal_farm.size() == 0u );

        animal_farm.buy_animal( new pig("Betty") );
        animal_farm.buy_animal( new pig("Benny") );
        animal_farm.buy_animal( new pig("Jeltzin") );
        animal_farm.buy_animal( new cow("Hanz") );
        animal_farm.buy_animal( new cow("Mary") );
        animal_farm.buy_animal( new cow("Frederik") );
        BOOST_ASSERT( animal_farm.size() == 6u );

        //
        // Then we make another farm...it will actually contain
        // a clone of the other farm.
        //
        farm new_farm( animal_farm.begin(), animal_farm.end() );
        BOOST_ASSERT( new_farm.size() == 6u );

        //
        // Is it really clones in the new farm?
        //
        BOOST_ASSERT( new_farm.begin()->name() == "Betty" );

        //
        // Then we search for an animal, Mary (the Crown Princess of Denmark),
        // because we would like to buy her ...
        //
        typedef farm::animal_iterator iterator;
        iterator to_sell;
        for( iterator i   = animal_farm.begin(),
                      end = animal_farm.end();
             i != end; ++i )
        {
            if( i->name() == "Mary" )
            {
                to_sell = i;
                break;
            }
        }

        farm::animal_transport mary = animal_farm.sell_animal( to_sell );

        if( mary->speak() == muuuh )
            //
            // Great, Mary is a cow, and she may live longer
            //
            new_farm.buy_animal( mary.release() );
        else
            //
            // Then the animal would be destroyed (!)
            // when we go out of scope.
            //
            ;

        //
        // Now we can observe some changes to the two farms...
        //
        BOOST_ASSERT( animal_farm.size() == 5u );
        BOOST_ASSERT( new_farm.size()    == 7u );

        //
        // The new farm has however underestimated how much
        // it cost to feed Mary and its owner is forced to sell the farm...
        //
        animal_farm.buy_farm( new_farm.sell_farm() );

        BOOST_ASSERT( new_farm.size()    == 0u );
        BOOST_ASSERT( animal_farm.size() == 12u );
    }

Navigate:

- home
- reference

Copyright: | Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see LICENSE_1_0.txt).