From 02e8762afe56055e01a28da3c075e44f70fdc3ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Sun, 8 Mar 2015 08:53:23 +0100 Subject: [PATCH] Update small_vector documentation + update links to Howard Hinnant's papers. --- doc/container.qbk | 24 +++- include/boost/container/allocator_traits.hpp | 7 +- include/boost/container/small_vector.hpp | 128 +++++++++++-------- 3 files changed, 98 insertions(+), 61 deletions(-) diff --git a/doc/container.qbk b/doc/container.qbk index 8e81a84..edea372 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -514,6 +514,21 @@ are a particular case where `static_vector` can be beneficial. [endsect] +[section:small_vector ['small_vector]] + +`small_vector` is a vector-like container optimized for the case when it contains few elements. +It contains some preallocated elements in-place, which allows it to avoid the use of dynamic storage allocation +when the actual number of elements is below that preallocated threshold. `small_vector` is inspired by +[@http://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h LLVM's `SmallVector`] container. +Unlike `static_vector`, `small_vector`'s capacity can grow beyond the initial preallocated capacity. + +`small_vector` is convertible to `small_vector_base`, a type that is independent +from the preallocated element count, allowing client code that does not need to be templated on that N argument. +`small_vector` inherits all `vector`'s member functions so it supports all standard features like emplacement, +stateful allocators, etc. + +[endsect] + [endsect] [section:extended_functionality Extended functionality] @@ -596,7 +611,7 @@ used to customize these containers: In the first C++ standard `list::size()` was not required to be constant-time, and that caused some controversy in the C++ community. Quoting Howard Hinnant's -[@http://home.roadrunner.com/~hinnant/On_list_size.html ['On List Size]] paper: +[@http://howardhinnant.github.io/On_list_size.html ['On List Size]] paper: [: ['There is a considerable debate on whether `std::list::size()` should be O(1) or O(N). The usual argument notes that it is a tradeoff with:] @@ -899,7 +914,7 @@ unsuccessful tries to deprecate or remove it from the standard. [*Boost.Containe as there is a superior [@http://www.boost.org/libs/dynamic_bitset/ Boost.DynamicBitset] solution. For issues with `vector` see the following papers: -* [@http://home.roadrunner.com/~hinnant/onvectorbool.html On `vector`] +* [@http://howardhinnant.github.io/onvectorbool.html On `vector`] * [@http://www.gotw.ca/publications/N1211.pdf vector: N1211: More Problems, Better Solutions], * [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2160.html N2160: Library Issue 96: Fixing vector], * [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2204.html N2204 A Specification to deprecate vector]. @@ -1082,15 +1097,16 @@ use [*Boost.Container]? There are several reasons for that: [section:release_notes Release Notes] [section:release_notes_boost_1_58_00 Boost 1.58 Release] -* Massive dependency reorganization. Now [*Boost.Container depends on very basic utilities like Boost.Core +* Experimental [classref boost::container::small_vector small_vector] container. +* Massive dependency reorganization. Now [*Boost.Container] depends on very basic utilities like Boost.Core and [*Boost.Intrusive]. Preprocessed code size have decreased considerably and compilation times have improved. * Added `nth` and `index_of` functions to containers with random-access iterators (except `basic_string`). * Added C++17's `allocator_traits::is_always_equal`. -* Experimental [classref boost::container::small_vector small_vector] container. * Updated containers to implement new constructors as specified in [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2210 2210. Missing allocator-extended constructor for allocator-aware containers]. * Fixed bugs: * [@https://svn.boost.org/trac/boost/ticket/9931 #9931: ['"flat_map::insert(ordered_unique_range_t...) fails with move_iterators"]] (reopened). + * [@https://svn.boost.org/trac/boost/ticket/11076 #11076: ['"Unqualified calls to memmove/memcpy in container/detail/copy_move_algo.hpp"]]. * [@https://svn.boost.org/trac/boost/ticket/10790 Trac #10790 (['long long errors from container"])]. * [@https://svn.boost.org/trac/boost/ticket/10808 Trac #10808 (['compare equal operator of vector is broken"])]. * [*Source Breaking]: [classref boost::container::scoped_allocator_adaptor scoped_allocator_adaptor]'s diff --git a/include/boost/container/allocator_traits.hpp b/include/boost/container/allocator_traits.hpp index 985bb26..cdaf2ea 100644 --- a/include/boost/container/allocator_traits.hpp +++ b/include/boost/container/allocator_traits.hpp @@ -161,11 +161,10 @@ struct allocator_traits //! Allocator::is_always_equal if such a type exists, otherwise a type //! with an internal constant static boolean member value == is_empty::value typedef see_documentation is_always_equal; - #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED //Still experimental //! Allocator::is_partially_propagable if such a type exists, otherwise a type //! with an internal constant static boolean member value == false + //! Note: Non-standard extension used to implement `small_vector_allocator`. typedef see_documentation is_partially_propagable; - #endif //! Defines an allocator: Allocator::rebind::other if such a type exists; otherwise, Allocator //! if Allocator is a class template instantiation of the form Allocator, where Args is zero or //! more type arguments ; otherwise, the instantiation of rebind_alloc is ill-formed. @@ -240,8 +239,6 @@ struct allocator_traits typedef BOOST_INTRUSIVE_OBTAIN_TYPE_WITH_DEFAULT(boost::container::container_detail::, Allocator, is_partially_propagable, container_detail::false_type) is_partially_propagable; - //is_always_equal and is_partially_propagable are not compatible - BOOST_STATIC_ASSERT((!is_always_equal::value || !is_partially_propagable::value)); //rebind_alloc & rebind_traits #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) @@ -348,7 +345,6 @@ struct allocator_traits } #endif - #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) //! Returns: a.storage_is_unpropagable(p) if is_partially_propagable::value is true; otherwise, //! false. static bool storage_is_unpropagable(const Allocator &a, pointer p) BOOST_NOEXCEPT_OR_NOTHROW @@ -365,6 +361,7 @@ struct allocator_traits return allocator_traits::priv_equal(flag, a, b); } + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) private: static pointer priv_allocate(container_detail::true_type, Allocator &a, size_type n, const_void_pointer p) { return a.allocate(n, p); } diff --git a/include/boost/container/small_vector.hpp b/include/boost/container/small_vector.hpp index 01bbf74..7ef0c23 100644 --- a/include/boost/container/small_vector.hpp +++ b/include/boost/container/small_vector.hpp @@ -53,11 +53,34 @@ namespace container { template > class small_vector_base; -///////////////////////////////////////////////////// -// -// small_vector_allocator -// -///////////////////////////////////////////////////// +#endif + +//! A non-standard allocator used to implement `small_vector`. +//! Users should never use it directly. It is described here +//! for documentation purposes. +//! +//! This allocator inherits from a standard-conforming allocator +//! and forwards member functiond to the standard allocator except +//! when internal storage is being used as memory source. +//! +//! This allocator is a "partially_propagable" allocator and +//! defines `is_partially_propagable` as true_type. +//! +//! A partially propagable allocator means that not all storage +//! allocatod by an instance of `small_vector_allocator` can be +//! deallocated by another instance of this type, even is both +//! instances compare equal or an instance is propagated to another +//! one using the copy/move constructor or assignment. The storage that +//! can never be propagated is identified by `storage_is_unpropagable(p)`. +//! +//! `boost::container::vector` supports partially propagable allocators +//! fallbacking to deep copy/swap/move operations when internal storage +//! is being used to store vector elements. +//! +//! `small_vector_allocator` assumes that will be instantiated as +//! `boost::container::vector< T, small_vector_allocator >` +//! and internal storage can be obtained downcasting that vector +//! to `small_vector_base`. template class small_vector_allocator : public Allocator @@ -65,24 +88,20 @@ class small_vector_allocator typedef unsigned int allocation_type; #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED private: - //Self type - typedef small_vector_allocator self_t; BOOST_COPYABLE_AND_MOVABLE(small_vector_allocator) - #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED - const Allocator &as_base() const { return static_cast(*this); } Allocator &as_base() { return static_cast(*this); } + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + public: #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED typedef allocator_traits allocator_traits_type; - typedef typename - container_detail::version::type version; #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED typedef typename allocator_traits::value_type value_type; @@ -95,18 +114,22 @@ class small_vector_allocator typedef typename allocator_traits::void_pointer void_pointer; typedef typename allocator_traits::const_void_pointer const_void_pointer; - typedef typename allocator_traits_type::propagate_on_container_copy_assignment propagate_on_container_copy_assignment; - typedef typename allocator_traits_type::propagate_on_container_move_assignment propagate_on_container_move_assignment; - typedef typename allocator_traits_type::propagate_on_container_swap propagate_on_container_swap; - typedef container_detail::bool_ is_always_equal; - typedef container_detail::bool_ is_partially_propagable; + typedef typename allocator_traits::propagate_on_container_copy_assignment propagate_on_container_copy_assignment; + typedef typename allocator_traits::propagate_on_container_move_assignment propagate_on_container_move_assignment; + typedef typename allocator_traits::propagate_on_container_swap propagate_on_container_swap; + //! An integral constant with member `::value == false` + typedef BOOST_CONTAINER_IMPDEF(container_detail::bool_) is_always_equal; + //! An integral constant with member `::value == true` + typedef BOOST_CONTAINER_IMPDEF(container_detail::bool_) is_partially_propagable; + + BOOST_CONTAINER_DOCIGN(typedef container_detail::version_type version;) //!Obtains an small_vector_allocator that allocates //!objects of type T2 template struct rebind { - typedef typename allocator_traits_type::template rebind_alloc::type other; + typedef typename allocator_traits::template rebind_alloc::type other; }; #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED) @@ -174,7 +197,8 @@ class small_vector_allocator small_vector_allocator & operator=(BOOST_RV_REF(small_vector_allocator) other) BOOST_NOEXCEPT_OR_NOTHROW { return static_cast(this->Allocator::operator=(::boost::move(other.as_base()))); } - pointer allocate(size_type count, const_void_pointer hint) + //!Allocates storage from the standard-conforming allocator + pointer allocate(size_type count, const_void_pointer hint = const_void_pointer()) { return allocator_traits_type::allocate(this->as_base(), count, hint); } //!Deallocates previously allocated memory. @@ -198,19 +222,21 @@ class small_vector_allocator //!Swaps two allocators, does nothing //!because this small_vector_allocator is stateless - friend void swap(self_t &l, self_t &r) BOOST_NOEXCEPT_OR_NOTHROW + friend void swap(small_vector_allocator &l, small_vector_allocator &r) BOOST_NOEXCEPT_OR_NOTHROW { boost::adl_move_swap(l.as_base(), r.as_base()); } //!An small_vector_allocator always compares to true, as memory allocated with one - //!instance can be deallocated by another instance - friend bool operator==(const small_vector_allocator &, const small_vector_allocator &) BOOST_NOEXCEPT_OR_NOTHROW - { return false; } + //!instance can be deallocated by another instance (except for unpropagable storage) + friend bool operator==(const small_vector_allocator &l, const small_vector_allocator &r) BOOST_NOEXCEPT_OR_NOTHROW + { return allocator_traits_type::equal(l.as_base(), r.as_base()); } //!An small_vector_allocator always compares to false, as memory allocated with one //!instance can be deallocated by another instance - friend bool operator!=(const small_vector_allocator &, const small_vector_allocator &) BOOST_NOEXCEPT_OR_NOTHROW - { return true; } -/* + friend bool operator!=(const small_vector_allocator &l, const small_vector_allocator &r) BOOST_NOEXCEPT_OR_NOTHROW + { return !(l == r); } + + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + /* //!An advanced function that offers in-place expansion shrink to fit and new allocation //!capabilities. Memory allocated with this function can only be deallocated with deallocate() //!or deallocate_many(). @@ -228,13 +254,13 @@ class small_vector_allocator //!This function is available only with Version == 2 size_type size(pointer p) const BOOST_NOEXCEPT_OR_NOTHROW { return allocator_traits_type::size(p); } -*/ + */ private: + /* //!Allocates just one object. Memory allocated with this function //!must be deallocated only with deallocate_one(). //!Throws bad_alloc if there is no enough memory //!This function is available only with Version == 2 -/* using Allocator::allocate_one; using Allocator::allocate_individual; using Allocator::deallocate_one; @@ -242,8 +268,6 @@ class small_vector_allocator using Allocator::allocate_many; using Allocator::deallocate_many;*/ - private: - bool is_internal_storage(pointer p) const { return this->internal_storage() == p; } @@ -259,20 +283,13 @@ class small_vector_allocator const derived_type &d_base = static_cast(v_base); return d_base.internal_storage(); } + #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED }; -#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED - -///////////////////////////////////////////////////// -// -// small_vector_base -// -///////////////////////////////////////////////////// - //! This class consists of common code from all small_vector types that don't depend on the //! "N" template parameter. This class is non-copyable and non-destructible, so this class tipically -//! used as reference argument to functions that read or write small vectors. Since small_vector -//! derives from small_vector_base, the conversion to small_vector_base is implicit: +//! used as reference argument to functions that read or write small vectors. Since `small_vector` +//! derives from `small_vector_base`, the conversion to `small_vector_base` is implicit: //! //! //! //Clients can pass any small_vector. @@ -298,19 +315,24 @@ class small_vector_base BOOST_COPYABLE_AND_MOVABLE(small_vector_base) - public: + friend class small_vector_allocator; + + pointer internal_storage() const BOOST_NOEXCEPT_OR_NOTHROW + { + return boost::intrusive::pointer_traits::pointer_to + (*const_cast(static_cast(static_cast(&m_storage_start)))); + } + typedef vector > base_type; + base_type &as_base() { return static_cast(*this); } + const base_type &as_base() const { return static_cast(*this); } + + public: typedef typename container_detail::aligned_storage ::value>::type storage_type; typedef small_vector_allocator allocator_type; - pointer internal_storage() const BOOST_NOEXCEPT_OR_NOTHROW - { return boost::intrusive::pointer_traits::pointer_to(*const_cast(static_cast(static_cast(&m_storage_start)))); } - protected: - base_type &as_base() { return static_cast(*this); } - const base_type &as_base() const { return static_cast(*this); } - typedef typename base_type::initial_capacity_t initial_capacity_t; explicit small_vector_base(initial_capacity_t, std::size_t initial_capacity) @@ -367,7 +389,7 @@ template struct small_vector_storage_calculator { typedef small_vector_base svh_type; - typedef typename svh_type::base_type svhb_type; + typedef vector > svhb_type; static const std::size_t s_align = container_detail::alignment_of::value; static const std::size_t s_size = sizeof(Storage); static const std::size_t svh_sizeof = sizeof(svh_type); @@ -407,20 +429,22 @@ struct small_vector_storage_definer #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED //! small_vector a vector-like container optimized for the case when it contains few elements. -//! It contains some preallocated elements in-place, which allows it to avoid the use of the small_vector_allocator +//! It contains some preallocated elements in-place, which allows it to avoid the use of dynamic storage allocation //! when the actual number of elements is below that preallocated threshold. //! -//! small_vector is convertible to small_vector_unbounded that is independent +//! `small_vector` is convertible to `small_vector_base` that is independent //! from the preallocated element capacity, so client code does not need to be templated on that N argument. //! -//! All `boost::container:vector` member functions are inherited. See `vector` documentation for details. +//! All `boost::container::vector` member functions are inherited. See `vector` documentation for details. //! //! \tparam T The type of object that is stored in the small_vector //! \tparam N The number of preallocated elements stored inside small_vector. It shall be less than Allocator::max_size(); -//! \tparam Allocator The small_vector_allocator used for memory management when the number of elements exceeds N. +//! \tparam Allocator The allocator used for memory management when the number of elements exceeds N. template ) > class small_vector : public small_vector_base - BOOST_CONTAINER_DOCIGN(BOOST_CONTAINER_I private small_vector_storage_definer::type) + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + , private small_vector_storage_definer::type + #endif { #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED typedef small_vector_base base_type;