Authors: | Thorsten Ottosen |
---|---|
Contact: | nesotto@cs.aau.dk or tottosen@dezide.com |
Organizations: | Department of Computer Science, Aalborg University, and Dezide Aps |
Date: | 27th of October 2007 |
Copyright: | Thorsten Ottosen 2004-2007. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see LICENSE_1_0.txt). |
Boost.Pointer Container provides containers for holding heap-allocated objects in an exception-safe manner and with minimal overhead. The aim of the library is in particular to make OO programming easier in C++ by establishing a standard set of classes, methods and designs for dealing with OO specific problems
Whenever a programmer wants to have a container of pointers to heap-allocated objects, there is usually only one exception-safe way: to make a container of smart pointers like boost::shared_ptr This approach is suboptimal if
This library therefore provides standard-like containers that are for storing heap-allocated or cloned objects (or in case of a map, the mapped object must be a heap-allocated or cloned object). For each of the standard containers there is a pointer container equivalent that takes ownership of the objects in an exception safe manner. In this respect the library is intended to solve the so-called polymorphic class problem.
The advantages of pointer containers are
The disadvantages are
When you do need shared semantics, this library is not what you need.
If you upgrade from one of these versions of Boost, then there has been one major interface change: map iterators now mimic iterators from std::map. Previously you may have written
for( boost::ptr_map<std::string,T>::iterator i = m.begin(), e = m.end(); i != e; ++i ) { std::cout << "key:" << i.key(); std::cout << "value:" << *i; i->foo(); // call T::foo() }
and this now needs to be converted into
for( boost::ptr_map<std::string,T>::iterator i = m.begin(), e = m.end(); i != e; ++i ) { std::cout << "key:" << i->first; std::cout << "value:" << *i->second; i->second->foo(); // call T::foo() }
Apart from the above change, the library now also introduces
std::auto_ptr<T> overloads:
std::auto_ptr<T> p( new T ); container.push_back( p );
Derived-to-Base conversion in transfer():
boost::ptr_vector<Base> vec; boost::ptr_list<Derived> list; ... vec.transfer( vec.begin(), list ); // now ok
Also note that Boost.Assign introduces better support for pointer containers.
Serialization has now been made optional thanks to Sebastian Ramacher. You simply include <boost/ptr_container/serialize.hpp> or perhaps just one of the more specialized headers.
All containers are now copy-constructible and assignable. So you can e.g. now do:
boost::ptr_vector<Derived> derived = ...; boost::ptr_vector<Base> base( derived ); base = derived;
As the example shows, derived-to-base class conversions are also allowed.
A few general functions have been added:
VoidPtrContainer& base(); const VoidPtrContainer& base() const;
These allow direct access to the wrapped container which is sometimes needed when you want to provide extra functionality.
A few new functions have been added to sequences:
void resize( size_type size ); void resize( size_type size, T* to_clone );
ptr_vector<T> has a few new helper functions to integrate better with C-arrays:
void transfer( iterator before, T** from, size_type size, bool delete_from = true ); T** c_array();
Finally, you can now also "copy" and "assign" an auto_type ptr by calling move():
boost::ptr_vector<T>::auto_type move_ptr = ...; return boost::ptr_container::move( move_ptr );
The library has been fairly stable, but a few new containers have been supported:
There are no docs for these classes yet, but they are almost identical to boost::ptr_set<T>, boost::ptr_map<Key,T> and boost::ptr_array<T,N>, respectively. The underlying containers stem from the two boost libraries
Furthermore, insert iterators have been added.
Starting with Boost v. 1.67.0, Boost.Pointer Container will use Boost.Config to conditionally provide std::unique_ptr-based interfaces in addition to, or instead of, interfaces using std::auto_ptr. Details are on the Compatible Smart Pointer page, which also explains the convention in this documentation of using
compatible-smart-ptr<T>
to indicate such conditional interfaces.
For C++98/03 users, this change has no observable effect.
For C++11/14 users, there is no effect on existing code that used previous versions of Boost.Pointer Container, but now all function overloads taking an std::auto_ptr parameter are accompanied by an overload taking std::unique_ptr. In the case of return types, std::auto_ptr is still always used. Note however that until C++17, it is possible to construct std::unique_ptr<T> implicitly from std::auto_ptr<T>. Thus, users are free to modernize their code by replacing any explicit mention of std::auto_ptr with std::unique_ptr. This change falls just short of a search-and-replace conversion, as it is possible that some code may have relied on std::auto_ptr being copyable. But such situations will result in compile-time errors which should be easy to fix.
Although std::auto_ptr is formally removed as of ISO C++17, certain compiler or standard library vendors have chosen to leave it in for backwards compatibility. For compilers and standard libraries where this is not the case, C++17 compilation of code using Boost.Pointer Container is not possible with Boost v. 1.66.* or earlier. This situation is fixed as of Boost v. 1.67.0.
There are indications that the void* implementation has a slight performance overhead compared to a T* based implementation. Furthermore, a T* based implementation is so much easier to use type-safely with algorithms. Therefore I anticipate to move to a T* based implementation.
Furthermore, the clone allocator might be allowed to have state. This design requires some thought, so if you have good ideas and use-cases' for this, please don't hesitate to contact me.
Also, support for Boost.Interprocess is on the todo list.
There has been a few request for boost::ptr_multi_index_container<T,...>. I investigated how difficult it would be, and it did turn out to be difficult, albeit not impossible. But I don't have the resources to implement this beast for years to come, so if someone really needs this container, I suggest that they talk with me in private about how it can be done.
The following people have been very helpful:
[1] | Matt Austern: "The Standard Librarian: Containers of Pointers" , C/C++ Users Journal Experts Forum. |
[2] | Bjarne Stroustrup, "The C++ Programming Language", Appendix E: "Standard-Library Exception Safety" |
[3] | Herb Sutter, "Exceptional C++". |
[4] | Herb Sutter, "More Exceptional C++". |
[5] | Kevlin Henney: "From Mechanism to Method: The Safe Stacking of Cats" , C++ Experts Forum, February 2002. |
[6] | Some of the few earlier attempts of pointer containers I have seen are the rather interesting NTL and the pointainer. As of this writing both libraries are not exceptions-safe and can leak. |
[7] | INTERNATIONAL STANDARD, Programming languages --- C++, ISO/IEC 14882, 1998. See section 23 in particular. |
[8] | C++ Standard Library Closed Issues List (Revision 27), Item 218, Algorithms do not use binary predicate objects for default comparisons. |
[9] | C++ Standard Library Active Issues List (Revision 27), Item 226, User supplied specializations or overloads of namespace std function templates. |
[10] | Harald Nowak, "A remove_if for vector", C/C++ Users Journal, July 2001. |
[11] | (1, 2) Boost smart pointer timings |
[12] | (1, 2) NTL: Array vs std::vector and boost::shared_ptr |
[13] | Kevlin Henney, Null Object, 2002. |
Copyright: | Thorsten Ottosen 2004-2006. |
---|