//

// 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 );     

}