From b683dba9be49978461920136989cdca3be5ec1ae Mon Sep 17 00:00:00 2001 From: nobody Date: Wed, 15 May 2002 19:48:00 +0000 Subject: [PATCH 001/175] New repository initialized by cvs2svn. [SVN r146] --- .gitattributes | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..3e84d7c7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,96 @@ +* text=auto !eol svneol=native#text/plain +*.gitattributes text svneol=native#text/plain + +# Scriptish formats +*.bat text svneol=native#text/plain +*.bsh text svneol=native#text/x-beanshell +*.cgi text svneol=native#text/plain +*.cmd text svneol=native#text/plain +*.js text svneol=native#text/javascript +*.php text svneol=native#text/x-php +*.pl text svneol=native#text/x-perl +*.pm text svneol=native#text/x-perl +*.py text svneol=native#text/x-python +*.sh eol=lf svneol=LF#text/x-sh +configure eol=lf svneol=LF#text/x-sh + +# Image formats +*.bmp binary svneol=unset#image/bmp +*.gif binary svneol=unset#image/gif +*.ico binary svneol=unset#image/ico +*.jpeg binary svneol=unset#image/jpeg +*.jpg binary svneol=unset#image/jpeg +*.png binary svneol=unset#image/png +*.tif binary svneol=unset#image/tiff +*.tiff binary svneol=unset#image/tiff +*.svg text svneol=native#image/svg%2Bxml + +# Data formats +*.pdf binary svneol=unset#application/pdf +*.avi binary svneol=unset#video/avi +*.doc binary svneol=unset#application/msword +*.dsp text svneol=crlf#text/plain +*.dsw text svneol=crlf#text/plain +*.eps binary svneol=unset#application/postscript +*.gz binary svneol=unset#application/gzip +*.mov binary svneol=unset#video/quicktime +*.mp3 binary svneol=unset#audio/mpeg +*.ppt binary svneol=unset#application/vnd.ms-powerpoint +*.ps binary svneol=unset#application/postscript +*.psd binary svneol=unset#application/photoshop +*.rdf binary svneol=unset#text/rdf +*.rss text svneol=unset#text/xml +*.rtf binary svneol=unset#text/rtf +*.sln text svneol=native#text/plain +*.swf binary svneol=unset#application/x-shockwave-flash +*.tgz binary svneol=unset#application/gzip +*.vcproj text svneol=native#text/xml +*.vcxproj text svneol=native#text/xml +*.vsprops text svneol=native#text/xml +*.wav binary svneol=unset#audio/wav +*.xls binary svneol=unset#application/vnd.ms-excel +*.zip binary svneol=unset#application/zip + +# Text formats +.htaccess text svneol=native#text/plain +*.bbk text svneol=native#text/xml +*.cmake text svneol=native#text/plain +*.css text svneol=native#text/css +*.dtd text svneol=native#text/xml +*.htm text svneol=native#text/html +*.html text svneol=native#text/html +*.ini text svneol=native#text/plain +*.log text svneol=native#text/plain +*.mak text svneol=native#text/plain +*.qbk text svneol=native#text/plain +*.rst text svneol=native#text/plain +*.sql text svneol=native#text/x-sql +*.txt text svneol=native#text/plain +*.xhtml text svneol=native#text/xhtml%2Bxml +*.xml text svneol=native#text/xml +*.xsd text svneol=native#text/xml +*.xsl text svneol=native#text/xml +*.xslt text svneol=native#text/xml +*.xul text svneol=native#text/xul +*.yml text svneol=native#text/plain +boost-no-inspect text svneol=native#text/plain +CHANGES text svneol=native#text/plain +COPYING text svneol=native#text/plain +INSTALL text svneol=native#text/plain +Jamfile text svneol=native#text/plain +Jamroot text svneol=native#text/plain +Jamfile.v2 text svneol=native#text/plain +Jamrules text svneol=native#text/plain +Makefile* text svneol=native#text/plain +README text svneol=native#text/plain +TODO text svneol=native#text/plain + +# Code formats +*.c text svneol=native#text/plain +*.cpp text svneol=native#text/plain +*.h text svneol=native#text/plain +*.hpp text svneol=native#text/plain +*.ipp text svneol=native#text/plain +*.tpp text svneol=native#text/plain +*.jam text svneol=native#text/plain +*.java text svneol=native#text/plain From f6a60033e388dba82ba28b1c02c7d1a4a36e2450 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 24 Apr 2005 14:23:24 +0000 Subject: [PATCH 002/175] Check in unordered associative containers. I haven't looked at these for ages but they pass my unit tests so they should be okay. [SVN r2585] --- include/boost/unordered/detail/allocator.hpp | 152 ++ include/boost/unordered/detail/hash_table.hpp | 1835 +++++++++++++++++ include/boost/unordered_map.hpp | 593 ++++++ include/boost/unordered_set.hpp | 581 ++++++ 4 files changed, 3161 insertions(+) create mode 100644 include/boost/unordered/detail/allocator.hpp create mode 100644 include/boost/unordered/detail/hash_table.hpp create mode 100644 include/boost/unordered_map.hpp create mode 100644 include/boost/unordered_set.hpp diff --git a/include/boost/unordered/detail/allocator.hpp b/include/boost/unordered/detail/allocator.hpp new file mode 100644 index 00000000..aaa414a5 --- /dev/null +++ b/include/boost/unordered/detail/allocator.hpp @@ -0,0 +1,152 @@ +// (C) Copyright Daniel James 2005. +// Use, modification and distribution are 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) + +#ifndef BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED +#define BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include +#include + +namespace boost { + namespace unordered_detail { + + // Work around for Microsoft's ETI bug. + + template struct get_value_type + { + typedef typename Allocator::value_type type; + }; + + template struct get_pointer + { + typedef typename Allocator::pointer type; + }; + + template struct get_const_pointer + { + typedef typename Allocator::const_pointer type; + }; + + template struct get_reference + { + typedef typename Allocator::reference type; + }; + + template struct get_const_reference + { + typedef typename Allocator::const_reference type; + }; + + #if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) + + template <> + struct get_value_type + { + typedef int type; + }; + + template <> + struct get_pointer + { + typedef int type; + }; + + template <> + struct get_const_pointer + { + typedef int type; + }; + + template <> + struct get_reference + { + typedef int type; + }; + + template <> + struct get_const_reference + { + typedef int type; + }; + + #endif + + template + struct allocator_constructor + { + typedef typename get_pointer::type pointer; + + Allocator& alloc_; + pointer ptr_; + + allocator_constructor(Allocator& a) + : alloc_(a), ptr_() {} + + ~allocator_constructor() { + if (ptr_) alloc_.deallocate(ptr_, 1); + } + + template + pointer construct(V const& v) { + pointer p = alloc_.allocate(1); + ptr_ = p; + alloc_.construct(p, v); + ptr_ = pointer(); + return p; + } + }; + + template + struct allocator_array_constructor + { + typedef typename get_pointer::type pointer; + + Allocator& alloc_; + pointer ptr_; + pointer constructed_; + std::size_t length_; + + allocator_array_constructor(Allocator& a) + : alloc_(a), ptr_(), constructed_(), length_(0) {} + + ~allocator_array_constructor() { + if (ptr_) { + for(pointer p = ptr_; p != constructed_; ++p) + alloc_.destroy(p); + + alloc_.deallocate(ptr_, length_); + } + } + + template + void construct(V const& v, std::size_t l) + { + length_ = l; + ptr_ = alloc_.allocate(length_); + pointer end = ptr_ + length_; + for(constructed_ = ptr_; constructed_ != end; ++constructed_) + alloc_.construct(constructed_, v); + } + + pointer get() const + { + return ptr_; + } + + pointer release() + { + pointer p(ptr_); + ptr_ = pointer(); + return p; + } + }; + } +} + +#endif diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp new file mode 100644 index 00000000..1ab62f52 --- /dev/null +++ b/include/boost/unordered/detail/hash_table.hpp @@ -0,0 +1,1835 @@ +// JTC1/SC22/WG21 N1456 Hash table implementation +// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html + +// boost/detail/hash_table.hpp + +// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright © 2005 Daniel James + +// 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) + +#ifndef BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED +#define BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 +#include +#endif + +// See hash_table::swap() for details about this. +#if !defined(BOOST_UNORDERED_SWAP_METHOD) +#define BOOST_UNORDERED_SWAP_METHOD 3 +#endif + +#if BOOST_UNORDERED_SWAP_METHOD == 1 +#include +#endif + +namespace boost { + namespace unordered_detail { + template struct type_wrapper {}; + + const static std::size_t default_initial_bucket_count = 50; + inline std::size_t next_prime(std::size_t n); + + // I bet this is already in boost somewhere. + + template + void hash_swap(T& x, T& y) + { + using namespace std; + swap(x, y); + } + + // prime number list, accessor + + static const std::size_t prime_list[] = { + 53ul, 97ul, 193ul, 389ul, 769ul, + 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, + 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, + 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, + 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, + 1610612741ul, 3221225473ul, 4294967291ul }; + + // no throw + inline std::size_t next_prime(std::size_t n) { + std::size_t const* bound = + std::lower_bound(prime_list,prime_list + 28, n); + if(bound == prime_list + 28) + bound--; + return *bound; + } + + // pair_cast - used to convert between pair types. + + template + inline std::pair pair_cast(std::pair const& x) + { + return std::pair(Dst1(x.first), Dst2(x.second)); + } + + // Hash Table Data + // + // Responsible for managing the hash buckets. Has no knowledge of hash + // functions or uniqueness. + + template + class hash_table_data + { + public: + class node; + class bucket; + + typedef std::size_t size_type; + + typedef Alloc value_allocator; + typedef typename boost::detail::allocator::rebind_to::type node_allocator; + typedef typename boost::detail::allocator::rebind_to::type bucket_allocator; + typedef typename get_value_type::type value_type; + typedef typename get_pointer::type node_ptr; + typedef typename get_pointer::type bucket_ptr; + typedef typename get_reference::type reference; + +#if defined(BOOST_UNORDERED_PARANOID) + // If the allocator has the expected pointer types I take some liberties. + BOOST_STATIC_CONSTANT(bool, is_pointer_allocator = + (boost::mpl::and_< + boost::is_same, + boost::is_same + >::value)); + + typedef typename boost::mpl::if_c< + is_pointer_allocator, bucket_ptr, node_ptr>::type link_ptr; +#else + typedef bucket_ptr link_ptr; +#endif + + // Hash Bucket + // + // all no throw (memory management is performed by hash_table_data). + + class bucket + { + bucket& operator=(bucket const&); + public: + link_ptr next_; + + bucket() : next_() + { + } + + bucket(bucket const& x) : next_() + { + // Only copy construct when allocating. + BOOST_ASSERT(!x.next_); + } + + bool empty() const + { + return !this->next_; + } + }; + + // Hash Node + // + // all no throw + + class node : public bucket + { + public: + node(value_type const& v) : bucket(), value_(v) {} + + value_type value_; + }; + +#if !defined(BOOST_UNORDERED_PARANOID) + class node_constructor + { + node_allocator& node_alloc_; + bucket_allocator& bucket_alloc_; + value_allocator value_alloc_; + + node_ptr ptr_; + bool value_allocated_; + bool bucket_allocated_; + + public: + + node_constructor(node_allocator& n, bucket_allocator& b) + : node_alloc_(n), bucket_alloc_(b), value_alloc_(n), + ptr_(), value_allocated_(false), bucket_allocated_(false) + { + } + + ~node_constructor() + { + if (ptr_) { + if (value_allocated_) + value_alloc_.destroy( + value_alloc_.address(ptr_->value_)); + if (bucket_allocated_) + bucket_alloc_.destroy( + bucket_alloc_.address(*ptr_)); + + node_alloc_.deallocate(ptr_, 1); + } + } + + template + node_ptr construct(V const& v) + { + assert(!ptr_); + value_allocated_ = bucket_allocated_ = false; + + ptr_ = node_alloc_.allocate(1); + + bucket_alloc_.construct(bucket_alloc_.address( + *ptr_), bucket()); + bucket_allocated_ = true; + + value_alloc_.construct(value_alloc_.address( + ptr_->value_), v); + value_allocated_ = true; + + node_ptr p = ptr_; + ptr_ = node_ptr(); + return p; + } + }; +#endif + + class local_iterator_base + { + public: + link_ptr node_pointer_; + + local_iterator_base() + : node_pointer_() {} + + explicit local_iterator_base(link_ptr n) + : node_pointer_(n) {} + + bool operator==(local_iterator_base const& x) const + { + return node_pointer_ == x.node_pointer_; + } + + bool operator!=(local_iterator_base const& x) const + { + return node_pointer_ != x.node_pointer_; + } + + reference operator*() const + { + BOOST_ASSERT(node_pointer_); + return static_cast(*node_pointer_).value_; + } + + void increment() + { + BOOST_ASSERT(node_pointer_); + node_pointer_ = node_pointer_->next_; + } + }; + + // Erase Iterator + // + // This is an internal 'iterator' (not an STL iterator) which stores a + // pointer to the pointer to the current node. This is needed to remove + // a node from a bucket. + // + // all no throw. + + class erase_iterator + { + link_ptr* prev_ptr; + + public: + + explicit erase_iterator(bucket_ptr b) + : prev_ptr(&b->next_) {} + + void next() + { + prev_ptr = &(*prev_ptr)->next_; + } + + operator bool() const + { + return *prev_ptr; + } + + value_type& operator*() const + { + return static_cast(**prev_ptr).value_; + } + + link_ptr& get_node() const + { + return *prev_ptr; + } + + bool operator!=(local_iterator_base const& y) + { + return *prev_ptr != y.node_pointer_; + } + }; + + class iterator_base + { + public: + bucket_ptr bucket_; + local_iterator_base local_; + + iterator_base() + : bucket_(), local_() {} + + iterator_base(bucket_ptr b) + : bucket_(b), local_(b->next_) {} + + iterator_base(bucket_ptr b, link_ptr n) + : bucket_(b), local_(n) {} + + iterator_base(bucket_ptr b, local_iterator_base const& it) + : bucket_(b), local_(it) {} + + local_iterator_base local() const + { + return local_iterator_base(local_); + } + + bool operator==(iterator_base const& x) const + { + return local_ == x.local_; + } + + bool operator!=(iterator_base const& x) const + { + return local_ != x.local_; + } + + reference operator*() const + { + return *local_; + } + + void increment() + { + BOOST_ASSERT(bucket_); + local_.increment(); + + while (!local_.node_pointer_) { + ++bucket_; + local_ = local_iterator_base(bucket_->next_); + } + } + }; + + // Member Variables + + node_allocator node_alloc_; + bucket_allocator bucket_alloc_; + + bucket_ptr buckets_; + size_type bucket_count_; + bucket_ptr cached_begin_bucket_; + size_type size_; + + // Constructor + + hash_table_data(size_type n, node_allocator const& a) + : node_alloc_(a), bucket_alloc_(a), + buckets_(), bucket_count_(next_prime(n)), + cached_begin_bucket_(), size_(0) + { + // The array constructor will clean up in the event of an + // exception. + allocator_array_constructor + constructor(bucket_alloc_); + constructor.construct(bucket(), bucket_count_ + 1); + + cached_begin_bucket_ = constructor.get() + bucket_count_; + + // Only release the buckets once everything is successfully + // done. + buckets_ = constructor.release(); + } + + // no throw + ~hash_table_data() + { + if(buckets_) { + if(buckets_[bucket_count_].next_) remove_end_marker(); + + for(size_type i = 0; i < bucket_count_; ++i) + delete_nodes(erase_iterator(buckets_ + i), + end(buckets_ + i)); + + for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2) + bucket_alloc_.destroy(buckets_ + i2); + + bucket_alloc_.deallocate(buckets_, bucket_count_ + 1); + } + } + + void add_end_marker() + { + BOOST_ASSERT(buckets_ && !buckets_[bucket_count_].next_); +#if !defined(BOOST_UNORDERED_PARANOID) + buckets_[bucket_count_].next_ = buckets_ + bucket_count_; +#else + if(is_pointer_allocator) { + buckets_[bucket_count_].next_ = buckets_ + bucket_count_; + } + else { + // This seems very wasteful, but I can't think of a better way + // to create an end node and work with all allocators. Although, + // I might change it to do something different when + // typename node_allocator::pointer == node*. + buckets_[bucket_count_].next_ = node_alloc_.allocate(1); + } +#endif + } + + void move_end_marker(hash_table_data& src) + { + BOOST_ASSERT(buckets_ && !buckets_[bucket_count_].next_); + BOOST_ASSERT(src.buckets_ && src.buckets_[src.bucket_count_].next_); + +#if !defined(BOOST_UNORDERED_PARANOID) + buckets_[bucket_count_].next_ = buckets_ + bucket_count_; +#else + if(is_pointer_allocator) { + buckets_[bucket_count_].next_ = buckets_ + bucket_count_; + } + else { + buckets_[bucket_count_].next_ + = src.buckets_[src.bucket_count_].next_; + } +#endif + + src.buckets_[src.bucket_count_].next_ = link_ptr(); + } + + void remove_end_marker() + { + BOOST_ASSERT(buckets_ && buckets_[bucket_count_].next_); + +#if defined(BOOST_UNORDERED_PARANOID) + if(!is_pointer_allocator) + node_alloc_.deallocate(static_cast( + buckets_[bucket_count_].next_), 1); +#endif + + buckets_[bucket_count_].next_ = link_ptr(); + } + + private: + + hash_table_data(hash_table_data const&); + hash_table_data& operator=(hash_table_data const&); + + public: + + // no throw + void swap(hash_table_data& other) + { + std::swap(buckets_, other.buckets_); + std::swap(bucket_count_, other.bucket_count_); + std::swap(cached_begin_bucket_, other.cached_begin_bucket_); + std::swap(size_, other.size_); + } + + // Return the bucket index for a hashed value. + // + // no throw + size_type index_from_hash(size_type hashed) const + { + return hashed % bucket_count_; + } + + // Begin & End + // + // no throw + + iterator_base begin() const + { + return size_ + ? iterator_base(cached_begin_bucket_) + : end(); + } + + iterator_base end() const + { + return iterator_base(buckets_ + bucket_count_); + } + + local_iterator_base begin(size_type n) const + { + return local_iterator_base(buckets_[n].next_); + } + + local_iterator_base end(size_type n) const + { + return local_iterator_base(); + } + + local_iterator_base begin(bucket_ptr b) const + { + return local_iterator_base(b->next_); + } + + local_iterator_base end(bucket_ptr b) const + { + return local_iterator_base(); + } + + // Bucket Size + + // no throw + size_type bucket_size(size_type n) const + { + std::size_t count = 0; + local_iterator_base it1 = begin(n); + local_iterator_base it2 = end(n); + while(it1 != it2) { + ++count; + it1.increment(); + } + return count; + } + + // Erase iterator + // + // Find the pointer to a node, for use when erasing. + // + // no throw + + erase_iterator get_for_erase(iterator_base r) const + { + erase_iterator it(r.bucket_); + local_iterator_base end(r.local()); + while(it != end) it.next(); + return it; + } + + // Link/Unlink/Move Node + // + // For adding nodes to buckets, removing them and moving them to a + // new bucket. + // + // no throw + + void link_node(link_ptr node, local_iterator_base pos) + { + node->next_ = pos.node_pointer_->next_; + pos.node_pointer_->next_ = node; + ++size_; + } + + void link_node(link_ptr node, bucket_ptr base) + { + node->next_ = base->next_; + base->next_ = node; + ++size_; + if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + } + + void unlink_node(erase_iterator it) + { + it.get_node() = it.get_node()->next_; + --size_; + } + + void move_node(hash_table_data& src, erase_iterator it, + bucket_ptr dst) + { + link_ptr n = it.get_node(); + src.unlink_node(it); + link_node(n, dst); + } + + // throws, strong exception-safety: + link_ptr construct_node(value_type const& v) + { +#if defined(BOOST_UNORDERED_PARANOID) + allocator_constructor a(node_alloc_); + return a.construct(v); +#else + node_constructor a(node_alloc_, bucket_alloc_); + return bucket_alloc_.address(*a.construct(v)); +#endif + } + + // Create Node + // + // Create a node and add it to the buckets in the given position. + // + // strong exception safety. + + iterator_base create_node(value_type const& v, bucket_ptr base) + { + // throws, strong exception-safety: + link_ptr node = construct_node(v); + + // Rest is no throw + link_node(node, base); + return iterator_base(base, node); + } + + iterator_base create_node(value_type const& v, iterator_base position) + { + // throws, strong exception-safety: + link_ptr node = construct_node(v); + + // Rest is no throw + link_node(node, position.local()); + return iterator_base(position.bucket_, node); + } + + iterator_base create_node(value_type const& v, + bucket_ptr base, local_iterator_base position) + { + // throws, strong exception-safety: + link_ptr node = construct_node(v); + + // Rest is no throw + if(position != end(base)) + link_node(node, position); + else + link_node(node, base); + + return iterator_base(base, node); + } + + // Delete Node + // + // Remove a node, or a range of nodes, from a bucket, and destory + // them. + // + // no throw + + void delete_node(erase_iterator it) + { + node_ptr n = node_alloc_.address( + static_cast(*it.get_node())); + unlink_node(it); + + node_alloc_.destroy(n); + node_alloc_.deallocate(n, 1); + } + + void delete_nodes(erase_iterator begin, local_iterator_base end) + { + while(begin != end) delete_node(begin); + } + + // Clear + // + // Remove all the nodes. + // + // no throw + // + // TODO: If delete_nodes did throw (it shouldn't but just for + // argument's sake), could this leave cached_begin_bucket_ pointing + // at an empty bucket? + + void clear() + { + bucket_ptr end = buckets_ + bucket_count_; + while(cached_begin_bucket_ != end) { + delete_nodes(erase_iterator(cached_begin_bucket_), + this->end(cached_begin_bucket_)); + ++cached_begin_bucket_; + } + BOOST_ASSERT(!size_); + } + + // Erase + // + // Return type of erase(const_iterator): + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1753.html#130 + // + // no throw + + iterator_base erase(iterator_base r) + { + BOOST_ASSERT(r != end()); + iterator_base next = r; + next.increment(); + delete_node(get_for_erase(r)); + // r has been invalidated but its bucket is still valid + recompute_begin_bucket(r.bucket_, next.bucket_); + return next; + } + + iterator_base erase(iterator_base r1, iterator_base r2) + { + if(r1 != r2) + { + BOOST_ASSERT(r1 != end()); + + if (r1.bucket_ == r2.bucket_) { + delete_nodes(get_for_erase(r1), r2.local()); + + // No need to call recompute_begin_bucket because + // the nodes are only deleted from one bucket, which + // still contains r2 after the erase. + BOOST_ASSERT(!r1.bucket_->empty()); + } + else { + delete_nodes(get_for_erase(r1), end(r1.bucket_)); + + for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i) + delete_nodes(erase_iterator(i), end(i)); + + delete_nodes(erase_iterator(r2.bucket_), r2.local()); + + // r1 has been invalidated but its bucket is still + // valid. + recompute_begin_bucket(r1.bucket_, r2.bucket_); + } + } + + return r2; + } + + // recompute_begin_bucket + // + // After an erase cached_begin_bucket_ might be left pointing to + // an empty bucket, so this is called to update it + // + // no throw + + void recompute_begin_bucket(bucket_ptr ptr) + { + BOOST_ASSERT(!(ptr < cached_begin_bucket_)); + + if(ptr == cached_begin_bucket_) + { + if (size_ != 0) { + while (cached_begin_bucket_->empty()) + ++cached_begin_bucket_; + } else { + cached_begin_bucket_ = buckets_ + bucket_count_; + } + } + } + + // This is called when a range has been erased + // + // no throw + + void recompute_begin_bucket(bucket_ptr i, bucket_ptr j) + { + BOOST_ASSERT(!(i < cached_begin_bucket_) && !(j < i)); + BOOST_ASSERT(j == buckets_ + bucket_count_ || !j->empty()); + + if(i == cached_begin_bucket_ && i->empty()) + cached_begin_bucket_ = j; + } + }; + + template + struct hash_table_data_type + { + typedef typename boost::detail::allocator::rebind_to::type + value_allocator; + typedef hash_table_data type; + }; + + template + class hash_table + : public hash_table_data_type::type + { + typedef typename hash_table_data_type::type data; + + public: + + typedef typename data::value_allocator value_allocator; + typedef typename data::node_allocator node_allocator; + typedef typename data::bucket_ptr bucket_ptr; + typedef typename data::erase_iterator erase_iterator; + + // Type definitions + + typedef KeyType key_type; + typedef Hash hasher; + typedef Pred key_equal; + typedef ValueType value_type; + typedef std::size_t size_type; + + // iterators + + typedef typename data::local_iterator_base local_iterator_base; + typedef typename data::iterator_base iterator_base; + + private: + + // From the compressed functions docs: + // + // "Finally, a word of caution for Visual C++ 6 users: if either + // argument is an empty type, then assigning to that member will + // produce memory corruption, unless the empty type has a "do + // nothing" assignment operator defined. This is due to a bug in + // the way VC6 generates implicit assignment operators." + // + // Nice. + // + // So use std::pair for Visual C++. + + class functions + { +#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 + boost::compressed_pair functions_; +#else + typedef std::pair functions; +#endif + + public: + + functions(hasher const& h, key_equal const& k) + : functions_(h, k) {} + + hasher const& hash_function() const + { +#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 + return functions_.first(); +#else + return functions_.first; +#endif + } + + key_equal const& key_eq() const + { +#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 + return functions_.second(); +#else + return functions_.second; +#endif + } + }; + + // Both hasher and key_equal's copy/assign can throw so double + // buffering is used to copy them. func_ points to the currently + // active function objects. + + typedef functions hash_table::*functions_ptr; + + functions func1_; + functions func2_; + functions_ptr func_; + + float mlf_; + size_type max_load_; + + public: + + // Constructors + + hash_table(size_type n, + hasher const& hf, key_equal const& eq, + value_allocator const& a) + : data(n, a), // throws, cleans itself up + func1_(hf, eq), // throws " " + func2_(hf, eq), // throws " " + func_(&hash_table::func1_), // no throw + mlf_(1.0f) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + } + + // Construct from iterators + + // initial_size + // + // A helper function for the copy constructor to calculate how many + // nodes will be created if the iterator's support it. Might get it + // totally wrong for containers with unique keys. + // + // no throw + + template + std::size_t initial_size(I i, I j, size_type x, + boost::random_access_traversal_tag) + { + // max load factor isn't set yet, but when it is, it'll be 1.0. + size_type n = j - i + 1; + return n > x ? n : x; + }; + + template + std::size_t initial_size(I i, I j, size_type n, + boost::incrementable_traversal_tag) + { + return n; + }; + + template + std::size_t initial_size(I i, I j, size_type x) + { + typedef typename boost::iterator_traversal::type + iterator_traversal_tag; + return initial_size(i, j, x, iterator_traversal_tag()); + }; + + template + hash_table(I i, I j, size_type n, + hasher const& hf, key_equal const& eq, + node_allocator const& a) + : data(initial_size(i, j, n), a), // throws, cleans itself up + func1_(hf, eq), // throws " " + func2_(hf, eq), // throws " " + func_(&hash_table::func1_), // no throw + mlf_(1.0f) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + + // This can throw, but hash_table_data's destructor will clean + // up. + insert(i, j); + } + + // Copy Construct + + hash_table(hash_table const& x) + : data(x.min_buckets_for_size(x.size()), x.node_alloc_), // throws + func1_(x.current_functions()), // throws + func2_(x.current_functions()), // throws + func_(&hash_table::func1_), // no throw + mlf_(x.mlf_) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + + // This can throw, but hash_table_data's destructor will clean + // up. + copy_buckets(x, *this, current_functions()); + } + + // Assign + // + // basic exception safety, if copy_functions of reserver throws + // the container is left in a sane, empty state. If copy_buckets + // throws the container is left with whatever was successfully + // copied. + + hash_table& operator=(hash_table const& x) + { + if(this != &x) + { + // TODO: I could rewrite this to use the existing nodes. + this->clear(); // no throw + func_ = copy_functions(x); // throws, strong + mlf_ = x.mlf_; // no throw + calculate_max_load(); // no throw + reserve(x.size()); // throws + copy_buckets(x, *this, current_functions()); // throws + } + + return *this; + } + + // Swap + // + // Swap's behaviour when allocators aren't equal is in dispute, see + // this paper for full details: + // + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1599.html + // + // It lists 3 possible behaviours: + // + // 1 - If the allocators aren't equal then throw an exception. + // 2 - Reallocate the elements in the containers with the + // appropriate allocators - messing up exception safety in + // the process. + // 3 - Swap the allocators, hoping that the allocators have a + // no-throw swap. + // + // The paper recommends #3. + // + // I've implemented all three, but actived #3 by default, to change + // it '#define BOOST_UNORDERED_SWAP_METHOD n' where n is the option. + // + // ---------------------------------------------------------------- + // + // Strong exception safety (might change unused function objects) + // + // Can throw if hash or predicate object's copy constructor throws. + // If allocators are unequal: + // Method 1: always throws. + // Method 2: can throw if copying throws + // (memory allocation/hash function object) + // Method 3: Can throw if allocator's swap throws + // (TODO: This one is broken right now, double buffering?) + + void swap(hash_table& x) + { + // This only effects the function objects that aren't in use + // so it is strongly exception safe, via. double buffering. + functions_ptr new_func_this = copy_functions(x); // throws + functions_ptr new_func_that = x.copy_functions(*this); // throws + + if(this->node_alloc_ == x.node_alloc_) { + this->data::swap(x); // no throw + } + else { +#if BOOST_UNORDERED_SWAP_METHOD == 1 + throw std::runtime_error( + "Swapping containers with different allocators.");; +#elif BOOST_UNORDERED_SWAP_METHOD == 2 + // Create new buckets in separate hash_table_data objects + // which will clean up if any of this throws. + data new_this(x.min_buckets_for_size(x.size_), + this->node_alloc_); // throws + copy_buckets(x, new_this, this->*new_func_this); // throws + + data new_that(min_buckets_for_size(this->size_), + x.node_alloc_); // throws + x.copy_buckets(*this, new_that, x.*new_func_that); // throws + + // Start updating the data here, no throw from now on. + new_this.move_end_marker(*this); // no throw + new_that.move_end_marker(x); // no throw + this->data::swap(new_this); // no throw + x.data::swap(new_that); // no throw +#elif BOOST_UNORDERED_SWAP_METHOD == 3 + hash_swap(this->node_alloc_, + x.node_alloc_); // no throw, or is it? + hash_swap(this->bucket_alloc_, + x.bucket_alloc_); // no throw, or is it? + this->data::swap(x); // no throw +#else +#error "Invalid swap method" +#endif + } + + // We've made it, the rest is no throw. + std::swap(mlf_, x.mlf_); + + func_ = new_func_this; + x.func_ = new_func_that; + + calculate_max_load(); + x.calculate_max_load(); + } + + private: + + functions const& current_functions() const + { + return this->*func_; + } + + // This copies the given function objects into the currently unused + // function objects and returns a pointer, that func_ can later be + // set to, to commit the change. + // + // Strong exception safety (since only usued function objects are + // changed). + functions_ptr copy_functions(hash_table const& x) + { + // no throw: + functions_ptr ptr = func_ == &hash_table::func1_ + ? &hash_table::func2_ : &hash_table::func1_; + // throws, functions not in use, so strong + this->*ptr = x.current_functions(); + return ptr; + } + + public: + + // accessors + + // no throw + node_allocator const& get_allocator() const + { + return this->node_alloc_; + } + + // no throw + hasher const& hash_function() const + { + return current_functions().hash_function(); + } + + // no throw + key_equal const& key_eq() const + { + return current_functions().key_eq(); + } + + // no throw + size_type size() const + { + return this->size_; + } + + // no throw + bool empty() const + { + return this->size_ == 0; + } + + // no throw + size_type max_size() const + { + return this->node_alloc_.max_size(); + } + + // strong safety + size_type bucket(key_type const& k) const + { + // hash_function can throw: + return this->index_from_hash(hash_function()(k)); + } + + // strong safety + bucket_ptr get_bucket(key_type const& k) const + { + return this->buckets_ + bucket(k); + } + + // no throw + size_type bucket_count() const + { + return this->bucket_count_; + } + + // no throw + size_type max_bucket_count() const + { + return this->bucket_alloc_.max_size(); + } + + private: + + // no throw + size_type min_buckets_for_size(size_type n) const + { + BOOST_ASSERT(mlf_ != 0); + + using namespace std; + + // From 6.3.1/13: + // size < mlf_ * count + // => count > size / mlf_ + // + // Or from rehash post-condition: + // count > size / mlf_ + return static_cast(floor(n / mlf_)) + 1; + } + + // no throw + void calculate_max_load() + { + using namespace std; + + // From 6.3.1/13: + // Only resize when size >= mlf_ * count + max_load_ = static_cast( + ceil(mlf_ * this->bucket_count_)); + } + + // basic exception safety + bool reserve(size_type n) + { + bool need_to_reserve = n >= max_load_; + // throws - basic: + if (need_to_reserve) rehash_impl(min_buckets_for_size(n)); + return need_to_reserve; + } + + public: + + // no throw + float max_load_factor() const + { + return mlf_; + } + + // no throw + void max_load_factor(float z) + { + mlf_ = z; + calculate_max_load(); + } + + // no throw + float load_factor() const + { + BOOST_ASSERT(this->bucket_count_ != 0); + return static_cast(this->size_) + / static_cast(this->bucket_count_); + } + + private: + + // key extractors + + // no throw + static key_type const& extract_key(value_type const& v) + { + return extract(v, (type_wrapper*)0); + } + + static key_type const& extract(value_type const& v, + type_wrapper*) + { + return v; + } + + static key_type const& extract(value_type const& v, + void*) + { + return v.first; + } + + public: + + // if hash function throws, basic exception safety + // strong otherwise. + void rehash(size_type n) + { + using namespace std; + + // no throw: + size_type min_size = min_buckets_for_size(size()); + // basic/strong: + rehash_impl(min_size > n ? min_size : n); + + BOOST_ASSERT(bucket_count() > size() / max_load_factor() + && bucket_count() >= n); + } + + private: + + // if hash function throws, basic exception safety + // strong otherwise + void rehash_impl(size_type n) + { + n = next_prime(n); // no throw + + if (n == bucket_count()) // no throw + return; + + data new_buckets(n, this->node_alloc_); // throws, seperate + move_buckets(*this, new_buckets); // basic/no throw + new_buckets.swap(*this); // no throw + calculate_max_load(); // no throw + } + + // move_buckets & copy_buckets + // + // Note: Because equivalent elements are already + // adjacent to each other in the existing buckets, this + // simple rehashing technique is sufficient to ensure + // that they remain adjacent to each other in the new + // buckets (but in reverse order). + // + // if the hash function throws, basic excpetion safety + // no throw otherwise + + void move_buckets(data& src, data& dst) + { + BOOST_ASSERT(dst.size_ == 0); + BOOST_ASSERT(src.node_alloc_ == dst.node_alloc_); + + bucket_ptr end = src.buckets_ + src.bucket_count_; // no throw + for(; src.cached_begin_bucket_ != end; // no throw + ++src.cached_begin_bucket_) { + erase_iterator it(src.cached_begin_bucket_); // no throw + while(it) { + // This next line throws iff the hash function throws. + bucket_ptr dst_bucket = dst.buckets_ + + dst.index_from_hash( + hash_function()(extract_key(*it))); + + dst.move_node(src, it, dst_bucket); // no throw + } + } + + // Move the end marker from the source to destination. + // Now destination is valid, source is not. + dst.move_end_marker(src); + } + + // basic excpetion safety, will leave dst partially filled. + + static void copy_buckets(data const& src, data& dst, functions const& f) + { + BOOST_ASSERT(dst.size_ == 0); + + // no throw: + bucket_ptr end = src.buckets_ + src.bucket_count_; + + hasher const& hf = f.hash_function(); + + // no throw: + for(bucket_ptr i = src.cached_begin_bucket_; i != end; ++i) { + // no throw: + for(local_iterator_base it = src.begin(i); + it != src.end(i); it.increment()) { + // hash function can throw. + bucket_ptr dst_bucket = dst.buckets_ + + dst.index_from_hash(hf(extract_key(*it))); + // throws, strong + dst.create_node(*it, dst_bucket); + } + } + } + + public: + + // Insert functions + // + // basic exception safety, if hash function throws + // strong otherwise. + + // if hash function throws, basic exception safety + // strong otherwise + value_type& operator[](key_type const& k) + { + BOOST_STATIC_ASSERT(!EquivalentKeys); + BOOST_STATIC_ASSERT(( + !boost::is_same::value)); + typedef typename value_type::second_type mapped_type; + + bucket_ptr bucket = get_bucket(k); + local_iterator_base node = find_iterator(bucket, k); + + if (node != this->end(bucket)) + return *node; + else + { + // Effects only in this block: + + if (reserve(size() + 1)) // basic/strong + bucket = get_bucket(k); // throws, strong + return *this->create_node( // throws, strong + value_type(k, mapped_type()), + bucket); + } + } + + // Insert for containers with equivalent keys + + private: + + // Insert node without checking if a resize is necessary. + + // strong exception safety. + iterator_base unchecked_insert_equivalent(value_type const& v) + { + key_type const& k = extract_key(v); + bucket_ptr bucket = get_bucket(k); + local_iterator_base position = find_iterator(bucket, k); + + // No effects until here. + return this->create_node(v, bucket, position); // throws, strong + } + + // strong exception safety + iterator_base unchecked_insert_equivalent(iterator_base const& it, + value_type const& v) + { + // Condition throws, no side effects. + if(it != this->end() && equal(extract_key(v), *it)) { + return this->create_node(v, it); // throws, strong + } + else { + return unchecked_insert_equivalent(v); // throws, strong + } + } + + public: + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert_equivalent(value_type const& v) + { + reserve(size() + 1); // basic/strong + return unchecked_insert_equivalent(v); // throws, strong + } + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert_equivalent(iterator_base const& it, value_type const& v) + { + if (it != this->end() && equal(extract_key(v), *it)) { // throws, no side effects + // Create new node immediately as rehashing will invalidate 'it' + iterator_base new_node = this->create_node(v, it); // throws, strong + + // If reserve rehashes, new_node is invalidated. + if (reserve(size() + 1)) // basic/strong + return iterator_base( // no throw + get_bucket(extract_key(v)), // throws, no effects + new_node.local()); + else + return new_node; + } + else { + return insert_equivalent(v); // basic/strong + } + } + + public: + + // Insert for containers with unique keys + + // if hash function throws, basic exception safety + // strong otherwise + std::pair insert_unique(value_type const& v) + { + // Throws, but no side effects in this initial code + key_type const& k = extract_key(v); + bucket_ptr bucket = get_bucket(k); + local_iterator_base pos = find_iterator(bucket, k); + + if (pos != this->end(bucket)) { // no throw + // Found existing key, return it. + return std::pair( + iterator_base(bucket, pos), false); // no throw + + } else { + // Doesn't already exist, add to bucket. + // Data is only changed in this block. + + // If we resize, then need to recalculate bucket. + if(reserve(size() + 1)) // throws, basic/strong + bucket = get_bucket(k); // throws, strong + + return std::pair( + this->create_node(v, bucket), true); // throws, strong + } + } + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert_unique(iterator_base const& it, value_type const& v) + { + // If we are given an iterator pointer to the given key, + // then just return it. + if(it != this->end() && equal(extract_key(v), *it)) // throws, strong + return it; + else + return insert_unique(v).first; // basic, if hash + } + + private: + + // if hash function throws, basic exception safety + // strong otherwise + void insert(value_type const& v) + { + if(EquivalentKeys) + insert_equivalent(v); + else + insert_unique(v); + } + + // if hash function throws, basic exception safety + // strong otherwise + void unchecked_insert(value_type const& v) + { + if(EquivalentKeys) + unchecked_insert_equivalent(v); + else + insert_unique(v); + } + + // Insert from iterators + + private: + + // basic exception safety + template + void insert_for_range(I i, I j, + boost::random_access_traversal_tag) + { + reserve(size() + (j - i)); // basic/strong + for (; i != j; ++i) unchecked_insert(*i); // strong + } + + // basic exception safety + template + void insert_for_range(I i, I j, + boost::incrementable_traversal_tag) + { + for (; i != j; ++i) insert(*i); // basic/strong + } + + public: + + // basic exception safety + template + void insert(InputIterator i, InputIterator j) + { + typedef typename boost::iterator_traversal::type + iterator_traversal_tag; + insert_for_range(i, j, iterator_traversal_tag()); + } + + public: + + // erase + + // no throw + iterator_base erase(iterator_base const& r) + { + return this->data::erase(r); + } + + // strong exception safety + size_type erase(key_type const& k) + { + // No side effects in initial section + bucket_ptr bucket = get_bucket(k); + size_type count = 0; + erase_iterator it(find_for_erase(bucket, k)); + + // Rest is no throw, side effects only after this point. + if (it) { + if (EquivalentKeys) { + do { + ++count; + this->delete_node(it); + } while(it && equal(k, *it)); + } + else { + count = 1; + this->delete_node(it); + } + + this->recompute_begin_bucket(bucket); + } + + return count; + } + + // no throw + iterator_base erase(iterator_base const& r1, iterator_base const& r2) + { + return this->data::erase(r1, r2); + } + + // count + // + // strong exception safety, no side effects + size_type count(key_type const& k) const + { + local_iterator_base it = find_iterator(k); // throws, strong + local_iterator_base end; + size_type count = 0; + + if(it != end) { + if(EquivalentKeys) { + do { + ++count; + it.increment(); + } while (it != end && equal(k, *it)); // throws, strong + } + else { + count = 1; + } + } + + return count; + } + + // find + // + // strong exception safety, no side effects + iterator_base find(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + local_iterator_base it = find_iterator(bucket, k); + + if (it != this->end(bucket)) + return iterator_base(bucket, it); + else + return this->end(); + } + + // equal_range + // + // strong exception safety, no side effects + std::pair equal_range(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + local_iterator_base it = find_iterator(bucket, k); + if (it != this->end(bucket)) { + local_iterator_base last = it; + + if(EquivalentKeys) { + local_iterator_base end = this->end(bucket); + local_iterator_base next = last; + next.increment(); + + while(next != end && equal(k, *next)) { + last = next; + next.increment(); + } + } + + iterator_base first(iterator_base(bucket, it)); + iterator_base second(iterator_base(bucket, last)); + second.increment(); + return std::pair(first, second); + } + else { + return std::pair( + this->end(), this->end()); + } + } + + private: + + // strong exception safety, no side effects + bool equal(key_type const& k, value_type const& v) const + { + return key_eq()(k, extract_key(v)); + } + + // strong exception safety, no side effects + local_iterator_base find_iterator(key_type const& k) const + { + return find_iterator(get_bucket(k), k); + } + + // strong exception safety, no side effects + local_iterator_base find_iterator(bucket_ptr bucket, + key_type const& k) const + { + local_iterator_base it = this->begin(bucket); + local_iterator_base end = this->end(bucket); + while (it != end && !equal(k, *it)) + it.increment(); + + return it; + } + + // strong exception safety, no side effects + erase_iterator find_for_erase(bucket_ptr bucket, key_type const& k) + const + { + erase_iterator it(bucket); + while(it && !equal(k, *it)) + it.next(); + + return it; + } + }; + + // Iterators + + template class hash_iterator; + template class hash_const_iterator; + template class hash_local_iterator; + template class hash_const_local_iterator; + class iterator_access; + + // Local Iterators + // + // all no throw + + template + class hash_local_iterator + : public boost::iterator < + std::forward_iterator_tag, + typename get_value_type::type, + std::ptrdiff_t, + typename get_pointer::type, + typename get_reference::type > + { + public: + typedef typename hash_local_iterator::pointer pointer; + typedef typename hash_local_iterator::reference reference; + typedef typename get_value_type::type value_type; + + private: + typedef typename hash_table_data::local_iterator_base base; + typedef hash_const_local_iterator const_local_iterator; + + friend class hash_const_local_iterator; + base base_; + + public: + hash_local_iterator() : base_() {} + explicit hash_local_iterator(base x) : base_(x) {} + reference operator*() const { return *base_; } + value_type* operator->() const { return &*base_; } + hash_local_iterator& operator++() { base_.increment(); return *this; } + hash_local_iterator operator++(int) { hash_local_iterator tmp(base_); base_.increment(); return tmp; } + bool operator==(hash_local_iterator x) const { return base_ == x.base_; } + bool operator==(const_local_iterator x) const { return base_ == x.base_; } + bool operator!=(hash_local_iterator x) const { return base_ != x.base_; } + bool operator!=(const_local_iterator x) const { return base_ != x.base_; } + }; + + template + class hash_const_local_iterator + : public boost::iterator < + std::forward_iterator_tag, + typename get_value_type::type, + std::ptrdiff_t, + typename get_const_pointer::type, + typename get_const_reference::type > + { + public: + typedef typename hash_const_local_iterator::pointer pointer; + typedef typename hash_const_local_iterator::reference reference; + typedef typename get_value_type::type value_type; + + private: + typedef typename hash_table_data::local_iterator_base base; + typedef hash_local_iterator local_iterator; + friend class hash_local_iterator; + base base_; + + public: + hash_const_local_iterator() : base_() {} + explicit hash_const_local_iterator(base x) : base_(x) {} + hash_const_local_iterator(local_iterator x) : base_(x.base_) {} + reference operator*() const { return *base_; } + value_type const* operator->() const { return &*base_; } + hash_const_local_iterator& operator++() { base_.increment(); return *this; } + hash_const_local_iterator operator++(int) { hash_const_local_iterator tmp(base_); base_.increment(); return tmp; } + bool operator==(local_iterator x) const { return base_ == x.base_; } + bool operator==(hash_const_local_iterator x) const { return base_ == x.base_; } + bool operator!=(local_iterator x) const { return base_ != x.base_; } + bool operator!=(hash_const_local_iterator x) const { return base_ != x.base_; } + }; + + // iterators + // + // all no throw + + + template + class hash_iterator + : public boost::iterator < + std::forward_iterator_tag, + typename get_value_type::type, + std::ptrdiff_t, + typename get_pointer::type, + typename get_reference::type > + { + public: + typedef typename hash_iterator::pointer pointer; + typedef typename hash_iterator::reference reference; + typedef typename get_value_type::type value_type; + + private: + typedef typename hash_table_data::iterator_base base; + typedef hash_const_iterator const_iterator; + friend class hash_const_iterator; + base base_; + + public: + + hash_iterator() : base_() {} + explicit hash_iterator(base const& x) : base_(x) {} + reference operator*() const { return *base_; } + value_type* operator->() const { return &*base_; } + hash_iterator& operator++() { base_.increment(); return *this; } + hash_iterator operator++(int) { hash_iterator tmp(base_); base_.increment(); return tmp; } + bool operator==(hash_iterator const& x) const { return base_ == x.base_; } + bool operator==(const_iterator const& x) const { return base_ == x.base_; } + bool operator!=(hash_iterator const& x) const { return base_ != x.base_; } + bool operator!=(const_iterator const& x) const { return base_ != x.base_; } + }; + + template + class hash_const_iterator + : public boost::iterator < + std::forward_iterator_tag, + typename get_value_type::type, + std::ptrdiff_t, + typename get_const_pointer::type, + typename get_const_reference::type > + { + public: + typedef typename hash_const_iterator::pointer pointer; + typedef typename hash_const_iterator::reference reference; + typedef typename get_value_type::type value_type; + + private: + typedef typename hash_table_data::iterator_base base; + typedef hash_iterator iterator; + friend class hash_iterator; + friend class iterator_access; + base base_; + + public: + + hash_const_iterator() : base_() {} + explicit hash_const_iterator(base const& x) : base_(x) {} + hash_const_iterator(iterator const& x) : base_(x.base_) {} + reference operator*() const { return *base_; } + value_type const* operator->() const { return &*base_; } + hash_const_iterator& operator++() { base_.increment(); return *this; } + hash_const_iterator operator++(int) { hash_const_iterator tmp(base_); base_.increment(); return tmp; } + bool operator==(iterator const& x) const { return base_ == x.base_; } + bool operator==(hash_const_iterator const& x) const { return base_ == x.base_; } + bool operator!=(iterator const& x) const { return base_ != x.base_; } + bool operator!=(hash_const_iterator const& x) const { return base_ != x.base_; } + }; + + class iterator_access + { + public: + template + static typename Iterator::base const& get(Iterator const& it) { + return it.base_; + } + }; + + template + class hash_types + { + public: + typedef hash_table hash_table; + + typedef typename hash_table::value_allocator value_allocator; + typedef typename hash_table::iterator_base iterator_base; + + typedef hash_const_local_iterator const_local_iterator; + typedef hash_local_iterator local_iterator; + typedef hash_const_iterator const_iterator; + typedef hash_iterator iterator; + + typedef typename hash_table::size_type size_type; + typedef std::ptrdiff_t difference_type; + }; + } // namespace boost::unordered_detail +} // namespace boost + +#endif // BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED + diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp new file mode 100644 index 00000000..c8a351b0 --- /dev/null +++ b/include/boost/unordered_map.hpp @@ -0,0 +1,593 @@ + +// JTC1/SC22/WG21 N1456 Hash table implementation +// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html + +// boost/unordered_map.hpp + +// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright © 2005 Daniel James. + +// 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) + + +#ifndef BOOST_UNORDERED_MAP_HPP_INCLUDED +#define BOOST_UNORDERED_MAP_HPP_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +#include +#include + +#include +#include + +namespace boost +{ + //! An unordered associative container that associates unique keys with another value. + /*! For full details see section 6.3.4.4 of the Technical report. + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + */ + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > > + class unordered_map + { + // Named for the benefit of Doxygen. + typedef boost::unordered_detail::hash_types< + std::pair, Key, Hash, + Pred, Alloc, false + > implementation_defined; + + typename implementation_defined::hash_table base; + + public: + // types + typedef Key key_type; + typedef std::pair value_type; + typedef T mapped_type; + typedef Hash hasher; + typedef Pred key_equal; + + // This version can be used when compiling against CVS. + typedef Alloc allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + + typedef typename implementation_defined::iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::local_iterator local_iterator; + typedef typename implementation_defined::const_local_iterator const_local_iterator; + + // construct/destroy/copy + + explicit unordered_map( + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(n, hf, eql, a) + { + } + + template + unordered_map(InputIterator f, InputIterator l, + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + typename implementation_defined::iterator_base const& + get(const_iterator const& it) + { + return boost::unordered_detail::iterator_access::get(it); + } + + public: + + allocator_type get_allocator() const + { + return base.get_allocator(); + } + + // size and capacity + + bool empty() const + { + return base.empty(); + } + + size_type size() const + { + return base.size(); + } + + size_type max_size() const + { + return base.max_size(); + } + + // iterators + + iterator begin() + { + return iterator(base.begin()); + } + + const_iterator begin() const + { + return const_iterator(base.begin()); + } + + iterator end() + { + return iterator(base.end()); + } + + const_iterator end() const + { + return const_iterator(base.end()); + } + + // modifiers + + std::pair insert(const value_type& obj) + { + return boost::unordered_detail::pair_cast( + base.insert_unique(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert_unique(get(hint), obj)); + } + + template + void insert(InputIterator first, InputIterator last) + { + base.insert(first, last); + } + + iterator erase(const_iterator position) + { + return iterator(base.erase(get(position))); + } + + size_type erase(const key_type& k) + { + return base.erase(k); + } + + iterator erase(const_iterator first, const_iterator last) + { + return iterator(base.erase(get(first), get(last))); + } + + void clear() + { + base.clear(); + } + + void swap(unordered_map& other) + { + base.swap(other.base); + } + + // observers + + hasher hash_function() const + { + return base.hash_function(); + } + + key_equal key_eq() const + { + return base.key_eq(); + } + + mapped_type& operator[](const key_type &k) + { + return base[k].second; + } + + // lookup + + iterator find(const key_type& k) + { + return iterator(base.find(k)); + } + + const_iterator find(const key_type& k) const + { + return const_iterator(base.find(k)); + } + + size_type count(const key_type& k) const + { + return base.count(k); + } + + std::pair + equal_range(const key_type& k) + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + std::pair + equal_range(const key_type& k) const + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + // bucket interface + + size_type bucket_count() const + { + return base.bucket_count(); + } + + size_type max_bucket_count() const + { + return base.max_bucket_count(); + } + + size_type bucket_size(size_type n) const + { + return base.bucket_size(n); + } + + size_type bucket(const key_type& k) const + { + return base.bucket(k); + } + + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + + const_local_iterator begin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + + const_local_iterator end(size_type n) const + { + return const_local_iterator(base.end(n)); + } + + // hash policy + + float load_factor() const + { + return base.load_factor(); + } + + float max_load_factor() const + { + return base.max_load_factor(); + } + + void max_load_factor(float m) + { + base.max_load_factor(m); + } + + void rehash(size_type n) + { + base.rehash(n); + } + }; // class template unordered_map + + template + void swap(unordered_map &m1, + unordered_map &m2) + { + m1.swap(m2); + } + + //! An unordered associative container that associates equivalent keys with another value. + /*! For full details see section 6.3.4.6 of the Technical report. + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + */ + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > > + class unordered_multimap + { + // Named for the benefit of Doxygen. + typedef boost::unordered_detail::hash_types< + std::pair, Key, Hash, + Pred, Alloc, true + > implementation_defined; + + typename implementation_defined::hash_table base; + + public: + // types + typedef Key key_type; + typedef std::pair value_type; + typedef T mapped_type; + typedef Hash hasher; + typedef Pred key_equal; + + // This version can be used when compiling against CVS. + + typedef Alloc allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + + typedef typename implementation_defined::iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::local_iterator local_iterator; + typedef typename implementation_defined::const_local_iterator const_local_iterator; + + // construct/destroy/copy + + explicit unordered_multimap( + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(n, hf, eql, a) + { + } + + template + unordered_multimap(InputIterator f, InputIterator l, + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + typename implementation_defined::iterator_base const& + get(const_iterator const& it) + { + return boost::unordered_detail::iterator_access::get(it); + } + + public: + + allocator_type get_allocator() const + { + return base.get_allocator(); + } + + // size and capacity + + bool empty() const + { + return base.empty(); + } + + size_type size() const + { + return base.size(); + } + + size_type max_size() const + { + return base.max_size(); + } + + // iterators + + iterator begin() + { + return iterator(base.begin()); + } + + const_iterator begin() const + { + return const_iterator(base.begin()); + } + + iterator end() + { + return iterator(base.end()); + } + + const_iterator end() const + { + return const_iterator(base.end()); + } + + // modifiers + + iterator insert(const value_type& obj) + { + return iterator(base.insert_equivalent(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert_equivalent(get(hint), obj)); + } + + template + void insert(InputIterator first, InputIterator last) + { + base.insert(first, last); + } + + iterator erase(const_iterator position) + { + return iterator(base.erase(get(position))); + } + + size_type erase(const key_type& k) + { + return base.erase(k); + } + + iterator erase(const_iterator first, const_iterator last) + { + return iterator(base.erase(get(first), get(last))); + } + + void clear() + { + base.clear(); + } + + void swap(unordered_multimap& other) + { + base.swap(other.base); + } + + // observers + + hasher hash_function() const + { + return base.hash_function(); + } + + key_equal key_eq() const + { + return base.key_eq(); + } + + // lookup + + iterator find(const key_type& k) + { + return iterator(base.find(k)); + } + + const_iterator find(const key_type& k) const + { + return const_iterator(base.find(k)); + } + + size_type count(const key_type& k) const + { + return base.count(k); + } + + std::pair + equal_range(const key_type& k) + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + std::pair + equal_range(const key_type& k) const + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + // bucket interface + + size_type bucket_count() const + { + return base.bucket_count(); + } + + size_type max_bucket_count() const + { + return base.max_bucket_count(); + } + + size_type bucket_size(size_type n) const + { + return base.bucket_size(n); + } + + size_type bucket(const key_type& k) const + { + return base.bucket(k); + } + + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + + const_local_iterator begin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + + const_local_iterator end(size_type n) const + { + return const_local_iterator(base.end(n)); + } + + // hash policy + + float load_factor() const + { + return base.load_factor(); + } + + float max_load_factor() const + { + return base.max_load_factor(); + } + + void max_load_factor(float m) + { + base.max_load_factor(m); + } + + void rehash(size_type n) + { + base.rehash(n); + } + }; // class template unordered_multimap + + template + void swap(unordered_multimap &m1, + unordered_multimap &m2) + { + m1.swap(m2); + } + +} // namespace boost + +#endif // BOOST_UNORDERED_MAP_HPP_INCLUDED diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp new file mode 100644 index 00000000..ad3ed669 --- /dev/null +++ b/include/boost/unordered_set.hpp @@ -0,0 +1,581 @@ + +// JTC1/SC22/WG21 N1456 Hash table implementation +// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html + +// boost/unordered_set.hpp + +// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright © 2005 Daniel James. + +// 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) + + +#ifndef BOOST_UNORDERED_SET_HPP_INCLUDED +#define BOOST_UNORDERED_SET_HPP_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +#include +#include + +#include +#include + +namespace boost +{ + //! An unordered associative container that stores unique values. + /*! For full details see section 6.3.4.3 of the Technical report. + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + */ + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > + class unordered_set + { + // Named for the benefit of Doxygen. + typedef boost::unordered_detail::hash_types< + Value, Value, Hash, + Pred, Alloc, false + > implementation_defined; + + typename implementation_defined::hash_table base; + + public: + // types + typedef Value key_type; + typedef Value value_type; + typedef Hash hasher; + typedef Pred key_equal; + + typedef Alloc allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + + typedef typename implementation_defined::const_iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::const_local_iterator local_iterator; + typedef typename implementation_defined::const_local_iterator const_local_iterator; + + // construct/destroy/copy + + explicit unordered_set( + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(n, hf, eql, a) + { + } + + template + unordered_set(InputIterator f, InputIterator l, + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + typename implementation_defined::iterator_base const& + get(const_iterator const& it) + { + return boost::unordered_detail::iterator_access::get(it); + } + + public: + + allocator_type get_allocator() const + { + return base.get_allocator(); + } + + // size and capacity + + bool empty() const + { + return base.empty(); + } + + size_type size() const + { + return base.size(); + } + + size_type max_size() const + { + return base.max_size(); + } + + // iterators + + iterator begin() + { + return iterator(base.begin()); + } + + const_iterator begin() const + { + return const_iterator(base.begin()); + } + + iterator end() + { + return iterator(base.end()); + } + + const_iterator end() const + { + return const_iterator(base.end()); + } + + // modifiers + + std::pair insert(const value_type& obj) + { + return boost::unordered_detail::pair_cast( + base.insert_unique(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert_unique(get(hint), obj)); + } + + template + void insert(InputIterator first, InputIterator last) + { + base.insert(first, last); + } + + iterator erase(const_iterator position) + { + return iterator(base.erase(get(position))); + } + + size_type erase(const key_type& k) + { + return base.erase(k); + } + + iterator erase(const_iterator first, const_iterator last) + { + return iterator(base.erase(get(first), get(last))); + } + + void clear() + { + base.clear(); + } + + void swap(unordered_set& other) + { + base.swap(other.base); + } + + // observers + + hasher hash_function() const + { + return base.hash_function(); + } + + key_equal key_eq() const + { + return base.key_eq(); + } + + // lookup + + iterator find(const key_type& k) + { + return iterator(base.find(k)); + } + + const_iterator find(const key_type& k) const + { + return const_iterator(base.find(k)); + } + + size_type count(const key_type& k) const + { + return base.count(k); + } + + std::pair + equal_range(const key_type& k) + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + std::pair + equal_range(const key_type& k) const + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + // bucket interface + + size_type bucket_count() const + { + return base.bucket_count(); + } + + size_type max_bucket_count() const + { + return base.max_bucket_count(); + } + + size_type bucket_size(size_type n) const + { + return base.bucket_size(n); + } + + size_type bucket(const key_type& k) const + { + return base.bucket(k); + } + + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + + const_local_iterator begin(size_type n) const + { + return local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + + const_local_iterator end(size_type n) const + { + return const_local_iterator(base.end(n)); + } + + // hash policy + + float load_factor() const + { + return base.load_factor(); + } + + float max_load_factor() const + { + return base.max_load_factor(); + } + + void max_load_factor(float m) + { + base.max_load_factor(m); + } + + void rehash(size_type n) + { + base.rehash(n); + } + }; // class template unordered_set + + template + void swap(unordered_set &m1, + unordered_set &m2) + { + m1.swap(m2); + } + + //! An unordered associative container that stores equivalent values. + /*! For full details see section 6.3.4.5 of the Technical report. + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + */ + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > + class unordered_multiset + { + // Named for the benefit of Doxygen. + typedef boost::unordered_detail::hash_types< + Value, Value, Hash, + Pred, Alloc, true + > implementation_defined; + + typename implementation_defined::hash_table base; + + public: + //types + typedef Value key_type; + typedef Value value_type; + typedef Hash hasher; + typedef Pred key_equal; + + typedef Alloc allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + + typedef typename implementation_defined::const_iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::const_local_iterator local_iterator; + typedef typename implementation_defined::const_local_iterator const_local_iterator; + + // construct/destroy/copy + + explicit unordered_multiset( + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(n, hf, eql, a) + { + } + + template + unordered_multiset(InputIterator f, InputIterator l, + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + typename implementation_defined::iterator_base const& + get(const_iterator const& it) + { + return boost::unordered_detail::iterator_access::get(it); + } + + public: + + allocator_type get_allocator() const + { + return base.get_allocator(); + } + + // size and capacity + + bool empty() const + { + return base.empty(); + } + + size_type size() const + { + return base.size(); + } + + size_type max_size() const + { + return base.max_size(); + } + + // iterators + + iterator begin() + { + return iterator(base.begin()); + } + + const_iterator begin() const + { + return const_iterator(base.begin()); + } + + iterator end() + { + return iterator(base.end()); + } + + const_iterator end() const + { + return const_iterator(base.end()); + } + + // modifiers + + iterator insert(const value_type& obj) + { + return iterator(base.insert_equivalent(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert_equivalent(get(hint), obj)); + } + + template + void insert(InputIterator first, InputIterator last) + { + base.insert(first, last); + } + + iterator erase(const_iterator position) + { + return iterator(base.erase(get(position))); + } + + size_type erase(const key_type& k) + { + return base.erase(k); + } + + iterator erase(const_iterator first, const_iterator last) + { + return iterator(base.erase(get(first), get(last))); + } + + void clear() + { + base.clear(); + } + + void swap(unordered_multiset& other) + { + base.swap(other.base); + } + + // observers + + hasher hash_function() const + { + return base.hash_function(); + } + + key_equal key_eq() const + { + return base.key_eq(); + } + + // lookup + + iterator find(const key_type& k) + { + return iterator(base.find(k)); + } + + const_iterator find(const key_type& k) const + { + return const_iterator(base.find(k)); + } + + size_type count(const key_type& k) const + { + return base.count(k); + } + + std::pair + equal_range(const key_type& k) + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + std::pair + equal_range(const key_type& k) const + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + // bucket interface + + size_type bucket_count() const + { + return base.bucket_count(); + } + + size_type max_bucket_count() const + { + return base.max_bucket_count(); + } + + size_type bucket_size(size_type n) const + { + return base.bucket_size(n); + } + + size_type bucket(const key_type& k) const + { + return base.bucket(k); + } + + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + + const_local_iterator begin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + + const_local_iterator end(size_type n) const + { + return const_local_iterator(base.end(n)); + } + + // hash policy + + float load_factor() const + { + return base.load_factor(); + } + + float max_load_factor() const + { + return base.max_load_factor(); + } + + void max_load_factor(float m) + { + base.max_load_factor(m); + } + + void rehash(size_type n) + { + base.rehash(n); + } + }; // class template unordered_multiset + + template + void swap(unordered_multiset &m1, + unordered_multiset &m2) + { + m1.swap(m2); + } + +} // namespace boost + +#endif // BOOST_UNORDERED_SET_HPP_INCLUDED From f3b00bc938a09d83a5ae453cbc5f51b09a9d867b Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 20 Jun 2005 21:55:28 +0000 Subject: [PATCH 003/175] Import my latest version of the unordered associative containers. Several changes: - Makes insert meet its exception specification (but needs some refactoring). - Some Borland work arounds, but not all there yet, maybe never will be. - Some extra doxygen comments, but not nearly enough. - Possibly other things that I'm too lazy to look up. [SVN r2642] --- include/boost/unordered/detail/allocator.hpp | 61 +++- include/boost/unordered/detail/hash_table.hpp | 327 +++++++++++------- include/boost/unordered_map.hpp | 30 +- 3 files changed, 270 insertions(+), 148 deletions(-) diff --git a/include/boost/unordered/detail/allocator.hpp b/include/boost/unordered/detail/allocator.hpp index aaa414a5..7725eb8b 100644 --- a/include/boost/unordered/detail/allocator.hpp +++ b/include/boost/unordered/detail/allocator.hpp @@ -10,35 +10,58 @@ # pragma once #endif -#include +#include + +#if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \ + && !defined(__BORLANDC__) +# define BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES +#endif + +#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES) +# include +#endif + #include namespace boost { namespace unordered_detail { +#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES) + template + struct rebind_wrap : ::boost::detail::allocator::rebind_to {}; +#else + template + struct rebind_wrap + { + typedef BOOST_DEDUCED_TYPENAME + Alloc::BOOST_NESTED_TEMPLATE rebind::other + type; + }; +#endif + // Work around for Microsoft's ETI bug. - template struct get_value_type + template struct allocator_value_type { typedef typename Allocator::value_type type; }; - template struct get_pointer + template struct allocator_pointer { typedef typename Allocator::pointer type; }; - template struct get_const_pointer + template struct allocator_const_pointer { typedef typename Allocator::const_pointer type; }; - template struct get_reference + template struct allocator_reference { typedef typename Allocator::reference type; }; - template struct get_const_reference + template struct allocator_const_reference { typedef typename Allocator::const_reference type; }; @@ -46,31 +69,31 @@ namespace boost { #if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) template <> - struct get_value_type + struct allocator_value_type { typedef int type; }; template <> - struct get_pointer + struct allocator_pointer { typedef int type; }; template <> - struct get_const_pointer + struct allocator_const_pointer { typedef int type; }; template <> - struct get_reference + struct allocator_reference { typedef int type; }; template <> - struct get_const_reference + struct allocator_const_reference { typedef int type; }; @@ -80,7 +103,7 @@ namespace boost { template struct allocator_constructor { - typedef typename get_pointer::type pointer; + typedef typename allocator_pointer::type pointer; Allocator& alloc_; pointer ptr_; @@ -100,12 +123,20 @@ namespace boost { ptr_ = pointer(); return p; } + + // no throw + pointer release() + { + pointer p = ptr_; + ptr_ = pointer(); + return p; + } }; template struct allocator_array_constructor { - typedef typename get_pointer::type pointer; + typedef typename allocator_pointer::type pointer; Allocator& alloc_; pointer ptr_; @@ -149,4 +180,8 @@ namespace boost { } } +#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES) +# undef BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES +#endif + #endif diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 1ab62f52..fe0e3838 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -13,6 +13,10 @@ #ifndef BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED #define BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + #include #include @@ -102,12 +106,18 @@ namespace boost { typedef std::size_t size_type; typedef Alloc value_allocator; - typedef typename boost::detail::allocator::rebind_to::type node_allocator; - typedef typename boost::detail::allocator::rebind_to::type bucket_allocator; - typedef typename get_value_type::type value_type; - typedef typename get_pointer::type node_ptr; - typedef typename get_pointer::type bucket_ptr; - typedef typename get_reference::type reference; + + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + node_allocator; + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + bucket_allocator; + + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type node_ptr; + typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type bucket_ptr; + typedef BOOST_DEDUCED_TYPENAME allocator_reference::type reference; #if defined(BOOST_UNORDERED_PARANOID) // If the allocator has the expected pointer types I take some liberties. @@ -117,7 +127,7 @@ namespace boost { boost::is_same >::value)); - typedef typename boost::mpl::if_c< + typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< is_pointer_allocator, bucket_ptr, node_ptr>::type link_ptr; #else typedef bucket_ptr link_ptr; @@ -195,7 +205,7 @@ namespace boost { } template - node_ptr construct(V const& v) + void construct(V const& v) { assert(!ptr_); value_allocated_ = bucket_allocated_ = false; @@ -209,12 +219,24 @@ namespace boost { value_alloc_.construct(value_alloc_.address( ptr_->value_), v); value_allocated_ = true; + } + // no throw + link_ptr release() + { node_ptr p = ptr_; ptr_ = node_ptr(); - return p; + return bucket_alloc_.address(*p); } }; +#else + class node_constructor + : public allocator_constructor + { + public: + node_constructor(node_allocator& n, bucket_allocator&) + : allocator_constructor(n); + }; #endif class local_iterator_base @@ -228,6 +250,11 @@ namespace boost { explicit local_iterator_base(link_ptr n) : node_pointer_(n) {} + bool not_finished() const + { + return node_pointer_; + } + bool operator==(local_iterator_base const& x) const { return node_pointer_ == x.node_pointer_; @@ -253,11 +280,10 @@ namespace boost { // Erase Iterator // - // This is an internal 'iterator' (not an STL iterator) which stores a - // pointer to the pointer to the current node. This is needed to remove - // a node from a bucket. + // This is an internal 'iterator' (not an STL iterator) which is + // used to erase or move a node. // - // all no throw. + // All no throw. class erase_iterator { @@ -273,7 +299,7 @@ namespace boost { prev_ptr = &(*prev_ptr)->next_; } - operator bool() const + bool not_finished() const { return *prev_ptr; } @@ -381,8 +407,7 @@ namespace boost { if(buckets_[bucket_count_].next_) remove_end_marker(); for(size_type i = 0; i < bucket_count_; ++i) - delete_nodes(erase_iterator(buckets_ + i), - end(buckets_ + i)); + delete_nodes(erase_iterator(buckets_ + i)); for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2) bucket_alloc_.destroy(buckets_ + i2); @@ -401,10 +426,10 @@ namespace boost { buckets_[bucket_count_].next_ = buckets_ + bucket_count_; } else { - // This seems very wasteful, but I can't think of a better way - // to create an end node and work with all allocators. Although, - // I might change it to do something different when - // typename node_allocator::pointer == node*. + // This seems very wasteful, but I can't think of a better + // way to create an end node and work with all allocators. + // Although, I might change it to do something different + // when typename node_allocator::pointer == node*. buckets_[bucket_count_].next_ = node_alloc_.allocate(1); } #endif @@ -488,7 +513,7 @@ namespace boost { return local_iterator_base(buckets_[n].next_); } - local_iterator_base end(size_type n) const + local_iterator_base end(size_type) const { return local_iterator_base(); } @@ -498,11 +523,6 @@ namespace boost { return local_iterator_base(b->next_); } - local_iterator_base end(bucket_ptr b) const - { - return local_iterator_base(); - } - // Bucket Size // no throw @@ -510,8 +530,7 @@ namespace boost { { std::size_t count = 0; local_iterator_base it1 = begin(n); - local_iterator_base it2 = end(n); - while(it1 != it2) { + while(it1.not_finished()) { ++count; it1.increment(); } @@ -527,8 +546,8 @@ namespace boost { erase_iterator get_for_erase(iterator_base r) const { erase_iterator it(r.bucket_); - local_iterator_base end(r.local()); - while(it != end) it.next(); + local_iterator_base pos(r.local()); + while(it != pos) it.next(); return it; } @@ -571,13 +590,9 @@ namespace boost { // throws, strong exception-safety: link_ptr construct_node(value_type const& v) { -#if defined(BOOST_UNORDERED_PARANOID) - allocator_constructor a(node_alloc_); - return a.construct(v); -#else node_constructor a(node_alloc_, bucket_alloc_); - return bucket_alloc_.address(*a.construct(v)); -#endif + a.construct(v); + return a.release(); } // Create Node @@ -613,7 +628,7 @@ namespace boost { link_ptr node = construct_node(v); // Rest is no throw - if(position != end(base)) + if(position.not_finished()) link_node(node, position); else link_node(node, base); @@ -640,7 +655,15 @@ namespace boost { void delete_nodes(erase_iterator begin, local_iterator_base end) { - while(begin != end) delete_node(begin); + while(begin != end) { + BOOST_ASSERT(begin.not_finished()); + delete_node(begin); + } + } + + void delete_nodes(erase_iterator begin) + { + while(begin.not_finished()) delete_node(begin); } // Clear @@ -657,8 +680,7 @@ namespace boost { { bucket_ptr end = buckets_ + bucket_count_; while(cached_begin_bucket_ != end) { - delete_nodes(erase_iterator(cached_begin_bucket_), - this->end(cached_begin_bucket_)); + delete_nodes(erase_iterator(cached_begin_bucket_)); ++cached_begin_bucket_; } BOOST_ASSERT(!size_); @@ -697,10 +719,12 @@ namespace boost { BOOST_ASSERT(!r1.bucket_->empty()); } else { - delete_nodes(get_for_erase(r1), end(r1.bucket_)); + BOOST_ASSERT(r1.bucket_ < r2.bucket_); + + delete_nodes(get_for_erase(r1)); for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i) - delete_nodes(erase_iterator(i), end(i)); + delete_nodes(erase_iterator(i)); delete_nodes(erase_iterator(r2.bucket_), r2.local()); @@ -749,28 +773,33 @@ namespace boost { } }; - template - struct hash_table_data_type +#if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) + template <> + class hash_table_data { - typedef typename boost::detail::allocator::rebind_to::type - value_allocator; - typedef hash_table_data type; + public: + typedef int size_type; + typedef int iterator_base; }; +#endif template class hash_table - : public hash_table_data_type::type + : public hash_table_data { - typedef typename hash_table_data_type::type data; + typedef hash_table_data data; + + typedef typename data::node_constructor node_constructor; + typedef typename data::link_ptr link_ptr; public: - typedef typename data::value_allocator value_allocator; - typedef typename data::node_allocator node_allocator; - typedef typename data::bucket_ptr bucket_ptr; - typedef typename data::erase_iterator erase_iterator; + typedef BOOST_DEDUCED_TYPENAME data::value_allocator value_allocator; + typedef BOOST_DEDUCED_TYPENAME data::node_allocator node_allocator; + typedef BOOST_DEDUCED_TYPENAME data::bucket_ptr bucket_ptr; + typedef BOOST_DEDUCED_TYPENAME data::erase_iterator erase_iterator; // Type definitions @@ -782,8 +811,8 @@ namespace boost { // iterators - typedef typename data::local_iterator_base local_iterator_base; - typedef typename data::iterator_base iterator_base; + typedef BOOST_DEDUCED_TYPENAME data::local_iterator_base local_iterator_base; + typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; private: @@ -804,7 +833,7 @@ namespace boost { #if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 boost::compressed_pair functions_; #else - typedef std::pair functions; + std::pair functions_; #endif public: @@ -890,9 +919,9 @@ namespace boost { template std::size_t initial_size(I i, I j, size_type x) { - typedef typename boost::iterator_traversal::type + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type iterator_traversal_tag; - return initial_size(i, j, x, iterator_traversal_tag()); + return initial_size(i, j, x, iterator_traversal_tag); }; template @@ -1259,10 +1288,11 @@ namespace boost { BOOST_ASSERT(src.node_alloc_ == dst.node_alloc_); bucket_ptr end = src.buckets_ + src.bucket_count_; // no throw + for(; src.cached_begin_bucket_ != end; // no throw ++src.cached_begin_bucket_) { erase_iterator it(src.cached_begin_bucket_); // no throw - while(it) { + while(it.not_finished()) { // This next line throws iff the hash function throws. bucket_ptr dst_bucket = dst.buckets_ + dst.index_from_hash( @@ -1292,7 +1322,7 @@ namespace boost { for(bucket_ptr i = src.cached_begin_bucket_; i != end; ++i) { // no throw: for(local_iterator_base it = src.begin(i); - it != src.end(i); it.increment()) { + it.not_finished(); it.increment()) { // hash function can throw. bucket_ptr dst_bucket = dst.buckets_ + dst.index_from_hash(hf(extract_key(*it))); @@ -1316,12 +1346,12 @@ namespace boost { BOOST_STATIC_ASSERT(!EquivalentKeys); BOOST_STATIC_ASSERT(( !boost::is_same::value)); - typedef typename value_type::second_type mapped_type; + typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type; bucket_ptr bucket = get_bucket(k); local_iterator_base node = find_iterator(bucket, k); - if (node != this->end(bucket)) + if (node.not_finished()) return *node; else { @@ -1358,10 +1388,10 @@ namespace boost { { // Condition throws, no side effects. if(it != this->end() && equal(extract_key(v), *it)) { - return this->create_node(v, it); // throws, strong + return this->create_node(v, it); // throws, strong } else { - return unchecked_insert_equivalent(v); // throws, strong + return unchecked_insert_equivalent(v); // throws, strong } } @@ -1371,8 +1401,33 @@ namespace boost { // strong otherwise iterator_base insert_equivalent(value_type const& v) { - reserve(size() + 1); // basic/strong - return unchecked_insert_equivalent(v); // throws, strong + key_type const& k = extract_key(v); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->buckets_ + + this->index_from_hash(hash_value); + local_iterator_base position = find_iterator(bucket, k); + + // Create the node before rehashing in case it throws. + // throws, no side effects: + node_constructor a(this->node_alloc_, this->bucket_alloc_); + a.construct(v); + + // strong/no throw: + if(reserve(size() + 1)) // basic/strong + bucket = this->buckets_ + this->index_from_hash(hash_value); + + // No throw from here. + + link_ptr node = a.release(); + + // I'm relying on local_iterator_base not being invalidated by + // the rehash here. + if(position.not_finished()) + link_node(node, position); + else + link_node(node, bucket); + + return iterator_base(bucket, node); } // if hash function throws, basic exception safety @@ -1380,16 +1435,20 @@ namespace boost { iterator_base insert_equivalent(iterator_base const& it, value_type const& v) { if (it != this->end() && equal(extract_key(v), *it)) { // throws, no side effects - // Create new node immediately as rehashing will invalidate 'it' - iterator_base new_node = this->create_node(v, it); // throws, strong + // Create the node before rehashing in case it throws. + // throws, no side effects: + node_constructor a(this->node_alloc_, this->bucket_alloc_); + a.construct(v); - // If reserve rehashes, new_node is invalidated. - if (reserve(size() + 1)) // basic/strong - return iterator_base( // no throw - get_bucket(extract_key(v)), // throws, no effects - new_node.local()); - else - return new_node; + // The hash function can throw in get_bucket, but that's okay + // because then only basic exception safety is required. + bucket_ptr base = reserve(size() + 1) ? + get_bucket(extract_key(v)) : it.bucket_; + + link_ptr node = a.release(); + link_node(node, it.local()); + + return iterator_base(base, node); } else { return insert_equivalent(v); // basic/strong @@ -1406,10 +1465,12 @@ namespace boost { { // Throws, but no side effects in this initial code key_type const& k = extract_key(v); - bucket_ptr bucket = get_bucket(k); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->buckets_ + + this->index_from_hash(hash_value); local_iterator_base pos = find_iterator(bucket, k); - if (pos != this->end(bucket)) { // no throw + if (pos.not_finished()) { // no throw // Found existing key, return it. return std::pair( iterator_base(bucket, pos), false); // no throw @@ -1418,12 +1479,20 @@ namespace boost { // Doesn't already exist, add to bucket. // Data is only changed in this block. + // Create the node before rehashing in case it throws. + // throws, no side effects: + node_constructor a(this->node_alloc_, this->bucket_alloc_); + a.construct(v); + // If we resize, then need to recalculate bucket. if(reserve(size() + 1)) // throws, basic/strong - bucket = get_bucket(k); // throws, strong + bucket = this->buckets_ + this->index_from_hash(hash_value); + + link_ptr node = a.release(); + link_node(node, bucket); return std::pair( - this->create_node(v, bucket), true); // throws, strong + iterator_base(bucket, node), true); // throws, strong } } @@ -1488,9 +1557,9 @@ namespace boost { template void insert(InputIterator i, InputIterator j) { - typedef typename boost::iterator_traversal::type + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type iterator_traversal_tag; - insert_for_range(i, j, iterator_traversal_tag()); + insert_for_range(i, j, iterator_traversal_tag); } public: @@ -1512,12 +1581,12 @@ namespace boost { erase_iterator it(find_for_erase(bucket, k)); // Rest is no throw, side effects only after this point. - if (it) { + if (it.not_finished()) { if (EquivalentKeys) { do { ++count; this->delete_node(it); - } while(it && equal(k, *it)); + } while(it.not_finished() && equal(k, *it)); } else { count = 1; @@ -1542,15 +1611,14 @@ namespace boost { size_type count(key_type const& k) const { local_iterator_base it = find_iterator(k); // throws, strong - local_iterator_base end; size_type count = 0; - if(it != end) { + if(it.not_finished()) { if(EquivalentKeys) { do { ++count; it.increment(); - } while (it != end && equal(k, *it)); // throws, strong + } while (it.not_finished() && equal(k, *it)); // throws, strong } else { count = 1; @@ -1568,7 +1636,7 @@ namespace boost { bucket_ptr bucket = get_bucket(k); local_iterator_base it = find_iterator(bucket, k); - if (it != this->end(bucket)) + if (it.not_finished()) return iterator_base(bucket, it); else return this->end(); @@ -1581,15 +1649,14 @@ namespace boost { { bucket_ptr bucket = get_bucket(k); local_iterator_base it = find_iterator(bucket, k); - if (it != this->end(bucket)) { + if (it.not_finished()) { local_iterator_base last = it; if(EquivalentKeys) { - local_iterator_base end = this->end(bucket); local_iterator_base next = last; next.increment(); - while(next != end && equal(k, *next)) { + while(next.not_finished() && equal(k, *next)) { last = next; next.increment(); } @@ -1625,8 +1692,7 @@ namespace boost { key_type const& k) const { local_iterator_base it = this->begin(bucket); - local_iterator_base end = this->end(bucket); - while (it != end && !equal(k, *it)) + while (it.not_finished() && !equal(k, *it)) it.increment(); return it; @@ -1637,7 +1703,7 @@ namespace boost { const { erase_iterator it(bucket); - while(it && !equal(k, *it)) + while(it.not_finished() && !equal(k, *it)) it.next(); return it; @@ -1660,18 +1726,16 @@ namespace boost { class hash_local_iterator : public boost::iterator < std::forward_iterator_tag, - typename get_value_type::type, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, std::ptrdiff_t, - typename get_pointer::type, - typename get_reference::type > + BOOST_DEDUCED_TYPENAME allocator_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_reference::type > { public: - typedef typename hash_local_iterator::pointer pointer; - typedef typename hash_local_iterator::reference reference; - typedef typename get_value_type::type value_type; + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; private: - typedef typename hash_table_data::local_iterator_base base; + typedef BOOST_DEDUCED_TYPENAME hash_table_data::local_iterator_base base; typedef hash_const_local_iterator const_local_iterator; friend class hash_const_local_iterator; @@ -1680,7 +1744,8 @@ namespace boost { public: hash_local_iterator() : base_() {} explicit hash_local_iterator(base x) : base_(x) {} - reference operator*() const { return *base_; } + BOOST_DEDUCED_TYPENAME allocator_reference::type operator*() const + { return *base_; } value_type* operator->() const { return &*base_; } hash_local_iterator& operator++() { base_.increment(); return *this; } hash_local_iterator operator++(int) { hash_local_iterator tmp(base_); base_.increment(); return tmp; } @@ -1694,18 +1759,16 @@ namespace boost { class hash_const_local_iterator : public boost::iterator < std::forward_iterator_tag, - typename get_value_type::type, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, std::ptrdiff_t, - typename get_const_pointer::type, - typename get_const_reference::type > + BOOST_DEDUCED_TYPENAME allocator_const_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_const_reference::type > { public: - typedef typename hash_const_local_iterator::pointer pointer; - typedef typename hash_const_local_iterator::reference reference; - typedef typename get_value_type::type value_type; + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; private: - typedef typename hash_table_data::local_iterator_base base; + typedef BOOST_DEDUCED_TYPENAME hash_table_data::local_iterator_base base; typedef hash_local_iterator local_iterator; friend class hash_local_iterator; base base_; @@ -1714,7 +1777,8 @@ namespace boost { hash_const_local_iterator() : base_() {} explicit hash_const_local_iterator(base x) : base_(x) {} hash_const_local_iterator(local_iterator x) : base_(x.base_) {} - reference operator*() const { return *base_; } + BOOST_DEDUCED_TYPENAME allocator_const_reference::type + operator*() const { return *base_; } value_type const* operator->() const { return &*base_; } hash_const_local_iterator& operator++() { base_.increment(); return *this; } hash_const_local_iterator operator++(int) { hash_const_local_iterator tmp(base_); base_.increment(); return tmp; } @@ -1733,18 +1797,16 @@ namespace boost { class hash_iterator : public boost::iterator < std::forward_iterator_tag, - typename get_value_type::type, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, std::ptrdiff_t, - typename get_pointer::type, - typename get_reference::type > + BOOST_DEDUCED_TYPENAME allocator_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_reference::type > { public: - typedef typename hash_iterator::pointer pointer; - typedef typename hash_iterator::reference reference; - typedef typename get_value_type::type value_type; + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; private: - typedef typename hash_table_data::iterator_base base; + typedef BOOST_DEDUCED_TYPENAME hash_table_data::iterator_base base; typedef hash_const_iterator const_iterator; friend class hash_const_iterator; base base_; @@ -1753,7 +1815,8 @@ namespace boost { hash_iterator() : base_() {} explicit hash_iterator(base const& x) : base_(x) {} - reference operator*() const { return *base_; } + BOOST_DEDUCED_TYPENAME allocator_reference::type + operator*() const { return *base_; } value_type* operator->() const { return &*base_; } hash_iterator& operator++() { base_.increment(); return *this; } hash_iterator operator++(int) { hash_iterator tmp(base_); base_.increment(); return tmp; } @@ -1767,18 +1830,16 @@ namespace boost { class hash_const_iterator : public boost::iterator < std::forward_iterator_tag, - typename get_value_type::type, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, std::ptrdiff_t, - typename get_const_pointer::type, - typename get_const_reference::type > + BOOST_DEDUCED_TYPENAME allocator_const_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_const_reference::type > { public: - typedef typename hash_const_iterator::pointer pointer; - typedef typename hash_const_iterator::reference reference; - typedef typename get_value_type::type value_type; + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; private: - typedef typename hash_table_data::iterator_base base; + typedef BOOST_DEDUCED_TYPENAME hash_table_data::iterator_base base; typedef hash_iterator iterator; friend class hash_iterator; friend class iterator_access; @@ -1789,7 +1850,8 @@ namespace boost { hash_const_iterator() : base_() {} explicit hash_const_iterator(base const& x) : base_(x) {} hash_const_iterator(iterator const& x) : base_(x.base_) {} - reference operator*() const { return *base_; } + BOOST_DEDUCED_TYPENAME allocator_const_reference::type + operator*() const { return *base_; } value_type const* operator->() const { return &*base_; } hash_const_iterator& operator++() { base_.increment(); return *this; } hash_const_iterator operator++(int) { hash_const_iterator tmp(base_); base_.increment(); return tmp; } @@ -1803,7 +1865,7 @@ namespace boost { { public: template - static typename Iterator::base const& get(Iterator const& it) { + static BOOST_DEDUCED_TYPENAME Iterator::base const& get(Iterator const& it) { return it.base_; } }; @@ -1814,18 +1876,21 @@ namespace boost { class hash_types { public: - typedef hash_table hash_table; + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + value_allocator; - typedef typename hash_table::value_allocator value_allocator; - typedef typename hash_table::iterator_base iterator_base; + typedef hash_table hash_table; + typedef hash_table_data data; + typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; typedef hash_const_local_iterator const_local_iterator; typedef hash_local_iterator local_iterator; typedef hash_const_iterator const_iterator; typedef hash_iterator iterator; - typedef typename hash_table::size_type size_type; + typedef BOOST_DEDUCED_TYPENAME data::size_type size_type; typedef std::ptrdiff_t difference_type; }; } // namespace boost::unordered_detail diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index c8a351b0..7e555586 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -51,13 +51,20 @@ namespace boost public: // types + /*! Key must be Assignable and CopyConstructible. + */ typedef Key key_type; typedef std::pair value_type; typedef T mapped_type; + /*! Hash is a unary function object type such for hf of type hasher + * hf(x) has type std::size_t. + */ typedef Hash hasher; + /*! Pred is a binary predicate that takes two arguments of type Key. + * Pred is an equivalence realtion + */ typedef Pred key_equal; - // This version can be used when compiling against CVS. typedef Alloc allocator_type; typedef typename allocator_type::pointer pointer; typedef typename allocator_type::const_pointer const_pointer; @@ -69,11 +76,24 @@ namespace boost typedef typename implementation_defined::iterator iterator; typedef typename implementation_defined::const_iterator const_iterator; + + /*! A local_iterator object may be used to iterate through a single + * bucket, but may not be used to iterate across buckets. + */ typedef typename implementation_defined::local_iterator local_iterator; - typedef typename implementation_defined::const_local_iterator const_local_iterator; + + /*! A const_local_iterator object may be used to iterate through a single + * bucket, but may not be used to iterate across buckets. + */ + typedef typename implementation_defined::const_local_iterator + const_local_iterator; // construct/destroy/copy + /*! Constructs an empty container with at least n buckets, using hf as + * the hash function and eq as the key equality predicate. a is used + * as the allocator. + */ explicit unordered_map( size_type n = boost::unordered_detail::default_initial_bucket_count, const hasher &hf = hasher(), @@ -83,6 +103,10 @@ namespace boost { } + /*! Constructs an empty container with at least n buckets, using hf as + * the hash function and eq as the key equality predicate, and inserts + * elements from [i,j) into it. a is used as the allocator. + */ template unordered_map(InputIterator f, InputIterator l, size_type n = boost::unordered_detail::default_initial_bucket_count, @@ -339,8 +363,6 @@ namespace boost typedef Hash hasher; typedef Pred key_equal; - // This version can be used when compiling against CVS. - typedef Alloc allocator_type; typedef typename allocator_type::pointer pointer; typedef typename allocator_type::const_pointer const_pointer; From 78e843f3cb5399956c18eed054a0c29818175ce5 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 4 Aug 2005 22:45:46 +0000 Subject: [PATCH 004/175] Update the links in the unordered headers. [SVN r2676] --- include/boost/unordered_map.hpp | 4 ++-- include/boost/unordered_set.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 7e555586..0e68f640 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -31,7 +31,7 @@ namespace boost { //! An unordered associative container that associates unique keys with another value. /*! For full details see section 6.3.4.4 of the Technical report. - * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1836.pdf */ template Date: Sat, 5 Nov 2005 16:57:31 +0000 Subject: [PATCH 005/175] Better results for max_size/max_bucket_count and some fixes for intel in strict mode. [SVN r2730] --- include/boost/unordered/detail/hash_table.hpp | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index fe0e3838..9638e15e 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,13 @@ namespace boost { swap(x, y); } + std::size_t float_to_size_t(float f) + { + return f > (std::numeric_limits::max)() ? + (std::numeric_limits::max)() : + static_cast(f); + } + // prime number list, accessor static const std::size_t prime_list[] = { @@ -83,6 +91,15 @@ namespace boost { return *bound; } + // no throw + inline std::size_t prev_prime(std::size_t n) { + std::size_t const* bound = + std::upper_bound(prime_list,prime_list + 28, n); + if(bound != prime_list) + bound--; + return *bound; + } + // pair_cast - used to convert between pair types. template @@ -1126,7 +1143,9 @@ namespace boost { // no throw size_type max_size() const { - return this->node_alloc_.max_size(); + // size < mlf_ * count + return float_to_size_t(ceil( + max_bucket_count() * mlf_)) - 1; } // strong safety @@ -1151,7 +1170,8 @@ namespace boost { // no throw size_type max_bucket_count() const { - return this->bucket_alloc_.max_size(); + // -1 to account for the end marker. + return prev_prime(this->bucket_alloc_.max_size() - 1); } private: @@ -1179,8 +1199,7 @@ namespace boost { // From 6.3.1/13: // Only resize when size >= mlf_ * count - max_load_ = static_cast( - ceil(mlf_ * this->bucket_count_)); + max_load_ = float_to_size_t(ceil(mlf_ * this->bucket_count_)); } // basic exception safety @@ -1189,6 +1208,7 @@ namespace boost { bool need_to_reserve = n >= max_load_; // throws - basic: if (need_to_reserve) rehash_impl(min_buckets_for_size(n)); + BOOST_ASSERT(n < max_load_); return need_to_reserve; } @@ -1201,8 +1221,12 @@ namespace boost { } // no throw + // + // TODO: the argument is a hint. So don't use it if it's + // unreasonably small. void max_load_factor(float z) { + BOOST_ASSERT(z > 0); mlf_ = z; calculate_max_load(); } @@ -1357,11 +1381,18 @@ namespace boost { { // Effects only in this block: + // Create the node before rehashing in case it throws. + // throws, no side effects: + node_constructor a(this->node_alloc_, this->bucket_alloc_); + a.construct(value_type(k, mapped_type())); + if (reserve(size() + 1)) // basic/strong bucket = get_bucket(k); // throws, strong - return *this->create_node( // throws, strong - value_type(k, mapped_type()), - bucket); + + link_ptr node = a.release(); + this->link_node(node, bucket); + + return *local_iterator_base(node); } } @@ -1423,9 +1454,9 @@ namespace boost { // I'm relying on local_iterator_base not being invalidated by // the rehash here. if(position.not_finished()) - link_node(node, position); + this->link_node(node, position); else - link_node(node, bucket); + this->link_node(node, bucket); return iterator_base(bucket, node); } @@ -1446,7 +1477,7 @@ namespace boost { get_bucket(extract_key(v)) : it.bucket_; link_ptr node = a.release(); - link_node(node, it.local()); + this->link_node(node, it.local()); return iterator_base(base, node); } @@ -1489,7 +1520,7 @@ namespace boost { bucket = this->buckets_ + this->index_from_hash(hash_value); link_ptr node = a.release(); - link_node(node, bucket); + this->link_node(node, bucket); return std::pair( iterator_base(bucket, node), true); // throws, strong From 8214c43060e960561827709b1f9fafbb60cbea8f Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 5 Nov 2005 17:24:20 +0000 Subject: [PATCH 006/175] Unit tests for unordered containers. [SVN r2731] --- test/Jamfile.v2 | 63 ++++ test/assign_tests.cpp | 86 ++++++ test/basic_tests.cpp | 207 +++++++++++++ test/check_return_type.hpp | 36 +++ test/clear_tests.cpp | 32 ++ test/concept_test.cpp | 32 ++ test/const_iterator_fail_test.cpp | 16 + test/const_local_iterator_fail_test.cpp | 18 ++ test/construct_tests.cpp | 273 +++++++++++++++++ test/container_tests.cpp | 228 ++++++++++++++ test/containers.hpp | 76 +++++ test/copy_construct_tests.cpp | 119 ++++++++ test/count_tests.cpp | 65 ++++ test/equal_range_tests.cpp | 100 +++++++ test/equivalent.hpp | 112 +++++++ test/erase_tests.cpp | 190 ++++++++++++ test/find_tests.cpp | 78 +++++ test/helpers/accessors.hpp | 12 + test/helpers/allocator.cpp | 209 +++++++++++++ test/helpers/allocator.hpp | 376 ++++++++++++++++++++++++ test/helpers/base.cpp | 31 ++ test/helpers/base.hpp | 15 + test/helpers/config.hpp | 18 ++ test/helpers/constructors.hpp | 69 +++++ test/helpers/equivalent.hpp | 45 +++ test/helpers/exception.cpp | 85 ++++++ test/helpers/exception.hpp | 60 ++++ test/helpers/exception_test.cpp | 108 +++++++ test/helpers/exception_test.hpp | 33 +++ test/helpers/exception_trigger.hpp | 49 +++ test/helpers/functional.cpp | 140 +++++++++ test/helpers/functional.hpp | 80 +++++ test/helpers/generators.cpp | 43 +++ test/helpers/generators.hpp | 41 +++ test/helpers/input_iterator_adaptor.hpp | 35 +++ test/helpers/invariant_checker.cpp | 54 ++++ test/helpers/invariant_checker.hpp | 78 +++++ test/helpers/less.hpp | 37 +++ test/helpers/member.cpp | 79 +++++ test/helpers/member.hpp | 40 +++ test/helpers/metafunctions.hpp | 100 +++++++ test/helpers/random_values.hpp | 203 +++++++++++++ test/helpers/strong.cpp | 32 ++ test/helpers/strong.hpp | 78 +++++ test/helpers/unit_test.hpp | 77 +++++ test/insert_tests.cpp | 193 ++++++++++++ test/invariant.hpp | 128 ++++++++ test/iterator_tests.cpp | 141 +++++++++ test/map_operator_tests.cpp | 69 +++++ test/max_load_factor_test.cpp | 170 +++++++++++ test/next_prime_tests.cpp | 18 ++ test/set_assign_fail_test.cpp | 14 + test/strong.hpp | 102 +++++++ test/swap_tests.hpp | 120 ++++++++ test/swap_tests.ipp | 102 +++++++ test/swap_tests1.cpp | 7 + test/swap_tests2.cpp | 7 + test/swap_tests3.cpp | 7 + test/type_tests.cpp | 206 +++++++++++++ 59 files changed, 5242 insertions(+) create mode 100644 test/Jamfile.v2 create mode 100644 test/assign_tests.cpp create mode 100644 test/basic_tests.cpp create mode 100644 test/check_return_type.hpp create mode 100644 test/clear_tests.cpp create mode 100644 test/concept_test.cpp create mode 100644 test/const_iterator_fail_test.cpp create mode 100644 test/const_local_iterator_fail_test.cpp create mode 100644 test/construct_tests.cpp create mode 100644 test/container_tests.cpp create mode 100644 test/containers.hpp create mode 100644 test/copy_construct_tests.cpp create mode 100644 test/count_tests.cpp create mode 100644 test/equal_range_tests.cpp create mode 100644 test/equivalent.hpp create mode 100644 test/erase_tests.cpp create mode 100644 test/find_tests.cpp create mode 100644 test/helpers/accessors.hpp create mode 100644 test/helpers/allocator.cpp create mode 100644 test/helpers/allocator.hpp create mode 100644 test/helpers/base.cpp create mode 100644 test/helpers/base.hpp create mode 100644 test/helpers/config.hpp create mode 100644 test/helpers/constructors.hpp create mode 100644 test/helpers/equivalent.hpp create mode 100644 test/helpers/exception.cpp create mode 100644 test/helpers/exception.hpp create mode 100644 test/helpers/exception_test.cpp create mode 100644 test/helpers/exception_test.hpp create mode 100644 test/helpers/exception_trigger.hpp create mode 100644 test/helpers/functional.cpp create mode 100644 test/helpers/functional.hpp create mode 100644 test/helpers/generators.cpp create mode 100644 test/helpers/generators.hpp create mode 100644 test/helpers/input_iterator_adaptor.hpp create mode 100644 test/helpers/invariant_checker.cpp create mode 100644 test/helpers/invariant_checker.hpp create mode 100644 test/helpers/less.hpp create mode 100644 test/helpers/member.cpp create mode 100644 test/helpers/member.hpp create mode 100644 test/helpers/metafunctions.hpp create mode 100644 test/helpers/random_values.hpp create mode 100644 test/helpers/strong.cpp create mode 100644 test/helpers/strong.hpp create mode 100644 test/helpers/unit_test.hpp create mode 100644 test/insert_tests.cpp create mode 100644 test/invariant.hpp create mode 100644 test/iterator_tests.cpp create mode 100644 test/map_operator_tests.cpp create mode 100644 test/max_load_factor_test.cpp create mode 100644 test/next_prime_tests.cpp create mode 100644 test/set_assign_fail_test.cpp create mode 100644 test/strong.hpp create mode 100644 test/swap_tests.hpp create mode 100644 test/swap_tests.ipp create mode 100644 test/swap_tests1.cpp create mode 100644 test/swap_tests2.cpp create mode 100644 test/swap_tests3.cpp create mode 100644 test/type_tests.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 new file mode 100644 index 00000000..7704f38c --- /dev/null +++ b/test/Jamfile.v2 @@ -0,0 +1,63 @@ + +# Copyright Daniel James 2005. Use, modification, and distribution are +# 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) + +import testing ; + +project unordered-test + : requirements + intel-linux:"-strict_ansi -cxxlib-icc" + # off + # TODO: Make this an option: + REDUCED_TESTS + ; + +lib helpers + : + [ glob helpers/*.cpp ] + ; + +framework = helpers/static /boost/test//boost_unit_test_framework ; + +test-suite helpers-test + : + [ run-fail helpers-test/simple_test.cpp $(framework) ] + [ run-fail helpers-test/allocator_test.cpp $(framework) ] + [ run helpers-test/exception_test.cpp $(framework) ] + ; + +test-suite compile-tests + : + [ compile-fail const_local_iterator_fail_test.cpp ] + [ compile-fail const_iterator_fail_test.cpp ] + [ compile-fail set_assign_fail_test.cpp ] + [ compile type_tests.cpp ] + [ run concept_test.cpp ] + ; + +test-suite basic-tests + : + [ run container_tests.cpp $(framework) ] + [ run iterator_tests.cpp $(framework) ] + [ run next_prime_tests.cpp $(framework) ] + [ run max_load_factor_test.cpp $(framework) ] + ; + +test-suite less-base-tests + : + [ run construct_tests.cpp $(framework) ] + [ run find_tests.cpp $(framework) ] + [ run count_tests.cpp $(framework) ] + [ run equal_range_tests.cpp $(framework) ] + [ run copy_construct_tests.cpp $(framework) ] + [ run swap_tests1.cpp $(framework) ] + [ run swap_tests2.cpp $(framework) ] + [ run swap_tests3.cpp $(framework) ] + [ run assign_tests.cpp $(framework) ] + [ run clear_tests.cpp $(framework) ] + [ run erase_tests.cpp $(framework) ] + [ run insert_tests.cpp $(framework) ] + [ run map_operator_tests.cpp $(framework) ] + ; + diff --git a/test/assign_tests.cpp b/test/assign_tests.cpp new file mode 100644 index 00000000..8d3e45ae --- /dev/null +++ b/test/assign_tests.cpp @@ -0,0 +1,86 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include "./helpers/unit_test.hpp" +#include "./helpers/exception_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/constructors.hpp" +#include "./helpers/constructors.hpp" +#include "./helpers/equivalent.hpp" +#include "./invariant.hpp" + +const int num_values = 50; + +META_FUNC_TEST_CASE(assign_test1, Container) +{ + test::constructors constructor; + + test::random_values values(num_values); + Container x(values.begin(), values.end(), 0, + constructor.hasher(55), constructor.key_equal(55), + constructor.allocator(10)); + x.max_load_factor(0.1); + + EXCEPTION_TEST(10000) + { + DEACTIVATE_EXCEPTIONS; + Container y; + INVARIANT_CHECK(y); + ACTIVATE_EXCEPTIONS; + + BOOST_CHECKPOINT("y = x"); + y = x; + + { + DEACTIVATE_EXCEPTIONS; + BOOST_CHECK_EQUAL(y.size(), x.size()); + BOOST_CHECK(test::equivalent(y.hash_function(), x.hash_function())); + BOOST_CHECK(test::equivalent(y.key_eq(), x.key_eq())); + BOOST_CHECK_EQUAL(y.max_load_factor(), x.max_load_factor()); + BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator())); + BOOST_CHECK(y.load_factor() <= y.max_load_factor()); + test::check_invariants(); + } + + BOOST_CHECKPOINT("y = y"); + y = y; + + { + DEACTIVATE_EXCEPTIONS; + BOOST_CHECK_EQUAL(y.size(), x.size()); + BOOST_CHECK(test::equivalent(y.hash_function(), x.hash_function())); + BOOST_CHECK(test::equivalent(y.key_eq(), x.key_eq())); + BOOST_CHECK_EQUAL(y.max_load_factor(), x.max_load_factor()); + BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator())); + BOOST_CHECK(y.load_factor() <= y.max_load_factor()); + test::check_invariants(); + } + + BOOST_CHECKPOINT("y = Container(values.begin(), values.end())"); + y = Container(values.begin(), values.end()); + + { + DEACTIVATE_EXCEPTIONS; + BOOST_CHECK_EQUAL(y.size(), x.size()); + BOOST_CHECK(test::equivalent(y.hash_function(), constructor.hasher())); + BOOST_CHECK(test::equivalent(y.key_eq(), constructor.key_equal())); + BOOST_CHECK_EQUAL(y.max_load_factor(), 1.0); + BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator())); + BOOST_CHECK(y.load_factor() <= 1.0); + test::check_invariants(); + } + } + EXCEPTION_TEST_END +} + +AUTO_META_TESTS( + (assign_test1), + CONTAINER_SEQ +) diff --git a/test/basic_tests.cpp b/test/basic_tests.cpp new file mode 100644 index 00000000..177edbcb --- /dev/null +++ b/test/basic_tests.cpp @@ -0,0 +1,207 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include +#include +#include +#include +#include +#include +#include "./helpers/unit_test.hpp" +#include "./helpers/random_values.hpp" + +typedef double comparison_type; + +template +struct check_return_type +{ + template + static void equals(T2) + { + BOOST_MPL_ASSERT((boost::is_same)); + } + + template + static void equals_ref(T2&) + { + BOOST_MPL_ASSERT((boost::is_same)); + } + + template + static void convertible(T2) + { + BOOST_MPL_ASSERT((boost::is_convertible)); + } +}; + +// 23.1.5 +template +void container_tests(X*, T*) +{ + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename X::difference_type difference_type; + typedef typename X::size_type size_type; + + BOOST_MPL_ASSERT((boost::is_same)); + // TODO: Actually 'lvalue of T' + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + // TODO: Iterator checks. + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); + BOOST_MPL_ASSERT((boost::is_convertible)); + + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); + + BOOST_MPL_ASSERT((boost::mpl::bool_::is_signed>)); + BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + BOOST_MPL_ASSERT_NOT((boost::mpl::bool_::is_signed>)); + BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); + BOOST_CHECK((comparison_type)(std::numeric_limits::max)() + > (comparison_type)(std::numeric_limits::max)()); + + { + X u; + BOOST_CHECK(u.size() == 0); + } +} + +template +void container_tests2(X& a) +{ + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename X::difference_type difference_type; + typedef typename X::size_type size_type; + + { + X u(a); + // BOOST_CHECK_EQUAL(a, u); + } + + { + X u = a; + // BOOST_CHECK_EQUAL(a, u); + } + + // Test that destructor destructs all elements + + { + X const a_const = a; + + check_return_type::equals(a.begin()); + check_return_type::equals(a_const.begin()); + check_return_type::equals(a.end()); + check_return_type::equals(a_const.end()); + } + + // No tests for ==, != since they're not required for unordered containers. + + { + X b; + a.swap(b); + a.swap(b); + } + + { + X u; + X& r = u; + + check_return_type::equals_ref(r = a); + } + + { + check_return_type::equals(a.size()); + BOOST_CHECK_EQUAL(a.size(), (size_type) std::distance(a.begin(), a.end())); + } + + { + check_return_type::equals(a.max_size()); + } + + { + check_return_type::convertible(a.empty()); + BOOST_CHECK_EQUAL(a.empty(), a.size() == 0); + } + + // TODO: member function size return number of elements. + // Semantics determined by constructors/inserts/erases. + // ie. will be tested in their tests. + + // begin() returns first element. + // end() return past the end iterator. + // Can't really test this for unordered containers. + + { + if(a.empty()) + BOOST_CHECK(a.begin() == a.end()); + } + + // TODO: test that const_iterator can replace iterator in comparisons. + + // TODO: test allocators + + // No need to test reversible. + + // Exceptions: + // No copy constructor or assignment operator of a returned iterator throws an exception. + + // No swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. + // Unless otherwise specified, iterators are not invalidated, and the values of objects aren't changed. +} + +BOOST_AUTO_UNIT_TEST(basic_tests) +{ + // I don't use the normal template mechanism here, as I want to specify the + // member type explicitly. + container_tests((boost::unordered_set*) 0, (int*) 0); + container_tests((boost::unordered_map*) 0, (std::pair*) 0); + container_tests((boost::unordered_multiset*) 0, (std::string*) 0); + container_tests((boost::unordered_multimap*) 0, + (std::pair*) 0); +} + +struct test_structure { int* x; }; + +META_FUNC_TEST_CASE(basic_tests_2, Container) +{ + + Container a; + container_tests2(a); + + { + test::random_values values1((std::min)(10u, a.max_size())); + Container b(values1.begin(), values1.end()); + container_tests2(b); + } + + { + test::random_values values2((std::min)(1000u, a.max_size())); + Container c(values2.begin(), values2.end()); + container_tests2(c); + } + + { + test::random_values values3((std::min)(100000u, a.max_size())); + Container d(values3.begin(), values3.end()); + container_tests2(d); + } +} + +AUTO_META_TESTS( + (basic_tests_2), + CONTAINER_SEQ +) diff --git a/test/check_return_type.hpp b/test/check_return_type.hpp new file mode 100644 index 00000000..a201e75d --- /dev/null +++ b/test/check_return_type.hpp @@ -0,0 +1,36 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TESTS_CHECK_RETURN_TYPE_HEADER) +#define BOOST_UNORDERED_TESTS_CHECK_RETURN_TYPE_HEADER + +#include + +template +struct check_return_type +{ + template + static int equals(T2) + { + BOOST_MPL_ASSERT((boost::is_same)); + return 0; + } + + template + static int equals_ref(T2&) + { + BOOST_MPL_ASSERT((boost::is_same)); + return 0; + } + + template + static int convertible(T2) + { + BOOST_MPL_ASSERT((boost::is_convertible)); + return 0; + } +}; + +#endif diff --git a/test/clear_tests.cpp b/test/clear_tests.cpp new file mode 100644 index 00000000..558ed539 --- /dev/null +++ b/test/clear_tests.cpp @@ -0,0 +1,32 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include "./helpers/unit_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/constructors.hpp" +#include "./invariant.hpp" + +META_FUNC_TEST_CASE(clear_test, Container) +{ + test::constructors constructor; + test::random_values values(100); + Container x(values.begin(), values.end(), 0, + constructor.hasher(55), constructor.key_equal(55), + constructor.allocator(10)); + + x.clear(); + BOOST_CHECK(x.empty()); + test::invariant_check(x); +} + +AUTO_META_TESTS( + (clear_test), + CONTAINER_SEQ +) diff --git a/test/concept_test.cpp b/test/concept_test.cpp new file mode 100644 index 00000000..ddd98845 --- /dev/null +++ b/test/concept_test.cpp @@ -0,0 +1,32 @@ +#include + +#include +#include + +int main() +{ + using namespace boost; + + typedef boost::unordered_set UnorderedSet; + typedef boost::unordered_multiset UnorderedMultiSet; + typedef boost::unordered_map UnorderedMap; + typedef boost::unordered_multimap UnorderedMultiMap; + + function_requires< UnorderedAssociativeContainerConcept >(); + function_requires< SimpleAssociativeContainerConcept >(); + function_requires< UniqueAssociativeContainerConcept >(); + + function_requires< UnorderedAssociativeContainerConcept >(); + function_requires< SimpleAssociativeContainerConcept >(); + function_requires< MultipleAssociativeContainerConcept >(); + + function_requires< UnorderedAssociativeContainerConcept >(); + function_requires< UniqueAssociativeContainerConcept >(); + function_requires< PairAssociativeContainerConcept >(); + + function_requires< UnorderedAssociativeContainerConcept >(); + function_requires< MultipleAssociativeContainerConcept >(); + function_requires< PairAssociativeContainerConcept >(); + + return 0; +} diff --git a/test/const_iterator_fail_test.cpp b/test/const_iterator_fail_test.cpp new file mode 100644 index 00000000..fcd7b82a --- /dev/null +++ b/test/const_iterator_fail_test.cpp @@ -0,0 +1,16 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include + +void func() +{ + typedef boost::unordered_map map; + typedef map::iterator iterator; + typedef map::const_iterator const_iterator; + + const_iterator x; + iterator y(x); +} diff --git a/test/const_local_iterator_fail_test.cpp b/test/const_local_iterator_fail_test.cpp new file mode 100644 index 00000000..348c9b5b --- /dev/null +++ b/test/const_local_iterator_fail_test.cpp @@ -0,0 +1,18 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include + +void func() +{ + // This is only required to fail for unordered maps & multimaps as for sets + // and multisets both iterator and const_iterator are const. + typedef boost::unordered_map map; + typedef map::local_iterator local_iterator; + typedef map::const_local_iterator const_local_iterator; + + const_local_iterator x; + local_iterator y(x); +} diff --git a/test/construct_tests.cpp b/test/construct_tests.cpp new file mode 100644 index 00000000..2c9cfb99 --- /dev/null +++ b/test/construct_tests.cpp @@ -0,0 +1,273 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include "./helpers/unit_test.hpp" +#include "./helpers/exception_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/input_iterator_adaptor.hpp" +#include "./helpers/constructors.hpp" +#include "./helpers/equivalent.hpp" +#include "./invariant.hpp" + +META_FUNC_TEST_CASE(empty_construct_test1, Container) +{ + test::constructors constructor; + + EXCEPTION_TEST(1000) + { + // TR1 6.3.1/9 row 4 + Container x(100, constructor.hasher(55), constructor.key_equal(55)); + BOOST_CHECK(x.bucket_count() >= 100); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); + + // TODO: Where? + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(empty_construct_test2, Container) +{ + test::constructors constructor; + + EXCEPTION_TEST(1000) + { + // TR1 6.3.1/9 row 5 + + // I can only use the default hasher here - as it'll match the default + // key_equal. + Container x(100, constructor.hasher()); + BOOST_CHECK(x.empty()); + BOOST_CHECK(x.bucket_count() >= 100); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); + + // TODO: Where? + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(empty_construct_test3, Container) +{ + test::constructors constructor; + + EXCEPTION_TEST(1000) + { + // TR1 6.3.1/9 row 6 + + Container x(200); + BOOST_CHECK(x.empty()); + BOOST_CHECK(x.bucket_count() >= 200); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); + + // TODO: Where? + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(empty_construct_test4, Container) +{ + test::constructors constructor; + + EXCEPTION_TEST(1000) + { + // TR1 6.3.1/9 row 7 + + Container x; + BOOST_CHECK(x.empty()); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); + + // TODO: Where? + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(empty_construct_test5, Container) +{ + test::constructors constructor; + + EXCEPTION_TEST(1000) + { + // TODO: Where? + Container x(100, constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); + BOOST_CHECK(x.empty()); + BOOST_CHECK(x.bucket_count() >= 100); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(range_construct_test1, Container) +{ + test::constructors constructor; + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + // TR1 6.3.1/9 row 8 + + Container x(values.begin(), values.end(), 100, + constructor.hasher(55), constructor.key_equal(55)); + BOOST_CHECK(x.bucket_count() >= 100); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); + // TODO: Check that values are in container. + + // TODO: Where? + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(range_construct_test2, Container) +{ + test::constructors constructor; + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + // TR1 6.3.1/9 row 9 + + Container x(values.begin(), values.end(), 100, constructor.hasher()); + BOOST_CHECK(x.bucket_count() >= 100); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); + // TODO: Check that values are in container. + + // TODO: Where? + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(range_construct_test3, Container) +{ + test::constructors constructor; + test::random_values values(20); + + EXCEPTION_TEST(1000) + { + // TR1 6.3.1/9 row 10 + + Container x(values.begin(), values.end(), 10); + BOOST_CHECK(x.bucket_count() >= 10); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); + // TODO: Check that values are in container. + + // TODO: Where? + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(range_construct_test4, Container) +{ + test::constructors constructor; + test::random_values values(20); + + EXCEPTION_TEST(1000) + { + // TR1 6.3.1/9 row 11 + + Container x(values.begin(), values.end()); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); + // TODO: Check that values are in container. + + // TODO: Where? + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(range_construct_test5, Container) +{ + test::constructors constructor; + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + // TODO: Where? + Container x(values.begin(), values.end(), 10, + constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); + BOOST_CHECK(x.bucket_count() >= 10); + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END +} + +// TODO: I should probably just make all the tests run from an input iterator. +META_FUNC_TEST_CASE(input_iterator_construct_test1, Container) +{ + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + Container x( + test::make_input_iterator(values.begin()), + test::make_input_iterator(values.end()) + ); + BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); + + test::invariant_check(x); + } + EXCEPTION_TEST_END + +} + +AUTO_META_TESTS( + (empty_construct_test1)(empty_construct_test2)(empty_construct_test3) + (empty_construct_test4)(empty_construct_test5) + (range_construct_test1)(range_construct_test2)(range_construct_test3) + (range_construct_test4)(range_construct_test5) + (input_iterator_construct_test1), + CONTAINER_SEQ +) diff --git a/test/container_tests.cpp b/test/container_tests.cpp new file mode 100644 index 00000000..48aa47fb --- /dev/null +++ b/test/container_tests.cpp @@ -0,0 +1,228 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include +#include +#include +#include +#include +#include +#include "./helpers/unit_test.hpp" +#include "./helpers/random_values.hpp" +#include "./equivalent.hpp" +#include "./check_return_type.hpp" + +typedef double comparison_type; + +// 23.1/5 +template +void container_tests(X*, T*) +{ + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename X::difference_type difference_type; + typedef typename X::size_type size_type; + + BOOST_MPL_ASSERT((boost::is_same)); + // TODO: Actually 'lvalue of T'/'const lvalue of T' + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + // TODO: Iterator checks. + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); + BOOST_MPL_ASSERT((boost::is_convertible)); + + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); + + BOOST_MPL_ASSERT((boost::mpl::bool_::is_signed>)); + BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + BOOST_MPL_ASSERT_NOT((boost::mpl::bool_::is_signed>)); + BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); + BOOST_CHECK((comparison_type)(std::numeric_limits::max)() + > (comparison_type)(std::numeric_limits::max)()); + + { + X u; + BOOST_CHECK(u.size() == 0); + BOOST_CHECK(X().size() == 0); + } +} + +template +void container_tests2(X& a) +{ + // 23.1/5 continued + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename X::difference_type difference_type; + typedef typename X::size_type size_type; + + test::unordered_equivalence_tester equivalent(a); + + { + X u(a); + equivalent.test(u); + } + + { + X u = a; + equivalent.test(u); + } + + // Test that destructor destructs all elements (already done by test::allocator/test::member?). + + { + X const& a_const = a; + + check_return_type::equals(a.begin()); + check_return_type::equals(a_const.begin()); + check_return_type::equals(a.end()); + check_return_type::equals(a_const.end()); + } + + // No tests for ==, != since they're not required for unordered containers. + + { + X b; + a.swap(b); + BOOST_CHECK(a.empty()); + equivalent.test(b); + a.swap(b); + equivalent.test(a); + BOOST_CHECK(b.empty()); + } + + { + X u; + X& r = u; + + check_return_type::equals_ref(r = a); + equivalent.test(r); + } + + { + check_return_type::equals(a.size()); + BOOST_CHECK_EQUAL(a.size(), (size_type) std::distance(a.begin(), a.end())); + } + + { + check_return_type::equals(a.max_size()); + // TODO: Check that a.max_size() == size of the largest possible container + // How do I do that? test::allocator checks that allocations don't exceed + // that allocator's maximum size. Could check that max_size() works, and + // that max_size() + 1 doesn't. Only practicle for small max_size though - + // and it might be possible to implement unordered containers such that + // max_size() > alloc.max_size(). Or is it? + } + + { + check_return_type::convertible(a.empty()); + BOOST_CHECK_EQUAL(a.empty(), a.size() == 0); + } + + // 23.1/7 + { + if(a.empty()) + BOOST_CHECK(a.begin() == a.end()); + } + + // 23.1/8 + { + iterator i = a.begin(), j = a.end(); + const_iterator ci = a.begin(), cj = a.end(); + + if(a.empty()) { + BOOST_CHECK(i == j); + BOOST_CHECK(i == cj); + BOOST_CHECK(ci == j); + BOOST_CHECK(ci == cj); + + BOOST_CHECK(!(i != j)); + BOOST_CHECK(!(i != cj)); + BOOST_CHECK(!(ci != j)); + BOOST_CHECK(!(ci != cj)); + } + else { + BOOST_CHECK(!(i == j)); + BOOST_CHECK(!(i == cj)); + BOOST_CHECK(!(ci == j)); + BOOST_CHECK(!(ci == cj)); + + BOOST_CHECK(i != j); + BOOST_CHECK(i != cj); + BOOST_CHECK(ci != j); + BOOST_CHECK(ci != cj); + } + } + + // TODO: 23.1/9 - Make sure this is checked for all constructors. + { + check_return_type::equals(a.get_allocator()); + } + + // TODO: 23.1/11 - Exception safety: + // No erase function throws an exception. + // No copy constructor or assignment operator of a returned iterator throws an exception. + + // No swap() function throws an exception unless that exception is thrown by the copy constructor of assignment operator of the container's Compare object. + // No swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. + // + // TODO: 21.1/12 + // + // Unless otherwise specified - iterators not invalidated, values not changed. +} + +BOOST_AUTO_UNIT_TEST(basic_tests) +{ + // I don't use the normal template mechanism here, as I want to specify the + // member type explicitly. + container_tests((boost::unordered_set*) 0, (int*) 0); + container_tests((boost::unordered_map*) 0, (std::pair*) 0); + container_tests((boost::unordered_multiset*) 0, (std::string*) 0); + container_tests((boost::unordered_multimap*) 0, + (std::pair*) 0); +} + +struct test_structure { int* x; }; + +META_FUNC_TEST_CASE(basic_tests_2, Container) +{ + + Container a; + container_tests2(a); + + { + test::random_values values1((std::min)(10u, a.max_size())); + Container b(values1.begin(), values1.end()); + container_tests2(b); + } + + { + test::random_values values2((std::min)(1000u, a.max_size())); + Container c(values2.begin(), values2.end()); + container_tests2(c); + } + + { + test::random_values values3((std::min)(100000u, a.max_size())); + Container d(values3.begin(), values3.end()); + container_tests2(d); + } +} + +AUTO_META_TESTS( + (basic_tests_2), + CONTAINER_SEQ +) diff --git a/test/containers.hpp b/test/containers.hpp new file mode 100644 index 00000000..674fe743 --- /dev/null +++ b/test/containers.hpp @@ -0,0 +1,76 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TESTS_CONTAINERS_HEADER) +#define BOOST_UNORDERED_TESTS_CONTAINERS_HEADER + +#include +#include +#include +#include +#include "./helpers/allocator.hpp" +#include "./helpers/functional.hpp" +#include "./helpers/member.hpp" + +typedef boost::unordered_set< + test::member + > test_set; +typedef boost::unordered_multiset< + test::member, test::hash, test::equals, + test::allocator + > test_multiset; +typedef boost::unordered_map< + test::member, test::member, test::hash, test::equals, + test::allocator > + > test_map; +typedef boost::unordered_multimap< + test::member, test::member, test::hash, test::equals, + test::minimal_allocator > + > test_multimap; + +typedef boost::unordered_set< + int, test::hash, test::equals, + test::allocator + > set_int; +typedef boost::unordered_multiset< + std::string, test::hash, test::equals, + test::allocator + > multiset_string; +typedef boost::unordered_map< + test::member, std::string, test::hash, test::equals, + test::allocator > + > map_member_string; +typedef boost::unordered_multimap< + int, test::member, test::hash, test::equals, + test::allocator > + > multimap_int_member; +typedef boost::unordered_map< + char, test::member, test::hash, test::equals, + test::allocator > + > map_char_member; +typedef boost::unordered_multiset< + char, test::hash, test::equals, + test::allocator + > multiset_char; + +typedef std::pair pair1; +typedef std::pair pair2; +typedef std::pair pair3; +BOOST_TT_BROKEN_COMPILER_SPEC(std::string) +BOOST_TT_BROKEN_COMPILER_SPEC(pair1) +BOOST_TT_BROKEN_COMPILER_SPEC(pair2) +BOOST_TT_BROKEN_COMPILER_SPEC(pair3) +BOOST_TT_BROKEN_COMPILER_SPEC(test::member) + +#ifdef REDUCED_TESTS +#define CONTAINER_SEQ \ + (test_set)(test_multiset)(test_map)(test_multimap) +#else +#define CONTAINER_SEQ \ + (set_int)(multiset_string)(map_member_string) \ + (multimap_int_member)(map_char_member)(multiset_char) +#endif + +#endif diff --git a/test/copy_construct_tests.cpp b/test/copy_construct_tests.cpp new file mode 100644 index 00000000..2bedd4e8 --- /dev/null +++ b/test/copy_construct_tests.cpp @@ -0,0 +1,119 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include "./helpers/unit_test.hpp" +#include "./helpers/exception_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/constructors.hpp" +#include "./equivalent.hpp" +#include "./invariant.hpp" + +// 23.1/5 + TR1 6.3.1/9 row 12 +META_FUNC_TEST_CASE(empty_copy_test1, X) +{ + X x; + test::unordered_equivalence_tester equivalent(x); + + EXCEPTION_TEST(1000) + { + X y(x); + equivalent.test(y); + test::invariant_check(y); + } + EXCEPTION_TEST_END + + test::invariant_check(x); +} + +META_FUNC_TEST_CASE(empty_copy_test2, X) +{ + test::constructors constructor; + X x(100, constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); + x.max_load_factor(4.0); + test::unordered_equivalence_tester equivalent(x); + + EXCEPTION_TEST(1000) + { + X y(x); + equivalent.test(y); + test::invariant_check(y); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(range_copy_construct,X) +{ + test::constructors constructor; + test::random_values values(10); + + X x(values.begin(), values.end(), 100, + constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); + x.max_load_factor(4.0); + test::unordered_equivalence_tester equivalent(x); + + EXCEPTION_TEST(1000) + { + X y(x); + equivalent.test(y); + test::invariant_check(y); + } + EXCEPTION_TEST_END +} + +template +void check_container(X const& x, test::unordered_equivalence_tester const& equivalent) +{ + equivalent.test(x); + test::invariant_check(x); +} + +META_FUNC_TEST_CASE(anon_copy_construct, X) +{ + test::constructors constructor; + test::random_values values(10); + + X x(values.begin(), values.end(), 100, + constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); + x.max_load_factor(4.0); + test::unordered_equivalence_tester equivalent(x); + + EXCEPTION_TEST(1000) + { + check_container(X(x), equivalent); + } + EXCEPTION_TEST_END +} + +template +X return_container(X const& x) +{ + return x; +} + +META_FUNC_TEST_CASE(copy_from_return,X) +{ + test::random_values values(10); + X x(values.begin(), values.end()); + test::unordered_equivalence_tester equivalent(x); + + EXCEPTION_TEST(1000) + { + X y(return_container(x)); + equivalent.test(y); + test::invariant_check(y); + } + EXCEPTION_TEST_END +} + +AUTO_META_TESTS( + (empty_copy_test1)(empty_copy_test2)(range_copy_construct) + (anon_copy_construct)(copy_from_return), + CONTAINER_SEQ +) diff --git a/test/count_tests.cpp b/test/count_tests.cpp new file mode 100644 index 00000000..b900556c --- /dev/null +++ b/test/count_tests.cpp @@ -0,0 +1,65 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include "./helpers/unit_test.hpp" +#include "./helpers/random_values.hpp" + +META_FUNC_TEST_CASE(count_const_test, Container) +{ + test::random_values values(500); + typedef typename test::random_values::iterator iterator; + + Container const x(values.begin(), values.end()); + + for(iterator it = values.begin(); it != values.end(); ++it) + { + BOOST_CHECK_EQUAL(x.count(values.get_key(*it)), values.key_count(*it)); + } + + typedef typename test::random_values::value_type value_type; + test::generator generator; + + for(int i = 0; i < 500; ++i) + { + value_type value = generator(); + BOOST_CHECK_EQUAL(x.count(values.get_key(value)), values.key_count(value)); + } +} + +META_FUNC_TEST_CASE(count_nonconst_test, Container) +{ + test::random_values values(500); + typedef typename test::random_values::iterator iterator; + + Container x(values.begin(), values.end()); + + for(iterator it = values.begin(); it != values.end(); ++it) + { + BOOST_CHECK_EQUAL(x.count(values.get_key(*it)), values.key_count(*it)); + } +} + +META_FUNC_TEST_CASE(empty_test, Container) +{ + typedef test::random_values random_values; + typedef typename random_values::value_type value_type; + test::generator generator; + Container x; + + for(int i = 0; i < 500; ++i) + { + BOOST_CHECK_EQUAL(x.count(random_values::get_key(generator())), 0u); + } +} + +AUTO_META_TESTS( + (count_const_test)(count_nonconst_test)(empty_test), + CONTAINER_SEQ +) diff --git a/test/equal_range_tests.cpp b/test/equal_range_tests.cpp new file mode 100644 index 00000000..0f1fedbf --- /dev/null +++ b/test/equal_range_tests.cpp @@ -0,0 +1,100 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include "./helpers/unit_test.hpp" +#include "./helpers/random_values.hpp" + +template +OutputIt copy_if(InputIt begin, InputIt end, OutputIt out, Condition cond) +{ + for(;begin != end; ++begin) + { + if(cond(*begin)) { + *out = *begin; + ++out; + } + } + + return out; +} + +template +void compare(std::pair const& range, + RandomValues const& values, Value const& v) +{ + typedef typename RandomValues::value_type value_type; + typedef std::vector value_container; + value_container range_values(range.first, range.second); + value_container orig_values; + copy_if(values.begin(), values.end(), std::back_inserter(orig_values), + values.key_match(v)); + + if(values.is_unique()) { + if(orig_values.empty()) { + BOOST_CHECK_EQUAL(range_values.size(), 0u); + } + else { + BOOST_CHECK_EQUAL(range_values.size(), 1u); + BOOST_CHECK(orig_values.front() == *range_values.begin()); + } + } + else { + std::sort(range_values.begin(), range_values.end()); + std::sort(orig_values.begin(), orig_values.end()); + BOOST_CHECK_EQUAL(range_values.size(), orig_values.size()); + if(range_values.size() == orig_values.size()) + BOOST_CHECK(std::equal(range_values.begin(), range_values.end(), + orig_values.begin())); + } +} + +META_FUNC_TEST_CASE(const_test, Container) +{ + test::random_values values(500); + typedef test::random_values random_values; + typedef typename random_values::iterator iterator; + + Container const x(values.begin(), values.end()); + + for(iterator it = values.begin(); it != values.end(); ++it) + { + compare(x.equal_range(values.get_key(*it)), + values, values.get_key(*it)); + } + + typedef typename random_values::value_type value_type; + test::generator generator; + + for(int i = 0; i < 500; ++i) + { + value_type v = generator(); + compare(x.equal_range(values.get_key(v)), + values, values.get_key(v)); + } +} + +META_FUNC_TEST_CASE(nonconst_test, Container) +{ + test::random_values values(500); + typedef typename test::random_values::iterator iterator; + + Container const x(values.begin(), values.end()); + + for(iterator it = values.begin(); it != values.end(); ++it) + { + compare(x.equal_range(values.get_key(*it)), + values, values.get_key(*it)); + } +} + +AUTO_META_TESTS( + (const_test)(nonconst_test), + CONTAINER_SEQ +) diff --git a/test/equivalent.hpp b/test/equivalent.hpp new file mode 100644 index 00000000..ce8808a3 --- /dev/null +++ b/test/equivalent.hpp @@ -0,0 +1,112 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER) +#define BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER + +#include "./helpers/strong.hpp" +#include "./helpers/equivalent.hpp" +#include "./helpers/metafunctions.hpp" +#include "./helpers/less.hpp" +#include +#include +#include +#include + +namespace test +{ + struct equals2_t + { + template + bool operator()(X const& x, Y const& y) + { + return x == y; + } + + template + bool operator()(std::pair const& x, std::pair const& y) + { + return x.first == y.first && x.second == y.second; + } + } equals2; + + template + class unordered_equivalence_tester + { + typename Container::size_type size_; + typename Container::hasher hasher_; + typename Container::key_equal key_equal_; + float max_load_factor_; + std::vector::type> values_; + public: + unordered_equivalence_tester(Container const &x) + : size_(x.size()), + hasher_(x.hash_function()), key_equal_(x.key_eq()), + max_load_factor_(x.max_load_factor()), + values_(x.begin(), x.end()) + { + std::sort(values_.begin(), values_.end(), + test::compare()); + } + + void test(Container const& x) const + { + BOOST_CHECK(size_ == x.size()); + BOOST_CHECK(test::equivalent(hasher_, x.hash_function())); + BOOST_CHECK(test::equivalent(key_equal_, x.key_eq())); + BOOST_CHECK(max_load_factor_ == x.max_load_factor()); + BOOST_CHECK(values_.size() == x.size()); + if(values_.size() == x.size()) { + std::vector::type> + copy(x.begin(), x.end()); + std::sort(copy.begin(), copy.end(), test::compare()); + BOOST_CHECK( + std::equal(values_.begin(), values_.end(), copy.begin(), + equals2)); + } + } + private: + unordered_equivalence_tester(); + }; +} + +#if 0 +namespace boost +{ + template + test::strong_tester_ptr create_tester_impl( + boost::unordered_set const& x, int) + { + return test::strong_tester_ptr(new test::unordered_strong_tester< + boost::unordered_set >(x)); + } + + template + test::strong_tester_ptr create_tester_impl( + boost::unordered_multiset const& x, int) + { + return test::strong_tester_ptr(new test::unordered_strong_tester< + boost::unordered_multiset >(x)); + } + + template + test::strong_tester_ptr create_tester_impl( + boost::unordered_map const& x, int) + { + return test::strong_tester_ptr(new test::unordered_strong_tester< + boost::unordered_map >(x)); + } + + template + test::strong_tester_ptr create_tester_impl( + boost::unordered_multimap const& x, int) + { + return test::strong_tester_ptr(new test::unordered_strong_tester< + boost::unordered_multimap >(x)); + } +} +#endif + +#endif diff --git a/test/erase_tests.cpp b/test/erase_tests.cpp new file mode 100644 index 00000000..306ef5e9 --- /dev/null +++ b/test/erase_tests.cpp @@ -0,0 +1,190 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include +#include "./helpers/unit_test.hpp" +#include "./helpers/exception_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/constructors.hpp" +#include "./invariant.hpp" + +META_FUNC_TEST_CASE(range_erase_test,Container) +{ + test::constructors constructor; + + test::random_values values(100); + typedef typename Container::iterator iterator; + + { + Container x(values.begin(), values.end(), 0, + constructor.hasher(55), + constructor.key_equal(55), + constructor.allocator(10)); + const std::size_t size = x.size(); + INVARIANT_CHECK(x); + iterator pos; + + // Should be no throw. + ACTIVATE_EXCEPTIONS; + + BOOST_CHECKPOINT("Erase nothing from the beginning"); + BOOST_CHECK(x.begin() == x.erase(x.begin(), x.begin())); + BOOST_CHECK_EQUAL(x.size(), size); + test::check_invariants(); + + BOOST_CHECKPOINT("Erase nothing from the end"); + BOOST_CHECK(x.end() == x.erase(x.end(), x.end())); + BOOST_CHECK_EQUAL(x.size(), size); + test::check_invariants(); + + BOOST_CHECKPOINT("Erase nothing from the middle"); + BOOST_CHECK(boost::next(x.begin(), 4) == x.erase( + boost::next(x.begin(), 4), + boost::next(x.begin(), 4))); + BOOST_CHECK_EQUAL(x.size(), size); + test::check_invariants(); + + BOOST_CHECKPOINT("Erase 3 from the middle"); + pos = x.erase(boost::next(x.begin(), 1), boost::next(x.begin(), 4)); + BOOST_CHECK(boost::next(x.begin(), 1) == pos); + BOOST_CHECK_EQUAL(x.size(), size - 3); + test::check_invariants(); + + BOOST_CHECKPOINT("Erase all but the first 1"); + pos = x.erase(boost::next(x.begin(), 1), x.end()); + BOOST_CHECK(x.end() == pos); + BOOST_CHECK_EQUAL(x.size(), 1u); + test::check_invariants(); + } + + { + Container x(values.begin(), values.end()); + const std::size_t size = x.size(); + INVARIANT_CHECK(x); + iterator pos; + + // Should be no throw. + ACTIVATE_EXCEPTIONS; + + BOOST_CHECKPOINT("Erase first 2"); + pos = x.erase(x.begin(), boost::next(x.begin(), 2)); + BOOST_CHECK(x.begin() == pos); + BOOST_CHECK_EQUAL(x.size(), size - 2); + test::check_invariants(); + } + + { + Container x(values.begin(), values.end()); + INVARIANT_CHECK(x); + iterator pos; + + // Should be no throw. + ACTIVATE_EXCEPTIONS; + + BOOST_CHECKPOINT("Erase all"); + pos = x.erase(x.begin(), x.end()); + BOOST_CHECK(x.begin() == pos && x.end() == pos); + BOOST_CHECK(x.empty()); + test::check_invariants(); + } +} + +META_FUNC_TEST_CASE(erase_by_key_test,Container) +{ + test::constructors constructor; + test::sorted_random_values values(10); + + // Exceptions only from the hash function. + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + Container x(values.begin(), values.end(), 0, + constructor.hasher(55), + constructor.key_equal(55), + constructor.allocator(10)); + INVARIANT_CHECK(x); + + for(int i = 0; i < 10; i += values.count(values[i])) { + std::size_t key_count = values.key_count(values[i]); + { + ACTIVATE_EXCEPTIONS; + BOOST_CHECK_EQUAL(key_count, + x.erase(values.get_key(values[i]))); + } + BOOST_CHECK(x.find(values.get_key(values[i])) == x.end()); + BOOST_CHECK_EQUAL(0u, x.erase(values.get_key(values[i]))); + } + + BOOST_CHECK(x.empty()); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(erase_subrange_test,Container) +{ + test::random_values values(100); + Container x(values.begin(), values.end()); + + // Should be no throw. + ACTIVATE_EXCEPTIONS; + + typedef typename Container::const_iterator const_iterator; + typedef typename Container::iterator iterator; + + std::size_t length = x.size(); + std::size_t begin_index = length / 2; + std::size_t end_index = (length + begin_index) / 2; + std::size_t sub_begin_index = (end_index - begin_index) / 4; + std::size_t sub_end_index = sub_begin_index * 3; + + const_iterator begin = boost::next(x.begin(), begin_index); + const_iterator end = boost::next(x.begin(), end_index); + + iterator pos = x.erase(boost::next(begin, sub_begin_index), + boost::next(begin, sub_end_index)); + + BOOST_CHECK(pos == boost::next(x.begin(), begin_index + sub_begin_index)); + BOOST_CHECK(pos == boost::next(begin, sub_begin_index)); + BOOST_CHECK_EQUAL( + (end_index - begin_index) - (sub_end_index - sub_begin_index), + static_cast(std::distance(begin, end))); + + test::invariant_check(x); +} + +META_FUNC_TEST_CASE(erase_by_iterator_test,Container) +{ + test::random_values values(100); + Container x(values.begin(), values.end()); + INVARIANT_CHECK(x); + std::size_t size = x.size(); + + typedef typename Container::iterator iterator; + + // Should be no throw. + ACTIVATE_EXCEPTIONS; + + while(!x.empty()) { + using namespace std; + int index = rand() % x.size(); + iterator pos = x.erase(boost::next(x.begin(), index)); + --size; + BOOST_CHECK_EQUAL(size, x.size()); + + BOOST_CHECK(boost::next(x.begin(), index) == pos); + test::check_invariants(); + } +} + +AUTO_META_TESTS( + (range_erase_test)(erase_by_key_test)(erase_subrange_test) + (erase_by_iterator_test), + CONTAINER_SEQ +) diff --git a/test/find_tests.cpp b/test/find_tests.cpp new file mode 100644 index 00000000..cab61bd9 --- /dev/null +++ b/test/find_tests.cpp @@ -0,0 +1,78 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include "./helpers/unit_test.hpp" +#include "./helpers/random_values.hpp" + +META_FUNC_TEST_CASE(find_const_test,Container) +{ + test::random_values values(500); + typedef typename test::random_values::iterator iterator; + + Container const x(values.begin(), values.end()); + + for(iterator it = values.begin(); it != values.end(); ++it) + { + typename Container::const_iterator pos = x.find(values.get_key(*it)); + BOOST_CHECK(pos != x.end()); + BOOST_CHECK(values.get_key(*pos) == values.get_key(*it)); + } +} + +META_FUNC_TEST_CASE(find_nonconst_test,Container) +{ + test::random_values values(500); + typedef typename test::random_values::iterator iterator; + + Container x(values.begin(), values.end()); + + for(iterator it = values.begin(); it != values.end(); ++it) + { + typename Container::iterator pos = x.find(values.get_key(*it)); + BOOST_CHECK(pos != x.end()); + BOOST_CHECK(values.get_key(*pos) == values.get_key(*it)); + } +} + +META_FUNC_TEST_CASE(missing_test,Container) +{ + test::random_values values(10); + Container x(values.begin(), values.end()); + + typedef typename test::random_values::value_type value_type; + test::generator generator; + + for(int i = 0; i < 500; ++i) + { + value_type const value = generator(); + bool const present_in_values = values.find(value) != values.end(); + bool const present_in_container = x.find(values.get_key(value)) + != x.end(); + BOOST_CHECK(present_in_values == present_in_container); + } +} + +META_FUNC_TEST_CASE(empty_test,Container) +{ + typedef test::random_values random_values; + typedef typename random_values::value_type value_type; + test::generator generator; + Container x; + + for(int i = 0; i < 500; ++i) + { + BOOST_CHECK(x.find(random_values::get_key(generator())) == x.end()); + } +} + +AUTO_META_TESTS( + (find_const_test)(find_nonconst_test)(missing_test)(empty_test), + CONTAINER_SEQ +) diff --git a/test/helpers/accessors.hpp b/test/helpers/accessors.hpp new file mode 100644 index 00000000..f5abfefd --- /dev/null +++ b/test/helpers/accessors.hpp @@ -0,0 +1,12 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_ACCESSORS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_ACCESSORS_HEADER + +namespace test +{ + +} diff --git a/test/helpers/allocator.cpp b/test/helpers/allocator.cpp new file mode 100644 index 00000000..830dd7bb --- /dev/null +++ b/test/helpers/allocator.cpp @@ -0,0 +1,209 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./config.hpp" +#include "./allocator.hpp" +#include "./exception_trigger.hpp" +#include "./exception.hpp" +#include +#include + +#if !defined(BOOST_OLD_IOSTREAMS) +# include +#else +# include +#endif + +namespace test +{ + namespace + { + const unsigned int max_track = 1000; + + struct allocate_details + { + int tag; + std::size_t length; + + allocate_details() + : tag(0), length(0) {} + + allocate_details(int t, int l) + : tag(t), length(l) {} + }; + + std::map allocate_map; + unsigned int reference_count = 0; + unsigned int alloc_count = 0; + + static void ref() + { + ++reference_count; + } + + static void unref() + { + if(--reference_count == 0) { + BOOST_CHECK_MESSAGE(alloc_count == 0 && allocate_map.empty(), + "Memory leak found"); + allocate_map.clear(); + } + } + } + + allocator_base::allocator_base(int x) + : tag(x) + { + ref(); + } + + allocator_base::allocator_base(allocator_base const& x) + : tag(x.tag) + { + ref(); + } + + allocator_base::~allocator_base() + { + unref(); + } + + allocator_base::size_type allocator_base::max_size() const + { + return (std::numeric_limits::max)(); + } + + void* allocator_base::allocate(size_type n, void const*, size_type size) + { + BOOST_CHECK(n <= max_size()); + + exception_trigger((allocator_exception*) 0); + + // TODO: This is not exception safe. + void* ptr = ::operator new(n * size); + ++alloc_count; + if(allocate_map.size() < max_track) + allocate_map[ptr] = allocate_details(tag, n); + + return ptr; + } + + void allocator_base::construct(void* ptr) + { + exception_trigger((allocator_exception*) 0); + } + + void allocator_base::destroy(void* ptr) + { + } + + void allocator_base::deallocate(void* ptr, size_type n) + { + BOOST_CHECK(n <= max_size()); + if(allocate_map.find(ptr) == allocate_map.end()) { + if(alloc_count <= allocate_map.size()) + BOOST_ERROR("Deallocating unknown pointer."); + } else { + // TODO: This is not exception safe. + BOOST_CHECK_EQUAL(allocate_map[ptr].tag, tag); + BOOST_CHECK_EQUAL(allocate_map[ptr].length, n); + allocate_map.erase(ptr); + ::operator delete(ptr); + } + --alloc_count; + } + + void allocator_base::swap(allocator_base& x) + { + std::swap(tag, x. tag); + } + + std::ostream& operator<<(std::ostream& out, allocator_base const& x) + { + out<<"Test Allocator("<::max)() / 4; + } + + void* minimal_allocator_base::allocate(size_type n, void const*, size_type size) + { + BOOST_CHECK(n <= max_size()); + + exception_trigger((allocator_exception*) 0); + + // TODO: This is not exception safe. + void* ptr = ::operator new(n * size); + allocate_map[ptr] = allocate_details(tag, n); + + return ptr; + } + + void minimal_allocator_base::construct(void* ptr) + { + exception_trigger((allocator_exception*) 0); + } + + void minimal_allocator_base::destroy(void* ptr) + { + } + + void minimal_allocator_base::deallocate(void* ptr, size_type n) + { + BOOST_CHECK(n <= max_size()); + if(allocate_map.find(ptr) == allocate_map.end()) { + BOOST_ERROR("Deallocating unknown pointer."); + } else { + // TODO: This is not exception safe. + BOOST_CHECK_EQUAL(allocate_map[ptr].tag, tag); + BOOST_CHECK_EQUAL(allocate_map[ptr].length, n); + allocate_map.erase(ptr); + ::operator delete(ptr); + } + } + + void minimal_allocator_base::swap(minimal_allocator_base& x) + { + std::swap(tag, x. tag); + } + + std::ostream& operator<<(std::ostream& out, minimal_allocator_base const& x) + { + out<<"Minimal Allocator("< +#include +#include + +namespace test +{ + struct allocator_base + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + int tag; + + allocator_base(int x); + allocator_base(allocator_base const&); + ~allocator_base(); + + size_type max_size() const; + + void* allocate(size_type, void const*, size_type); + void construct(void*); + void destroy(void*); + void deallocate(void*, size_type); + void swap(allocator_base&); + private: + allocator_base& operator=(allocator_base const&); + }; + + std::ostream& operator<<(std::ostream&, allocator_base const&); + bool allocator_equals(allocator_base const& x, allocator_base const& y); + + template + struct allocator : allocator_base + { + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template + struct rebind + { + typedef allocator other; + }; + + pointer address(reference x) const + { + return &x; + } + + const_pointer address(const_reference x) const + { + return &x; + } + + allocator(int x = 1) : allocator_base(x) {} + template + allocator(allocator const& x) : allocator_base(x) {} + allocator(allocator const& x) : allocator_base(x) {} + ~allocator() {} + + pointer allocate(size_type n, T const* hint = 0) + { + return static_cast( + allocator_base::allocate(n, hint, sizeof(T))); + } + + void construct(pointer ptr, T const& x) + { + allocator_base::construct(ptr); + new((void*)ptr)T(x); + } + + void destroy(pointer ptr) + { + allocator_base::destroy(ptr); + ptr->~T(); + } + + void deallocate(pointer ptr, size_type n) + { + allocator_base::deallocate(ptr, n); + } + }; + + template + inline bool operator==(allocator const& x, allocator const& y) + { + return test::allocator_equals(x, y); + } + + template + inline bool operator!=(allocator const& x, allocator const& y) + { + return !test::allocator_equals(x, y); + } + + template + void swap(allocator& x, allocator& y) + { + x.swap(y); + } + + template + allocator create_allocator(allocator*) + { + return allocator(); + } + + template + allocator create_allocator(allocator*, int x) + { + return allocator(x); + } + + template struct minimal_allocator; + typedef unsigned short minimal_size_type; + + template + class minimal_pointer_base + { + protected: + typedef minimal_pointer_base pointer_base; + minimal_pointer_base() : ptr_(0) {} + explicit minimal_pointer_base(T* ptr) : ptr_(ptr) {} + ~minimal_pointer_base() {} + Ptr& get() { return *static_cast(this); } + T* ptr_; + public: + typedef void (minimal_pointer_base::*bool_type)() const; + void this_type_does_not_support_comparisons() const {} + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + Ptr& operator++() { ++ptr_; return get(); } + Ptr operator++(int) { Ptr tmp(get()); ++ptr_; return tmp; } + + Ptr operator+(minimal_size_type s) const + { + return Ptr(ptr_ + s); + } + + T& operator[](minimal_size_type s) const + { + return ptr_[s]; + } + + operator bool_type() const + { + return ptr_ ? + &minimal_pointer_base::this_type_does_not_support_comparisons + : 0; + } + + bool operator!() const { return !ptr_; } + bool operator==(Ptr const& x) const { return ptr_ == x.ptr_; } + bool operator!=(Ptr const& x) const { return ptr_ != x.ptr_; } + bool operator<(Ptr const& x) const { return ptr_ < x.ptr_; } + bool operator>(Ptr const& x) const { return ptr_ > x.ptr_; } + bool operator<=(Ptr const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(Ptr const& x) const { return ptr_ >= x.ptr_; } + + friend std::ostream& operator<<(std::ostream& out, minimal_pointer_base const& x) + { + out< class minimal_pointer; + template class minimal_const_pointer; + + template + class minimal_pointer + : public minimal_pointer_base, T> + { + friend struct minimal_allocator; + friend class minimal_pointer_base, T>; + friend class minimal_const_pointer; + typedef typename minimal_pointer::pointer_base base; + minimal_pointer(T* ptr) : base(ptr) {} + + typedef minimal_const_pointer const_pointer; + typedef minimal_pointer pointer; + public: + minimal_pointer() : base() {} + + bool operator==(pointer const& x) const { return base::operator==(x); } + bool operator!=(pointer const& x) const { return base::operator!=(x); } + bool operator<(pointer const& x) const { return base::operator<(x);} + bool operator>(pointer const& x) const { return base::operator>(x);} + bool operator<=(pointer const& x) const { return base::operator<=(x);} + bool operator>=(pointer const& x) const { return base::operator<=(x);} + + bool operator==(const_pointer const& x) const { return x == *this; } + bool operator!=(const_pointer const& x) const { return x != *this; } + bool operator<(const_pointer const& x) const { return x > *this; } + bool operator>(const_pointer const& x) const { return x < *this; } + bool operator<=(const_pointer const& x) const { return x >= *this; } + bool operator>=(const_pointer const& x) const { return x <= *this; } + }; + + template + class minimal_const_pointer + : public minimal_pointer_base, T const> + { + friend struct minimal_allocator; + friend class minimal_pointer_base, T const>; + typedef typename minimal_const_pointer::pointer_base base; + minimal_const_pointer(T* ptr) : base(ptr) {} + + typedef minimal_const_pointer const_pointer; + typedef minimal_pointer pointer; + public: + minimal_const_pointer() : base() {} + minimal_const_pointer(minimal_pointer const& x) : base(x.ptr_) {} + + bool operator==(const_pointer const& x) const { return base::operator==(x); } + bool operator!=(const_pointer const& x) const { return base::operator!=(x); } + bool operator<(const_pointer const& x) const { return base::operator<(x);} + bool operator>(const_pointer const& x) const { return base::operator>(x);} + bool operator<=(const_pointer const& x) const { return base::operator<=(x);} + bool operator>=(const_pointer const& x) const { return base::operator<=(x);} + + bool operator==(pointer const& x) const { return operator==(const_pointer(x)); } + bool operator!=(pointer const& x) const { return operator!=(const_pointer(x)); } + bool operator<(pointer const& x) const { return operator<(const_pointer(x));} + bool operator>(pointer const& x) const { return operator>(const_pointer(x));} + bool operator<=(pointer const& x) const { return operator<=(const_pointer(x));} + bool operator>=(pointer const& x) const { return operator<=(const_pointer(x));} + }; + + struct minimal_allocator_base + { + typedef minimal_size_type size_type; + typedef std::ptrdiff_t difference_type; + + int tag; + + minimal_allocator_base(int x); + minimal_allocator_base(minimal_allocator_base const&); + ~minimal_allocator_base(); + + size_type max_size() const; + + void* allocate(size_type, void const*, size_type); + void construct(void*); + void destroy(void*); + void deallocate(void*, size_type); + void swap(minimal_allocator_base&); + private: + minimal_allocator_base& operator=(minimal_allocator_base const&); + }; + + std::ostream& operator<<(std::ostream&, minimal_allocator_base const&); + bool allocator_equals(minimal_allocator_base const&, minimal_allocator_base const&); + + template + struct minimal_allocator : minimal_allocator_base + { + typedef minimal_pointer pointer; + typedef minimal_const_pointer const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template + struct rebind + { + typedef minimal_allocator other; + }; + + pointer address(reference r) + { + return pointer(&r); + } + + const_pointer address(const_reference r) + { + return const_pointer(&r); + } + + pointer allocate(size_type n) + { + return pointer(static_cast( + minimal_allocator_base::allocate(n, 0, sizeof(T)))); + } + + pointer allocate(size_type n, const_pointer u) + { + return pointer(static_cast( + minimal_allocator_base::allocate(n, u.ptr_, sizeof(T)))); + } + + void deallocate(pointer p, size_type n) + { + minimal_allocator_base::deallocate(p.ptr_, n); + } + + minimal_allocator() + : minimal_allocator_base(0) + { + } + + explicit minimal_allocator(int tag) + : minimal_allocator_base(tag) + { + } + + template + minimal_allocator(minimal_allocator const& x) + : minimal_allocator_base(x) + { + } + + minimal_allocator(minimal_allocator const& x) + : minimal_allocator_base(x) + { + } + + void construct(pointer p, T const& t) + { + minimal_allocator_base::construct(p.ptr_); + new((void*)p.ptr_) T(t); + } + + void destroy(pointer p) + { + minimal_allocator_base::destroy(p.ptr_); + ((T*)p.ptr_)->~T(); + } + private: + minimal_allocator& operator=(minimal_allocator const&); + }; + + template + inline bool operator==(minimal_allocator const& x, minimal_allocator const& y) + { + return test::allocator_equals(x, y); + } + + template + inline bool operator!=(minimal_allocator const& x, minimal_allocator const& y) + { + return !test::allocator_equals(x, y); + } + + template + void swap(minimal_allocator& x, minimal_allocator& y) + { + x.swap(y); + } + + template + minimal_allocator create_allocator(minimal_allocator*) + { + return minimal_allocator(); + } + + template + minimal_allocator create_allocator(minimal_allocator*, int x) + { + return minimal_allocator(x); + } +} + +#endif diff --git a/test/helpers/base.cpp b/test/helpers/base.cpp new file mode 100644 index 00000000..d351b08e --- /dev/null +++ b/test/helpers/base.cpp @@ -0,0 +1,31 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./base.hpp" +#include +#include + +namespace test +{ + namespace + { + std::vector end_checks; + } + + void register_end_check(void(*check)()) + { + end_checks.push_back(check); + } + + void call_check(void(*check)()) + { + check(); + } + + void end() + { + std::for_each(end_checks.begin(), end_checks.end(), call_check); + } +} diff --git a/test/helpers/base.hpp b/test/helpers/base.hpp new file mode 100644 index 00000000..5cc58b1a --- /dev/null +++ b/test/helpers/base.hpp @@ -0,0 +1,15 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_BASE_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_BASE_HEADER + +namespace test +{ + void register_end_check(void(*)()); + void end(); +} + +#endif diff --git a/test/helpers/config.hpp b/test/helpers/config.hpp new file mode 100644 index 00000000..5ac1a11e --- /dev/null +++ b/test/helpers/config.hpp @@ -0,0 +1,18 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_CONFIG_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_CONFIG_HEADER + +#include + +// From Boost.Dynamic Bitset: + +// support for pre 3.0 libstdc++ - thanks Phil Edwards! +#if defined (__STL_CONFIG_H) && !defined (__STL_USE_NEW_IOSTREAMS) +# define BOOST_OLD_IOSTREAMS +#endif + +#endif diff --git a/test/helpers/constructors.hpp b/test/helpers/constructors.hpp new file mode 100644 index 00000000..51925509 --- /dev/null +++ b/test/helpers/constructors.hpp @@ -0,0 +1,69 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_CONSTRUCTORS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_CONSTRUCTORS_HEADER + +namespace test +{ + template + Allocator create_allocator(Allocator*, int = 0) + { + return Allocator(); + } + + template + Hasher create_hasher(Hasher*, int = 0) + { + return Hasher(); + } + + template + KeyEqual create_key_equal(KeyEqual*, int = 0) + { + return KeyEqual(); + } + + template + class constructors + { + public: + typedef typename Container::allocator_type allocator_type; + typedef typename Container::hasher hasher_type; + typedef typename Container::key_equal key_equal_type; + + allocator_type allocator() const + { + return create_allocator((allocator_type*) 0); + } + + allocator_type allocator(int x) const + { + return create_allocator((allocator_type*) 0, x); + } + + hasher_type hasher() const + { + return create_hasher((hasher_type*) 0); + } + + hasher_type hasher(int x) const + { + return create_hasher((hasher_type*) 0, x); + } + + key_equal_type key_equal() const + { + return create_key_equal((key_equal_type*) 0); + } + + key_equal_type key_equal(int x) const + { + return create_key_equal((key_equal_type*) 0, x); + } + }; +} + +#endif diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp new file mode 100644 index 00000000..3416a838 --- /dev/null +++ b/test/helpers/equivalent.hpp @@ -0,0 +1,45 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_EQUIVALENT_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_EQUIVALENT_HEADER + +#include +#include + +namespace test +{ + template + bool equivalent_impl(std::equal_to, std::equal_to, int) + { + return true; + } + + template + bool equivalent_impl(boost::hash, boost::hash, int) + { + return true; + } + + template + bool equivalent_impl(T const& x, T const& y, float) + { + return x == y; + } + + template + bool equivalent(T const& x, T const& y) + { + return equivalent_impl(x, y, 0); + } + + template + bool equivalent(std::pair const& x, std::pair const& y) + { + return equivalent(x.first, y.first) && equivalent(x.second, y.second); + } +} + +#endif diff --git a/test/helpers/exception.cpp b/test/helpers/exception.cpp new file mode 100644 index 00000000..b50638cd --- /dev/null +++ b/test/helpers/exception.cpp @@ -0,0 +1,85 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./exception.hpp" + +namespace test +{ + exception::exception() + : message_("Triggered test exception") + { + } + + exception::exception(char const* message) + : message_(message) + { + } + + char const* exception::what() const throw() + { + return message_; + } + + hash_exception::hash_exception() + : exception("Triggered hash exception") + { + } + + hash_exception::hash_exception(char const* message) + : exception(message) + { + } + + hash_copy_exception::hash_copy_exception() + : hash_exception("Triggered hash copy exception") + { + } + + hash_copy_exception::hash_copy_exception(char const* message) + : hash_exception(message) + { + } + + pred_exception::pred_exception() + : exception("Triggered pred exception") + { + } + + pred_exception::pred_exception(char const* message) + : exception(message) + { + } + + pred_copy_exception::pred_copy_exception() + : pred_exception("Triggered pred copy exception") + { + } + + pred_copy_exception::pred_copy_exception(char const* message) + : pred_exception(message) + { + } + + allocator_exception::allocator_exception() + : exception("Triggered pred exception") + { + } + + allocator_exception::allocator_exception(char const* message) + : exception(message) + { + } + + allocator_copy_exception::allocator_copy_exception() + : allocator_exception("Triggered pred copy exception") + { + } + + allocator_copy_exception::allocator_copy_exception(char const* message) + : allocator_exception(message) + { + } +} + diff --git a/test/helpers/exception.hpp b/test/helpers/exception.hpp new file mode 100644 index 00000000..692ceaf2 --- /dev/null +++ b/test/helpers/exception.hpp @@ -0,0 +1,60 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_HEADER + +#include + +namespace test +{ + // Exception Handling + + struct exception : std::exception + { + char const* message_; + exception(); + exception(char const*); + char const* what() const throw(); + }; + + struct hash_exception : exception + { + hash_exception(); + hash_exception(char const*); + }; + + struct hash_copy_exception : hash_exception + { + hash_copy_exception(); + hash_copy_exception(char const*); + }; + + struct pred_exception : exception + { + pred_exception(); + pred_exception(char const*); + }; + + struct pred_copy_exception : pred_exception + { + pred_copy_exception(); + pred_copy_exception(char const*); + }; + + struct allocator_exception : exception + { + allocator_exception(); + allocator_exception(char const*); + }; + + struct allocator_copy_exception : allocator_exception + { + allocator_copy_exception(); + allocator_copy_exception(char const*); + }; +} + +#endif diff --git a/test/helpers/exception_test.cpp b/test/helpers/exception_test.cpp new file mode 100644 index 00000000..a8ec3e12 --- /dev/null +++ b/test/helpers/exception_test.cpp @@ -0,0 +1,108 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./exception_test.hpp" +#include +#include +#include + +namespace test +{ + // TODO: (Writing this here instead of the headers to avoid recompiling + // the world) + // + // There are some major design flaws with the exception testing code, + // apart from global variables that is. + + namespace + { + int num_iterations = 0; + int current_iteration = 0; + int trigger_count = 0; + int max_trigger_count = 0; + bool failed = false; + bool exception_testing = false; + bool exceptions_active = false; + } + + void exception_start(int n) + { + num_iterations = n; + current_iteration = 0; + max_trigger_count = 0; + trigger_count = 0; + failed = false; + exception_testing = true; + exceptions_active = true; + } + + void exception_loop() + { + BOOST_CHECK(exceptions_active); + + ++current_iteration; + max_trigger_count = trigger_count; + exception_testing = failed; + exceptions_active = failed; + trigger_count = 0; + failed = false; + } + + bool exception_loop_test() + { + if(exception_testing && current_iteration == num_iterations) { + BOOST_ERROR("Too many iterations"); + return false; + } + else { + return exception_testing; + } + } + + void exception_failure() + { + failed = true; + } + + bool true_once() + { + ++trigger_count; + return !exception_testing || trigger_count > max_trigger_count; + } + + bool exception_trigger_test() + { + ++trigger_count; + return exception_testing && exceptions_active + && trigger_count > max_trigger_count; + } + + void exception_trigger() + { + if(exception_trigger_test()) throw exception(); + } + + void exception_trigger(char const* message) + { + if(exception_trigger_test()) throw exception(message); + } + + bool exceptions_activate(bool value) + { + bool old = exceptions_active; + exceptions_active = value; + return old; + } + + exception_control::exception_control(bool value) + : old_value(exceptions_activate(value)) + { + } + + exception_control::~exception_control() + { + exceptions_activate(old_value); + } +} diff --git a/test/helpers/exception_test.hpp b/test/helpers/exception_test.hpp new file mode 100644 index 00000000..80fb3db1 --- /dev/null +++ b/test/helpers/exception_test.hpp @@ -0,0 +1,33 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_TEST_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_TEST_HEADER + +#include +#include +#include "./exception.hpp" +#include "./exception_trigger.hpp" +#include "./base.hpp" + +namespace test +{ + void exception_start(int); + bool exception_loop_test(); + void exception_loop(); + void exception_failure(); + + bool true_once(); +} + +#define EXCEPTION_TEST(count) \ + for(::test::exception_start(count); ::test::exception_loop_test(); \ + ::test::exception_loop()) \ + try + +#define EXCEPTION_TEST_END \ + catch(::test::exception const&) { ::test::exception_failure(); } + +#endif diff --git a/test/helpers/exception_trigger.hpp b/test/helpers/exception_trigger.hpp new file mode 100644 index 00000000..358ea7f6 --- /dev/null +++ b/test/helpers/exception_trigger.hpp @@ -0,0 +1,49 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_TRIGGER_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_TRIGGER_HEADER + +#include + +namespace test +{ + // Exception Handling + + bool exception_trigger_test(); + void exception_trigger(); + void exception_trigger(char const*); + + template + void exception_trigger(T*) + { + if(exception_trigger_test()) throw T(); + } + + template + void exception_trigger(T*, char const* msg) + { + if(exception_trigger_test()) throw T(msg); + } + + struct exception_control + { + bool old_value; + + exception_control(bool); + ~exception_control(); + }; + +} + +#define ACTIVATE_EXCEPTIONS \ + ::test::exception_control BOOST_PP_CAT(ACTIVATE_EXCEPTIONS_, __LINE__) \ + (true) +#define DEACTIVATE_EXCEPTIONS \ + ::test::exception_control BOOST_PP_CAT(ACTIVATE_EXCEPTIONS_, __LINE__) \ + (false) + +#endif + diff --git a/test/helpers/functional.cpp b/test/helpers/functional.cpp new file mode 100644 index 00000000..03458b41 --- /dev/null +++ b/test/helpers/functional.cpp @@ -0,0 +1,140 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./config.hpp" +#include "./functional.hpp" +#include "./exception.hpp" +#include "./exception_trigger.hpp" +#include +#include + +#if !defined(BOOST_OLD_IOSTREAMS) +# include +#else +# include +#endif + +namespace test +{ + // Hash + + hash::hash(int x) + : offset(x) {} + + hash::hash(hash const& x) + : offset(x.offset) + { + exception_trigger((hash_copy_exception*) 0); + } + + hash& hash::operator=(hash const& x) + { + exception_trigger((hash_copy_exception*) 0); + offset = x.offset; + exception_trigger((hash_copy_exception*) 0); + + return *this; + } + + std::size_t hash::calculate_hash(std::size_t x) const + { + exception_trigger((hash_exception*) 0); + return x + offset; + } + + std::size_t hash::operator()(char const* x) const + { + return calculate_hash(boost::hash()(x)); + } + + bool hash::operator==(hash const& x) const + { + return offset == x.offset; + } + + std::ostream& operator<<(std::ostream& out, hash x) + { + out<<"Test Hash("< +#include + +namespace test +{ + struct hash + { + int offset; + + explicit hash(int x = 1); + hash(hash const&); + hash& operator=(hash const&); + + std::size_t calculate_hash(std::size_t) const; + + template + std::size_t operator()(T const& x) const + { + return calculate_hash(boost::hash()(x)); + } + + std::size_t operator()(char const* x) const; + bool operator==(hash const& x) const; + }; + + std::ostream& operator<<(std::ostream& out, hash x); + hash create_hasher(hash*); + hash create_hasher(hash*, int x); + + struct equals + { + int tag; + + explicit equals(int x = 1); + equals(equals const&); + equals& operator=(equals const&); + + bool calculate_equals(bool) const; + + template + bool operator()(T1 const& x, T2 const& y) const + { + return calculate_equals(x == y); + } + + bool operator()(char const*, char const*) const; + bool operator==(equals const& x) const; + }; + + std::ostream& operator<<(std::ostream& out, equals x); + equals create_key_equal(equals*); + equals create_key_equal(equals*, int x); + + struct less + { + int tag; + + explicit less(int x = 0); + + template + bool operator()(T1 const& x, T2 const& y) const + { + return x < y; + } + + bool operator()(char const*, char const*) const; + }; + + std::ostream& operator<<(std::ostream& out, less x); +} + +#endif diff --git a/test/helpers/generators.cpp b/test/helpers/generators.cpp new file mode 100644 index 00000000..247a795c --- /dev/null +++ b/test/helpers/generators.cpp @@ -0,0 +1,43 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "generators.hpp" + +// Generators + +namespace test +{ + int generate(int const*) + { + using namespace std; + return rand(); + } + + char generate(char const*) + { + using namespace std; + return (char)(rand() % 26) + 'a'; + } + + std::string generate(std::string const*) + { + using namespace std; + + static test::generator char_gen; + + std::string result; + + int length = rand() % 10; + for(int i = 0; i < length; ++i) + result += char_gen(); + + //std::generate_n( + // std::back_inserter(result), + // rand() % 10, + // char_gen); + + return result; + } +} diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp new file mode 100644 index 00000000..3cbb9e37 --- /dev/null +++ b/test/helpers/generators.hpp @@ -0,0 +1,41 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER + +#include +#include +#include + +namespace test +{ + int generate(int const*); + char generate(char const*); + std::string generate(std::string const*); + template std::pair generate( + std::pair const*); + + template + struct generator + { + typedef T value_type; + value_type operator()() + { + return generate((T const*) 0); + } + }; + + template + std::pair generate(std::pair const*) + { + static generator g1; + static generator g2; + + return std::pair(g1(), g2()); + } +} + +#endif diff --git a/test/helpers/input_iterator_adaptor.hpp b/test/helpers/input_iterator_adaptor.hpp new file mode 100644 index 00000000..715e123a --- /dev/null +++ b/test/helpers/input_iterator_adaptor.hpp @@ -0,0 +1,35 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER + +#include + +namespace test +{ + template + struct input_iterator_adaptor + : boost::iterator_adaptor< + input_iterator_adaptor, Iterator, + boost::use_default, std::input_iterator_tag> + { + typedef boost::iterator_adaptor< + input_iterator_adaptor, Iterator, + boost::use_default, std::input_iterator_tag> base; + + explicit input_iterator_adaptor(Iterator it = Iterator()) + : base(it) {} + }; + + template + input_iterator_adaptor make_input_iterator(Iterator it) + { + return input_iterator_adaptor(it); + } +} + +#endif + diff --git a/test/helpers/invariant_checker.cpp b/test/helpers/invariant_checker.cpp new file mode 100644 index 00000000..2f74750a --- /dev/null +++ b/test/helpers/invariant_checker.cpp @@ -0,0 +1,54 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include +#include "./invariant_checker.hpp" +#include +#include +#include + +namespace test +{ + namespace + { + typedef std::set check_set; + check_set checks; + } + + invariant_checker_base::invariant_checker_base() + { + } + + invariant_checker_base::~invariant_checker_base() + { + } + + void check_invariants() + { + // This was causing compile errors on Visual C++, because check + // has return type void. + //std::for_each(checks.begin(), checks.end(), + // boost::mem_fun(&invariant_checker_base::check)); + + check_set::iterator end = checks.end(); + for(check_set::iterator it = checks.begin(); it != end; ++it) + (*it)->check(); + } + + void invariant_add(invariant_checker_base* check) + { + checks.insert(check); + } + + void invariant_remove(invariant_checker_base* check) + { + checks.erase(check); + } + + void initial_check(invariant_checker_base const& x) + { + x.check(); + } +} diff --git a/test/helpers/invariant_checker.hpp b/test/helpers/invariant_checker.hpp new file mode 100644 index 00000000..a678e3b3 --- /dev/null +++ b/test/helpers/invariant_checker.hpp @@ -0,0 +1,78 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER + +#include +#include "./exception_trigger.hpp" + +namespace test +{ + struct invariant_checker_base + { + invariant_checker_base(); + virtual void check() const = 0; + protected: + virtual ~invariant_checker_base(); + }; + + void check_invariants(); + void invariant_add(invariant_checker_base*); + void invariant_remove(invariant_checker_base*); + + template + struct invariant_checker : invariant_checker_base + { + T& object_; + + invariant_checker(T& o) : object_(o) + { + invariant_add(this); + } + + ~invariant_checker() + { + check(); + invariant_remove(this); + } + + void check() const + { + DEACTIVATE_EXCEPTIONS; + invariant_impl(object_); + } + }; + + // On compilers without RVO check() will be called multiple times. + // No big deal I suppose. + template + invariant_checker make_invariant_checker(T& o) + { + return invariant_checker(o); + } + + // Calling this also prevents an unused variable warning when using + // an invariant checker. + void initial_check(::test::invariant_checker_base const&); + + // A one time invariant check. + template + void invariant_check(T const& x) + { + DEACTIVATE_EXCEPTIONS; + invariant_impl(x); + } +} + +#define INVARIANT_CHECK(o) \ + INVARIANT_CHECK2(o, BOOST_PP_CAT(invariant_checker_, __LINE__)) + +#define INVARIANT_CHECK2(o, name) \ + ::test::invariant_checker_base const& name = \ + ::test::make_invariant_checker(o); \ + ::test::initial_check(name) + +#endif diff --git a/test/helpers/less.hpp b/test/helpers/less.hpp new file mode 100644 index 00000000..9151685f --- /dev/null +++ b/test/helpers/less.hpp @@ -0,0 +1,37 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_LESS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_LESS_HEADER + +#include + +namespace test +{ + template + bool compare_impl(T const& x, T const& y, float) + { + return x < y; + } + + template + bool compare_impl(std::pair const& x, + std::pair const& y, int) + { + return x.first < y.first || + (x.first == y.first && x.second < y.second); + } + + struct compare { + template + bool operator()(T const& x, T const& y) + { + return compare_impl(x, y, 0); + } + }; +} + +#endif + diff --git a/test/helpers/member.cpp b/test/helpers/member.cpp new file mode 100644 index 00000000..7edfc27e --- /dev/null +++ b/test/helpers/member.cpp @@ -0,0 +1,79 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./config.hpp" +#include "./member.hpp" +#include "./exception_trigger.hpp" +#include "./generators.hpp" + +#if !defined(BOOST_OLD_IOSTREAMS) +# include +#else +# include +#endif + +namespace test +{ + member::member(int x) + : value(x) + { + exception_trigger(); + } + + member::member(member const& x) + : value(x.value) + { + exception_trigger(); + } + + member& member::operator=(member const& x) + { + exception_trigger(); + value = x.value; + exception_trigger(); + + return *this; + } + + member::~member() + { + value = -1; + } + + bool member::operator==(member const& x) const + { + exception_trigger(); + return value == x.value; + } + + bool member::operator<(member const& x) const + { + exception_trigger(); + return value < x.value; + } + + std::ostream& operator<<(std::ostream& out, member const& x) + { + out<<"Test class("<()()); + } +} + +#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +namespace test +#else +namespace boost +#endif +{ + std::size_t hash_value(test::member const& x) + { + return x.value; + } +} diff --git a/test/helpers/member.hpp b/test/helpers/member.hpp new file mode 100644 index 00000000..2ea51c11 --- /dev/null +++ b/test/helpers/member.hpp @@ -0,0 +1,40 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_MEMBER_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_MEMBER_HEADER + +#include +#include + +namespace test +{ + struct member + { + int value; + + explicit member(int x = 0); + member(member const&); + member& operator=(member const&); + ~member(); + + bool operator==(member const&) const; + bool operator<(member const&) const; + }; + + std::ostream& operator<<(std::ostream&, member const&); + test::member generate(test::member const*); +} + +#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +namespace test +#else +namespace boost +#endif +{ + std::size_t hash_value(test::member const& x); +} + +#endif diff --git a/test/helpers/metafunctions.hpp b/test/helpers/metafunctions.hpp new file mode 100644 index 00000000..26702abb --- /dev/null +++ b/test/helpers/metafunctions.hpp @@ -0,0 +1,100 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER + +#include +#include +#include +#include +#include + +namespace test +{ + struct unordered_set_type { char x[100]; }; + struct unordered_multiset_type { char x[200]; }; + struct unordered_map_type { char x[300]; }; + struct unordered_multimap_type { char x[400]; }; + + template + unordered_set_type container_type( + boost::unordered_set const*); + template + unordered_multiset_type container_type( + boost::unordered_multiset const*); + template + unordered_map_type container_type( + boost::unordered_map const*); + template + unordered_multimap_type container_type( + boost::unordered_multimap const*); + + template + struct is_set + { + BOOST_STATIC_CONSTANT(bool, value = + sizeof(container_type((Container const*)0)) + == sizeof(unordered_set_type) || + sizeof(container_type((Container const*)0)) + == sizeof(unordered_multiset_type) + ); + }; + + template + struct is_map + { + BOOST_STATIC_CONSTANT(bool, value = + sizeof(container_type((Container const*)0)) + == sizeof(unordered_map_type) || + sizeof(container_type((Container const*)0)) + == sizeof(unordered_multimap_type) + ); + }; + + template + struct has_unique_keys + { + BOOST_STATIC_CONSTANT(bool, value = + sizeof(container_type((Container const*)0)) + == sizeof(unordered_set_type) || + sizeof(container_type((Container const*)0)) + == sizeof(unordered_map_type) + ); + }; + + template + struct has_equivalent_keys + { + BOOST_STATIC_CONSTANT(bool, value = + sizeof(container_type((Container const*)0)) + == sizeof(unordered_multiset_type) || + sizeof(container_type((Container const*)0)) + == sizeof(unordered_multimap_type) + ); + }; + + // Non Const Value Type + + template + struct map_non_const_value_type + { + typedef std::pair< + typename Container::key_type, + typename Container::mapped_type> type; + }; + + + template + struct non_const_value_type + : boost::mpl::eval_if, + map_non_const_value_type, + boost::mpl::identity > + { + }; +} + +#endif + diff --git a/test/helpers/random_values.hpp b/test/helpers/random_values.hpp new file mode 100644 index 00000000..4ba605c3 --- /dev/null +++ b/test/helpers/random_values.hpp @@ -0,0 +1,203 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER + +#include "./generators.hpp" +#include "./metafunctions.hpp" +#include +#include +#include + +namespace test +{ + template + struct accessors + { + // get_key + // + // Given either the value_type or the key_type returns the key. + static typename Container::key_type const& + get_key(typename Container::key_type const& x) + { + return x; + } + + template + static typename Container::key_type const& + get_key(std::pair const& x) + { + return x.first; + } + + static typename Container::value_type const& + get_mapped(typename Container::key_type const& x) + { + return x; + } + + template + static M const& + get_mapped(std::pair const& x) + { + return x.second; + } + }; + + template + struct random_values : public accessors + { + typedef accessors base; + + typedef typename non_const_value_type::type value_type; + typedef typename Container::key_type key_type; + + std::vector values_; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + + explicit random_values(std::size_t count) + { + values_.reserve(count); + std::generate_n(std::back_inserter(values_), + count, test::generator()); + } + + iterator begin() { return values_.begin(); } + iterator end() { return values_.end(); } + const_iterator begin() const { return values_.begin(); } + const_iterator end() const { return values_.end(); } + + value_type const& operator[](std::size_t i) const { return values_[i]; } + + struct key_matcher0 + { + template + bool operator()(X const& x, Y const& y) const + { + return base::get_key(x) == base::get_key(y); + } + }; + + // No, I don't know why didn't I just use bind. + struct key_matcher1 + { + key_type x; + + key_matcher1(key_type const& x) : x(x) {} + + bool operator()(key_type const& y) + { + return x == y; + } + + template + bool operator()(std::pair const& y) + { + return x == y.first; + } + }; + + static key_matcher0 key_match() + { + return key_matcher0(); + } + + static key_matcher1 key_match(key_type const& x) + { + return key_matcher1(x); + } + + template + static key_matcher1 key_match(std::pair const& x) + { + return key_matcher1(x.first); + } + + template + iterator find(K const& x) + { + return std::find_if(values_.begin(), values_.end(), key_match(x)); + } + + template + std::size_t count(K const& x) + { + return std::count_if(values_.begin(), values_.end(), + key_match(x)); + } + + template + std::size_t key_count(K const& x) + { + if(has_unique_keys::value) + return find(x) != values_.end(); + else + return count(x); + } + + static bool is_unique() + { + return has_unique_keys::value; + } + }; + + template + struct sorted_random_values : public random_values + { + typedef random_values base; + typedef typename base::value_type value_type; + typedef typename base::key_type key_type; + typedef typename base::iterator iterator; + typedef typename base::const_iterator const_iterator; + + explicit sorted_random_values(std::size_t count) + : base(count) + { + std::stable_sort(this->begin(), this->end()); + } + + struct key_compare0 + { + template + bool operator()(X const& x, Y const& y) const + { + return base::get_key(x) < base::get_key(y); + } + }; + + static key_compare0 key_compare() + { + return key_compare0(); + } + + template + iterator find(K const& x) + { + iterator pos = std::lower_bound(this->begin(), this->end(), x, key_compare()); + return this->key_match()(x, *pos) ? pos : this->end(); + } + + template + std::size_t count(K const& x) + { + std::pair range = + std::equal_range(this->begin(), this->end(), x, key_compare()); + return range.second - range.first; + } + + template + std::size_t key_count(K const& x) + { + if(has_unique_keys::value) + return find(x) != this->end(); + else + return count(x); + } + }; +} + +#endif diff --git a/test/helpers/strong.cpp b/test/helpers/strong.cpp new file mode 100644 index 00000000..0290f42f --- /dev/null +++ b/test/helpers/strong.cpp @@ -0,0 +1,32 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./strong.hpp" +#include + +namespace test +{ + strong_tester::strong_tester() : dismissed_(false) {} + strong_tester::~strong_tester() { BOOST_CHECK(dismissed_); } + void strong_tester::dismiss() const { dismissed_ = true; } + bool strong_tester::is_dismissed() const { return dismissed_; } + void strong_tester::call_test() { + if(!is_dismissed()) + { + DEACTIVATE_EXCEPTIONS; + try { + test(); + } catch(...) { + BOOST_ERROR("Exception thrown in strong test."); + } + dismissed_ = true; + } + } + + strong_test_holder::strong_test_holder(strong_tester_ptr const& x) : ptr_(x) {} + strong_test_holder::~strong_test_holder() { ptr_->call_test(); } + bool strong_test_holder::is_dismissed() const { return ptr_->is_dismissed(); } + void strong_test_holder::dismiss() { ptr_->dismiss(); } +} diff --git a/test/helpers/strong.hpp b/test/helpers/strong.hpp new file mode 100644 index 00000000..c90c80e8 --- /dev/null +++ b/test/helpers/strong.hpp @@ -0,0 +1,78 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER + +#include +#include "./exception_trigger.hpp" + +namespace test +{ + class strong_tester + { + mutable bool dismissed_; + protected: + strong_tester(); + public: + virtual ~strong_tester(); + void dismiss() const; + bool is_dismissed() const; + void call_test(); + virtual void test() = 0; + }; + + template + class default_strong_tester + : public strong_tester + { + T const& reference; + T copy; + public: + default_strong_tester(T const& x) : reference(x), copy(x) {} + + void test() + { + BOOST_CHECK(reference == copy); + } + }; + + typedef boost::shared_ptr strong_tester_ptr; + + //template + //strong_tester_ptr create_tester_impl(T const& x, float) + //{ + // return strong_tester_ptr(new default_strong_tester(x)); + //} + + template + strong_tester_ptr create_tester(T const& x) + { + DEACTIVATE_EXCEPTIONS; + return create_tester_impl(x, 0); + } + + class strong_test_holder + { + strong_tester_ptr ptr_; + public: + strong_test_holder(strong_tester_ptr const&); + ~strong_test_holder(); + bool is_dismissed() const; + void dismiss(); + private: + strong_test_holder(strong_test_holder const&); + strong_test_holder& operator=(strong_test_holder const&); + }; +} + +#define STRONG_TEST_ANON(x) \ + STRONG_TEST(BOOST_PP_CAT(STRONG_TEST_tester, __LINE__), x) + +#define STRONG_TEST(tester, x) \ + for(::test::strong_test_holder tester(::test::create_tester(x)); \ + !tester.is_dismissed(); tester.dismiss()) + +#endif diff --git a/test/helpers/unit_test.hpp b/test/helpers/unit_test.hpp new file mode 100644 index 00000000..3fe6efee --- /dev/null +++ b/test/helpers/unit_test.hpp @@ -0,0 +1,77 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_AUTO_UNIT_TEST_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_AUTO_UNIT_TEST_HEADER + +#include "./base.hpp" +#include +#include +#include +#include +#include + + +#define AUTO_UNIT_TEST(name) \ + AUTO_UNIT_TEST2(name, BOOST_PP_CAT(name##_, impl)) + +#define AUTO_UNIT_TEST2(name, impl_name) \ + void impl_name(); \ + BOOST_AUTO_UNIT_TEST(name) \ + { \ + impl_name(); \ + ::test::end(); \ + } \ + void impl_name() + +#define AUTO_TEMPLATE_TEST(name, T, type_seq) \ + AUTO_TEMPLATE_TEST2(name, BOOST_PP_CAT(name##_, impl), T, type_seq) + +#define AUTO_TEMPLATE_TEST2(name, impl_name, T, type_seq) \ + template \ + void impl_name(); \ + BOOST_PP_SEQ_FOR_EACH(AUTO_TEMPLATE_TEST_OP, name, type_seq) \ + template \ + void impl_name() + +#define AUTO_TEMPLATE_TEST_OP(r, name, type) \ + static boost::unit_test::ut_detail::auto_unit_test_registrar \ + BOOST_PP_CAT(test_registrar_##name##_, type) \ + ( BOOST_TEST_CASE( BOOST_PP_CAT(name##_, impl) ) ); + +#define META_FUNC_TEST_CASE(name, T) \ + META_FUNC_TEST_CASE2(name, T, BOOST_PP_CAT(name##_, impl)) + +#define META_FUNC_TEST_CASE2(name, T, impl_name) \ + template \ + void impl_name(T* = 0); \ + template \ + void name(T* x = 0) { \ + impl_name(x); \ + ::test::end(); \ + } \ + template \ + void impl_name(T*) + +#define RUN_TEST_OP(r, product) \ + RUN_TEST_OP2( \ + BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(1, product)) \ + ), \ + BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_SEQ_ELEM(1, product) \ + ) + +#define RUN_TEST_OP2(name, test_func, type) \ + BOOST_AUTO_UNIT_TEST(name) \ + { \ + test_func((type*) 0); \ + ::test::end(); \ + } + +#define AUTO_META_TESTS(test_seq, param_seq) \ + BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_TEST_OP, (test_seq)(param_seq)) + +#endif diff --git a/test/insert_tests.cpp b/test/insert_tests.cpp new file mode 100644 index 00000000..8166349e --- /dev/null +++ b/test/insert_tests.cpp @@ -0,0 +1,193 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include +#include "./helpers/unit_test.hpp" +#include "./helpers/exception_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/input_iterator_adaptor.hpp" +#include "./strong.hpp" +#include "./invariant.hpp" + +META_FUNC_TEST_CASE(insert_individual,Container) +{ + test::random_values values(100); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + Container x; + + INVARIANT_CHECK(x); + + ACTIVATE_EXCEPTIONS; + + for(typename test::random_values::iterator + it = values.begin(); it != values.end(); ++it) + { + STRONG_TEST(tester, x) { + try { + x.insert(*it); + } catch(test::hash_exception) { + tester.dismiss(); + throw; + } + } + + DEACTIVATE_EXCEPTIONS; + typename Container::iterator pos = x.find(values.get_key(*it)); + BOOST_CHECK(pos != x.end() && + x.key_eq()(values.get_key(*pos), values.get_key(*it))); + } + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(insert_with_previous_item_hint,Container) +{ + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + + Container x; + INVARIANT_CHECK(x); + + ACTIVATE_EXCEPTIONS; + + typename Container::const_iterator prev = x.begin(); + + for(typename test::random_values::iterator + it = values.begin(); it != values.end(); ++it) + { + x.insert(prev, *it); + + typename Container::iterator pos = x.find(values.get_key(*it)); + BOOST_CHECK(pos != x.end() && + x.key_eq()(values.get_key(*pos), values.get_key(*it))); + prev = pos; + } + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(insert_with_begin_hint,Container) +{ + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + Container x; + INVARIANT_CHECK(x); + ACTIVATE_EXCEPTIONS; + + for(typename test::random_values::iterator + it = values.begin(); it != values.end(); ++it) + { + x.insert(x.begin(), *it); + + typename Container::iterator pos = x.find(values.get_key(*it)); + BOOST_CHECK(pos != x.end() && + x.key_eq()(values.get_key(*pos), values.get_key(*it))); + } + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(insert_with_end_hint,Container) +{ + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + Container x; + INVARIANT_CHECK(x); + ACTIVATE_EXCEPTIONS; + + for(typename test::random_values::iterator + it = values.begin(); it != values.end(); ++it) + { + x.insert(x.end(), *it); + + typename Container::iterator pos = x.find(values.get_key(*it)); + BOOST_CHECK(pos != x.end() && + x.key_eq()(values.get_key(*pos), values.get_key(*it))); + } + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(insert_with_random_hint,Container) +{ + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + Container x; + INVARIANT_CHECK(x); + ACTIVATE_EXCEPTIONS; + + for(typename test::random_values::iterator + it = values.begin(); it != values.end(); ++it) + { + using namespace std; + x.insert(boost::next(x.begin(), rand() % (x.size() + 1)), *it); + + typename Container::iterator pos = x.find(values.get_key(*it)); + BOOST_CHECK(pos != x.end() && + x.key_eq()(values.get_key(*pos), values.get_key(*it))); + } + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(insert_range,Container) +{ + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + Container x; + INVARIANT_CHECK(x); + ACTIVATE_EXCEPTIONS; + + x.insert(values.begin(), values.end()); + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(insert_range_input_iterator,Container) +{ + test::random_values values(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + Container x; + INVARIANT_CHECK(x); + ACTIVATE_EXCEPTIONS; + + x.insert(test::make_input_iterator(values.begin()), + test::make_input_iterator(values.end())); + } + EXCEPTION_TEST_END +} + +AUTO_META_TESTS( + (insert_individual)(insert_with_previous_item_hint)(insert_with_begin_hint) + (insert_with_end_hint)(insert_with_random_hint)(insert_range) + (insert_range_input_iterator), + CONTAINER_SEQ +) diff --git a/test/invariant.hpp b/test/invariant.hpp new file mode 100644 index 00000000..a91714cb --- /dev/null +++ b/test/invariant.hpp @@ -0,0 +1,128 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_CONTAINER_INVARIANT_HEADER) +#define BOOST_UNORDERED_TEST_CONTAINER_INVARIANT_HEADER + +#include +#include +#include +#include "./helpers/invariant_checker.hpp" +#include "./helpers/functional.hpp" + +namespace test +{ + template + bool check_matches(Key const&, Pred const&) + { + return true; + } + + bool check_matches(hash const& h, equals const& p) + { + // TODO: This isn't actually true - change functional so that it is. + BOOST_CHECK_EQUAL(h.offset, p.tag); + return true; + } + + template + void invariant_check_container(Container const& x) + { + // Check that the begin and end iterators match the container size. + // (And also that you can iterate through all the elements). + BOOST_CHECK_EQUAL((std::size_t) std::distance(x.begin(), x.end()), + x.size()); + + BOOST_CHECK(check_matches(x.hash_function(), x.key_eq())); + + // It is possible for this to legally fail, eg. if you + // set max_load_factor to lower than the current load factor and + // don't give the table a chance to rehash. + BOOST_WARN(x.load_factor() <= x.max_load_factor()); + } + + template + void invariant_check_set(Container const& x) + { + invariant_check_container(x); + + // Check that the elements are in the correct buckets. + std::size_t count = 0; + std::size_t bucket_count = x.bucket_count(); + for(std::size_t i = 0; i < bucket_count; ++i) { + std::size_t bucket_size = 0; + for(typename Container::const_local_iterator j = x.begin(i); + j != x.end(i); ++j) + { + ++bucket_size; + BOOST_CHECK_EQUAL(i, x.bucket(*j)); + BOOST_CHECK_EQUAL(i, + x.hash_function()(*j) % bucket_count); + } + BOOST_CHECK_EQUAL(bucket_size, x.bucket_size(i)); + + count += bucket_size; + } + + // Check that the size matches what we've just seen. + BOOST_CHECK_EQUAL(count, x.size()); + } + + template + void invariant_check_map(Container const& x) + { + invariant_check_container(x); + + // Check that the elements are in the correct buckets. + std::size_t count = 0; + std::size_t bucket_count = x.bucket_count(); + for(std::size_t i = 0; i < bucket_count; ++i) { + std::size_t bucket_size = 0; + for(typename Container::const_local_iterator j = x.begin(i); + j != x.end(i); ++j) + { + ++bucket_size; + BOOST_CHECK_EQUAL(i, x.bucket(j->first)); + BOOST_CHECK_EQUAL(i, + x.hash_function()(j->first) % bucket_count); + } + BOOST_CHECK_EQUAL(bucket_size, x.bucket_size(i)); + + count += bucket_size; + } + + // Check that the size matches what we've just seen. + BOOST_CHECK_EQUAL(count, x.size()); + } +} + +namespace boost +{ + template + void invariant_impl(boost::unordered_set const& x) + { + test::invariant_check_set(x); + } + + template + void invariant_impl(boost::unordered_multiset const& x) + { + test::invariant_check_set(x); + } + + template + void invariant_impl(boost::unordered_map const& x) + { + test::invariant_check_map(x); + } + + template + void invariant_impl(boost::unordered_multimap const& x) + { + test::invariant_check_map(x); + } +} + +#endif diff --git a/test/iterator_tests.cpp b/test/iterator_tests.cpp new file mode 100644 index 00000000..19a12277 --- /dev/null +++ b/test/iterator_tests.cpp @@ -0,0 +1,141 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include +#include + +#define BOOST_AUTO_TEST_MAIN +#include + +// TODO: Add these headers to new_iterator_tests.hpp +#include +#include +#include + +#include +#include +#include +#include "./helpers/unit_test.hpp" + +BOOST_AUTO_UNIT_TEST(iterator_tests) +{ + boost::unordered_set > set; + boost::unordered_multiset multiset; + boost::unordered_map map; + boost::unordered_multimap > multimap; + + set.insert(std::pair("Anthony","Cleopatra")); + set.insert(std::pair("Victoria","Albert")); + set.insert(std::pair("Pete","Dud")); + set.insert(std::pair("Blair","Brown")); + set.insert(std::pair("John","Yoko")); + set.insert(std::pair("Charles","Diana")); + set.insert(std::pair("Marx","Engels")); + set.insert(std::pair("Sid","Nancy")); + set.insert(std::pair("Lucy","Ricky")); + set.insert(std::pair("George","Mildred")); + set.insert(std::pair("Fanny","Alexander")); + set.insert(std::pair("Den","Angie")); + set.insert(std::pair("Sonny","Cher")); + set.insert(std::pair("Bonnie","Clyde")); + set.insert(std::pair("Punch","Judy")); + set.insert(std::pair("Powell","Pressburger")); + set.insert(std::pair("Jekyll","Hyde")); + + multiset.insert(0.434321); + multiset.insert(443421); + multiset.insert(0.434321); + + map[98] = 3; + map[99] = 4; + map[2000] = 788421; + map[2001] = 2; + + static char* strings1 = "Banjo\0Banjo\0Ukulele"; + + static char* strings[] = { + strings1, + strings1 + 6, + strings1 + 12 + }; + + BOOST_CHECK(std::string(strings[0]) == std::string(strings[1])); + BOOST_CHECK(strings[0] != strings[1]); + + multimap.insert(std::make_pair(strings[0], std::make_pair(5,6))); + multimap.insert(std::make_pair(strings[1], std::make_pair(85,32))); + multimap.insert(std::make_pair(strings[1], std::make_pair(91,142))); + multimap.insert(std::make_pair(strings[2], std::make_pair(12,3))); + multimap.insert(std::make_pair(strings[2], std::make_pair(10,94))); + multimap.insert(std::make_pair(strings[2], std::make_pair(345,42))); + + BOOST_CHECK_EQUAL(multimap.count(strings[0]), 1); + BOOST_CHECK_EQUAL(multimap.count(strings[1]), 2); + BOOST_CHECK_EQUAL(multimap.count(strings[2]), 3); + + boost::forward_readable_iterator_test(set.begin(), set.end(), + *set.begin(), *boost::next(set.begin())); + boost::forward_readable_iterator_test(multiset.begin(), multiset.end(), + *multiset.begin(), *boost::next(multiset.begin())); + boost::forward_readable_iterator_test(map.begin(), map.end(), + *map.begin(), *boost::next(map.begin())); + boost::forward_readable_iterator_test(multimap.begin(), multimap.end(), + *multimap.begin(), *boost::next(multimap.begin())); +} + +BOOST_AUTO_UNIT_TEST(rubbish_iterator_test) +{ + typedef boost::unordered_map map; + typedef map::local_iterator local_iterator; + typedef map::const_local_iterator const_local_iterator; + typedef map::iterator iterator; + typedef map::const_iterator const_iterator; + + map m; + iterator it; + const_iterator const_it(it); + local_iterator local; + const_local_iterator const_local(local); + + m[10] = 3; + m[25] = 5; + m[2] = 21; + + it = m.begin(); + const_it = m.begin(); + map::size_type index = m.bucket(10); + local = m.begin(index); + const_local = m.begin(index); + + BOOST_CHECK(it == const_it); + BOOST_CHECK(const_it == it); + BOOST_CHECK(local == const_local); + BOOST_CHECK(const_local == local); + + BOOST_CHECK(it++ == const_it); + BOOST_CHECK(local++ == const_local); + + BOOST_CHECK(it != const_it); + BOOST_CHECK(const_it != it); + BOOST_CHECK(local != const_local); + BOOST_CHECK(const_local != local); + + BOOST_CHECK(++const_it == it); + BOOST_CHECK(++const_local == local); + + it = m.begin(); + int values[3]; + std::pair const& r1= *it++; + values[0] = r1.second; + values[1] = it++->second; + values[2] = it++->second; + BOOST_CHECK(it == m.end()); + + std::sort(values, values+3); + + BOOST_CHECK_EQUAL(values[0], 3); + BOOST_CHECK_EQUAL(values[1], 5); + BOOST_CHECK_EQUAL(values[2], 21); +} diff --git a/test/map_operator_tests.cpp b/test/map_operator_tests.cpp new file mode 100644 index 00000000..238d2860 --- /dev/null +++ b/test/map_operator_tests.cpp @@ -0,0 +1,69 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include +#include "./helpers/unit_test.hpp" +#include "./helpers/exception_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/input_iterator_adaptor.hpp" +#include "./strong.hpp" +#include "./invariant.hpp" + +// TODO: This is just the first insert test slightly modified, should do better. + +META_FUNC_TEST_CASE(insert_individual,Container) +{ + test::random_values values(100); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + Container x; + + INVARIANT_CHECK(x); + + ACTIVATE_EXCEPTIONS; + + for(typename test::random_values::iterator + it = values.begin(); it != values.end(); ++it) + { + typename Container::value_type::second_type* ref; + + // Looking at TR1 I can't find this requirement anywhere, but it + // would seem silly not to require it so I think it's either an + // omission or I haven't looked hard enough. + STRONG_TEST(tester, x) { + try { + ref = &x[it->first]; + } catch(test::hash_exception) { + tester.dismiss(); + throw; + } + } + + DEACTIVATE_EXCEPTIONS; + *ref = it->second; + + typename Container::iterator pos = x.find(values.get_key(*it)); + BOOST_CHECK(pos != x.end() && + x.key_eq()(values.get_key(*pos), values.get_key(*it)) && + test::equivalent(*it, *pos)); + } + } + EXCEPTION_TEST_END +} + +typedef boost::unordered_map map1; +typedef boost::unordered_map > map2; + +AUTO_META_TESTS( + (insert_individual), + (map1)(map2) +) diff --git a/test/max_load_factor_test.cpp b/test/max_load_factor_test.cpp new file mode 100644 index 00000000..c4a0570b --- /dev/null +++ b/test/max_load_factor_test.cpp @@ -0,0 +1,170 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include +#include + +#define BOOST_AUTO_TEST_MAIN +#include +#include + +#include + +// If the bucket count is higher than the upper bounds in this file, it's not a +// bug (but it's not that great either). There are also some tests on when the +// number of buckets is increased on an insert, these tests are checking that +// some implementation details are working - if the implementation is changed +// they can be removed. +// +// TODO: the argument to max_load_factor is just a hint, so test against the +// container's actual max_load_factor. + +BOOST_AUTO_UNIT_TEST(test_rehash) +{ + boost::counting_iterator begin(0); + boost::counting_iterator end(1000); + + boost::unordered_set x1(begin, end); + BOOST_CHECK(x1.bucket_count() >= 1000); + BOOST_WARN(x1.bucket_count() < 2000); + + x1.max_load_factor(0.5); + x1.rehash(0); + BOOST_CHECK(x1.bucket_count() >= 2000); + BOOST_WARN(x1.bucket_count() < 4000); + x1.rehash(0); + BOOST_CHECK(x1.bucket_count() >= 2000); + BOOST_WARN(x1.bucket_count() < 4000); + + x1.max_load_factor(2.0); + x1.rehash(0); + BOOST_CHECK(x1.bucket_count() >= 500); + BOOST_WARN(x1.bucket_count() < 1000); + x1.rehash(1500); + BOOST_CHECK(x1.bucket_count() >= 1500); + BOOST_WARN(x1.bucket_count() < 3000); + + x1.max_load_factor(0.5); + x1.rehash(0); + BOOST_CHECK(x1.bucket_count() >= 2000); + BOOST_WARN(x1.bucket_count() < 4000); +} + +BOOST_AUTO_UNIT_TEST(test_insert_range) +{ + boost::counting_iterator begin(0); + boost::counting_iterator end(1000); + + boost::unordered_set x1(begin, end); + BOOST_CHECK(x1.bucket_count() >= 1000); + BOOST_WARN(x1.bucket_count() < 2000); + + x1.clear(); + x1.max_load_factor(0.5); + x1.rehash(0); + x1.insert(begin, end); + BOOST_CHECK(x1.bucket_count() >= 2000); + BOOST_WARN(x1.bucket_count() < 4000); + + x1.clear(); + x1.max_load_factor(2.0); + x1.rehash(0); + x1.insert(begin, end); + BOOST_CHECK(x1.bucket_count() >= 500); + BOOST_WARN(x1.bucket_count() < 1000); +} + +BOOST_AUTO_UNIT_TEST(test_insert) +{ + boost::unordered_map x1; + + size_t i; + for(i = 0; i < 1000; ++i) + { + size_t old_bucket_count = x1.bucket_count(); + x1[i] = i; + BOOST_CHECK(i <= x1.bucket_count()); + BOOST_CHECK(x1.bucket_count() == old_bucket_count || + x1.size() >= old_bucket_count); + + // This isn't really required: + BOOST_WARN(x1.size() < x1.bucket_count()); + } + + x1.clear(); + x1.max_load_factor(2.0); + x1.rehash(0); + + for(i = 0; i < 1000; ++i) + { + size_t old_bucket_count = x1.bucket_count(); + x1[i] = i; + BOOST_CHECK(i <= x1.bucket_count() * 2); + BOOST_CHECK(x1.bucket_count() == old_bucket_count || + x1.size() >= old_bucket_count * 2); + + // This isn't really required: + BOOST_WARN(x1.size() < x1.bucket_count() * 2); + } + + x1.clear(); + x1.rehash(0); + x1.max_load_factor(0.5); + + for(i = 0; i < 1000; ++i) + { + size_t old_bucket_count = x1.bucket_count(); + x1[i] = i; + BOOST_CHECK(i * 2 <= x1.bucket_count()); + BOOST_CHECK(x1.bucket_count() == old_bucket_count || + x1.size() * 2 >= old_bucket_count); + + // This isn't really required: + BOOST_WARN(x1.size() * 2 < x1.bucket_count()); + } +} + +BOOST_AUTO_UNIT_TEST(test_large_mlf) +{ + boost::unordered_set x1; + x1.max_load_factor(static_cast( + (std::numeric_limits::size_type>::max)() + ) * 10); + + boost::unordered_map::size_type bucket_count = x1.bucket_count(); + + for(int i = 0; i < 1000; ++i) + { + x1.insert(i); + BOOST_CHECK(x1.bucket_count() == bucket_count); + } + + boost::counting_iterator begin(1000); + boost::counting_iterator end(2000); + x1.insert(begin, end); + BOOST_CHECK(x1.bucket_count() == bucket_count); +} + +BOOST_AUTO_UNIT_TEST(test_infinite_mlf) +{ + if(std::numeric_limits::has_infinity) + { + boost::unordered_set x1; + x1.max_load_factor(std::numeric_limits::infinity()); + + boost::unordered_map::size_type bucket_count = x1.bucket_count(); + + for(int i = 0; i < 1000; ++i) + { + x1.insert(i); + BOOST_CHECK(x1.bucket_count() == bucket_count); + } + + boost::counting_iterator begin(1000); + boost::counting_iterator end(2000); + x1.insert(begin, end); + BOOST_CHECK(x1.bucket_count() == bucket_count); + } +} diff --git a/test/next_prime_tests.cpp b/test/next_prime_tests.cpp new file mode 100644 index 00000000..b3a81f81 --- /dev/null +++ b/test/next_prime_tests.cpp @@ -0,0 +1,18 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include + +#define BOOST_AUTO_TEST_MAIN +#include + +BOOST_AUTO_UNIT_TEST(next_prime_test) +{ + BOOST_CHECK_EQUAL(53ul, boost::unordered_detail::next_prime(0)); + BOOST_CHECK_EQUAL(53ul, boost::unordered_detail::next_prime(52)); + BOOST_CHECK_EQUAL(53ul, boost::unordered_detail::next_prime(53)); + BOOST_CHECK_EQUAL(97ul, boost::unordered_detail::next_prime(54)); + BOOST_CHECK_EQUAL(98317ul, boost::unordered_detail::next_prime(50000ul)); +} diff --git a/test/set_assign_fail_test.cpp b/test/set_assign_fail_test.cpp new file mode 100644 index 00000000..ccf4e93d --- /dev/null +++ b/test/set_assign_fail_test.cpp @@ -0,0 +1,14 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include + +void func() +{ + boost::unordered_set x; + x.insert(10); + boost::unordered_set::iterator it = x.begin(); + *x = 25; +} diff --git a/test/strong.hpp b/test/strong.hpp new file mode 100644 index 00000000..86218568 --- /dev/null +++ b/test/strong.hpp @@ -0,0 +1,102 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TESTS_STRONG_HEADER) +#define BOOST_UNORDERED_TESTS_STRONG_HEADER + +#include "./helpers/strong.hpp" +#include "./helpers/equivalent.hpp" +#include "./helpers/metafunctions.hpp" +#include +#include +#include +#include + +namespace test +{ + struct equals_t + { + template + bool operator()(X const& x, Y const& y) + { + return x == y; + } + + template + bool operator()(std::pair const& x, std::pair const& y) + { + return x.first == y.first && x.second == y.second; + } + } equals1; + + template + class unordered_strong_tester + : public strong_tester + { + Container const& reference_; + + typename Container::size_type size_; + typename Container::hasher hasher_; + typename Container::key_equal key_equal_; + + std::deque::type> values_; + public: + unordered_strong_tester(Container const &x) + : reference_(x), size_(x.size()), + hasher_(x.hash_function()), key_equal_(x.key_eq()), + values_(x.begin(), x.end()) + { + } + + void test() + { + BOOST_CHECK(size_ == reference_.size()); + BOOST_CHECK(test::equivalent(hasher_, reference_.hash_function())); + BOOST_CHECK(test::equivalent(key_equal_, reference_.key_eq())); + BOOST_CHECK(values_.size() == reference_.size()); + BOOST_CHECK(values_.size() == reference_.size() && + std::equal(values_.begin(), values_.end(), reference_.begin(), equals1)); + } + private: + unordered_strong_tester(); + }; +} + +namespace boost +{ + template + test::strong_tester_ptr create_tester_impl( + boost::unordered_set const& x, int) + { + return test::strong_tester_ptr(new test::unordered_strong_tester< + boost::unordered_set >(x)); + } + + template + test::strong_tester_ptr create_tester_impl( + boost::unordered_multiset const& x, int) + { + return test::strong_tester_ptr(new test::unordered_strong_tester< + boost::unordered_multiset >(x)); + } + + template + test::strong_tester_ptr create_tester_impl( + boost::unordered_map const& x, int) + { + return test::strong_tester_ptr(new test::unordered_strong_tester< + boost::unordered_map >(x)); + } + + template + test::strong_tester_ptr create_tester_impl( + boost::unordered_multimap const& x, int) + { + return test::strong_tester_ptr(new test::unordered_strong_tester< + boost::unordered_multimap >(x)); + } +} + +#endif diff --git a/test/swap_tests.hpp b/test/swap_tests.hpp new file mode 100644 index 00000000..1c4bbbf3 --- /dev/null +++ b/test/swap_tests.hpp @@ -0,0 +1,120 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include +#include "./helpers/unit_test.hpp" +#include "./helpers/exception_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/constructors.hpp" +#include "./helpers/equivalent.hpp" +#include "./invariant.hpp" + +META_FUNC_TEST_CASE(swap_test1, Container) +{ + test::constructors constructor; + + test::random_values values_x(10); + test::random_values values_y(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + + Container x(values_x.begin(), values_x.end(), 0, + constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); + x.max_load_factor(0.5); + Container y(values_y.begin(), values_y.end(), 0, + constructor.hasher(23), constructor.key_equal(23), constructor.allocator(12)); + y.max_load_factor(2.0); + + INVARIANT_CHECK(x); + INVARIANT_CHECK(y); + + ACTIVATE_EXCEPTIONS; + + if(BOOST_UNORDERED_SWAP_METHOD == 1 + && !test::equivalent(x.get_allocator(), y.get_allocator())) { + BOOST_CHECK_THROW(x.swap(y), std::runtime_error); + } + else { +#if BOOST_UNORDERED_SWAP_METHOD == 2 + // Swap Method 2 can't meet TR1's exception specifications. Sigh. + x.swap(y); +#else + try { + x.swap(y); + } + catch (test::hash_copy_exception&) { + throw; + } + catch (test::pred_copy_exception&) { + throw; + } + catch (...) { + BOOST_ERROR("Only the copy constructor/assigned of the hash " + "and predicate objects are allowed to throw in swap."); + throw; + } +#endif + + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(23))); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(23))); + BOOST_CHECK_EQUAL(x.max_load_factor(), 2.0); +#if BOOST_UNORDERED_SWAP_METHOD == 2 + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); +#else + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(12))); +#endif + + BOOST_CHECK(test::equivalent(y.hash_function(), constructor.hasher(55))); + BOOST_CHECK(test::equivalent(y.key_eq(), constructor.key_equal(55))); + BOOST_CHECK_EQUAL(y.max_load_factor(), 0.5); +#if BOOST_UNORDERED_SWAP_METHOD == 2 + BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator(12))); +#else + BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator(10))); +#endif + } + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(self_swap, Container) +{ + test::constructors constructor; + test::random_values values_x(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + + Container x(values_x.begin(), values_x.end(), 0, + constructor.hasher(55), constructor.key_equal(55), + constructor.allocator(10)); + x.max_load_factor(0.5); + + INVARIANT_CHECK(x); + + ACTIVATE_EXCEPTIONS; + + x.swap(x); + + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); + BOOST_CHECK_EQUAL(x.max_load_factor(), 0.5); + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); + } + EXCEPTION_TEST_END +} + +AUTO_META_TESTS( + (swap_test1)(self_swap), + CONTAINER_SEQ +) diff --git a/test/swap_tests.ipp b/test/swap_tests.ipp new file mode 100644 index 00000000..67623d99 --- /dev/null +++ b/test/swap_tests.ipp @@ -0,0 +1,102 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_AUTO_TEST_MAIN +#include + +#include +#include "./helpers/unit_test.hpp" +#include "./helpers/exception_test.hpp" +#include "./helpers/random_values.hpp" +#include "./helpers/constructors.hpp" +#include "./helpers/equivalent.hpp" +#include "./invariant.hpp" + +META_FUNC_TEST_CASE(swap_test1, Container) +{ + test::constructors constructor; + + test::random_values values_x(10); + test::random_values values_y(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + + Container x(values_x.begin(), values_x.end(), 0, + constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); + x.max_load_factor(0.5); + Container y(values_y.begin(), values_y.end(), 0, + constructor.hasher(23), constructor.key_equal(23), constructor.allocator(12)); + y.max_load_factor(2.0); + + INVARIANT_CHECK(x); + INVARIANT_CHECK(y); + + ACTIVATE_EXCEPTIONS; + + if(BOOST_UNORDERED_SWAP_METHOD == 1 + && !test::equivalent(x.get_allocator(), y.get_allocator())) { + BOOST_CHECK_THROW(x.swap(y), std::runtime_error); + } + else { + x.swap(y); + + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(23))); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(23))); + BOOST_CHECK_EQUAL(x.max_load_factor(), 2.0); +#if BOOST_UNORDERED_SWAP_METHOD == 2 + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); +#else + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(12))); +#endif + + BOOST_CHECK(test::equivalent(y.hash_function(), constructor.hasher(55))); + BOOST_CHECK(test::equivalent(y.key_eq(), constructor.key_equal(55))); + BOOST_CHECK_EQUAL(y.max_load_factor(), 0.5); +#if BOOST_UNORDERED_SWAP_METHOD == 2 + BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator(12))); +#else + BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator(10))); +#endif + } + } + EXCEPTION_TEST_END +} + +META_FUNC_TEST_CASE(self_swap, Container) +{ + test::constructors constructor; + test::random_values values_x(10); + + EXCEPTION_TEST(1000) + { + DEACTIVATE_EXCEPTIONS; + + Container x(values_x.begin(), values_x.end(), 0, + constructor.hasher(55), constructor.key_equal(55), + constructor.allocator(10)); + x.max_load_factor(0.5); + + INVARIANT_CHECK(x); + + ACTIVATE_EXCEPTIONS; + + x.swap(x); + + BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); + BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); + BOOST_CHECK_EQUAL(x.max_load_factor(), 0.5); + BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); + } + EXCEPTION_TEST_END +} + +AUTO_META_TESTS( + (swap_test1)(self_swap), + CONTAINER_SEQ +) diff --git a/test/swap_tests1.cpp b/test/swap_tests1.cpp new file mode 100644 index 00000000..5a61be4f --- /dev/null +++ b/test/swap_tests1.cpp @@ -0,0 +1,7 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#define BOOST_UNORDERED_SWAP_METHOD 1 +#include "./swap_tests.hpp" diff --git a/test/swap_tests2.cpp b/test/swap_tests2.cpp new file mode 100644 index 00000000..56ccbd27 --- /dev/null +++ b/test/swap_tests2.cpp @@ -0,0 +1,7 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#define BOOST_UNORDERED_SWAP_METHOD 2 +#include "./swap_tests.hpp" diff --git a/test/swap_tests3.cpp b/test/swap_tests3.cpp new file mode 100644 index 00000000..13a5951f --- /dev/null +++ b/test/swap_tests3.cpp @@ -0,0 +1,7 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#define BOOST_UNORDERED_SWAP_METHOD 3 +#include "./swap_tests.hpp" diff --git a/test/type_tests.cpp b/test/type_tests.cpp new file mode 100644 index 00000000..3c494b5c --- /dev/null +++ b/test/type_tests.cpp @@ -0,0 +1,206 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include +#include +#include "./helpers/functional.hpp" + +template +struct iterator_checks +{ + typedef Iterator iterator; + + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_value::type, + typename Container::value_type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_reference::type, + typename Container::reference>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_pointer::type, + typename Container::pointer>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_difference::type, + typename Container::difference_type>)); +}; + +template +struct const_iterator_checks +{ + typedef ConstIterator const_iterator; + + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_value::type, + typename Container::value_type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_reference::type, + typename Container::const_reference>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_pointer::type, + typename Container::const_pointer>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_difference::type, + typename Container::difference_type>)); +}; + +template +struct unordered_typedef_checks +{ + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + typedef typename Container::local_iterator local_iterator; + typedef typename Container::const_local_iterator const_local_iterator; + + // 6.3.1/9 rows 1-3 + BOOST_MPL_ASSERT((boost::is_same< + Key, + typename Container::key_type>)); + BOOST_MPL_ASSERT((boost::is_same< + Hash, + typename Container::hasher>)); + BOOST_MPL_ASSERT((boost::is_same< + Pred, + typename Container::key_equal>)); + + // 6.3.1/9 rows 4-5 + // TODO: A local_iterator may be used to iterate through a single + // bucket but may not be used to iterate across buckets. + BOOST_MPL_ASSERT((boost::is_same< + typename boost::BOOST_ITERATOR_CATEGORY::type, + typename boost::BOOST_ITERATOR_CATEGORY::type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_value::type, + typename boost::iterator_value::type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_difference::type, + typename boost::iterator_difference::type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_pointer::type, + typename boost::iterator_pointer::type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_reference::type, + typename boost::iterator_reference::type>)); + + BOOST_MPL_ASSERT((boost::is_same< + typename boost::BOOST_ITERATOR_CATEGORY::type, + typename boost::BOOST_ITERATOR_CATEGORY::type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_value::type, + typename boost::iterator_value::type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_difference::type, + typename boost::iterator_difference::type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_pointer::type, + typename boost::iterator_pointer::type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_reference::type, + typename boost::iterator_reference::type>)); + + // TODO: Is this ever specified? + BOOST_MPL_ASSERT((boost::is_same< + AllocatorType, + typename Container::allocator_type>)); + BOOST_MPL_ASSERT((boost::is_same< + typename AllocatorType::pointer, + typename Container::pointer>)); + BOOST_MPL_ASSERT((boost::is_same< + typename AllocatorType::const_pointer, + typename Container::const_pointer>)); + BOOST_MPL_ASSERT((boost::is_same< + typename AllocatorType::reference, + typename Container::reference>)); + BOOST_MPL_ASSERT((boost::is_same< + typename AllocatorType::const_reference, + typename Container::const_reference>)); +}; + +template +struct unordered_map_checks +{ + unordered_typedef_checks c1; + iterator_checks c2; + const_iterator_checks c3; + iterator_checks c4; + const_iterator_checks c5; + + // 6.3.1/7 + BOOST_MPL_ASSERT((boost::is_same< + typename Container::value_type, + std::pair >)); +}; + +template +struct unordered_set_checks +{ + unordered_typedef_checks c1; + const_iterator_checks c2; + const_iterator_checks c3; + const_iterator_checks c4; + const_iterator_checks c5; + + // 6.3.1/7 + BOOST_MPL_ASSERT((boost::is_same< + typename Container::value_type, + typename Container::key_type>)); +}; + +unordered_set_checks< + boost::unordered_multiset, int, + boost::hash, std::equal_to, std::allocator +> int_multiset_check; + +unordered_set_checks< + boost::unordered_multiset, + std::string, test::hash, test::less, std::allocator +> custom_string_multiset_check; + +unordered_set_checks< + boost::unordered_set, int, + boost::hash, std::equal_to, std::allocator +> int_set_check; + +unordered_set_checks< + boost::unordered_set, + std::string, test::hash, test::less, std::allocator +> custom_string_set_check; + +unordered_map_checks< + boost::unordered_map, std::string, int, + boost::hash, std::equal_to, + std::allocator > +> string_int_check; + +unordered_map_checks< + boost::unordered_map, + char const*, std::string, + test::hash, test::less, + std::allocator > +> custom_check; + +unordered_map_checks< + boost::unordered_multimap, + int, int, + boost::hash, std::equal_to, + std::allocator > +> int_int_multi_check; + +unordered_map_checks< + boost::unordered_multimap, + std::string, int, + boost::hash, std::equal_to, + std::allocator > +> string_int_multi_check; + +unordered_map_checks< + boost::unordered_multimap, + float, std::string, + test::hash, test::less, + std::allocator > +> custom_multi_check; From 8e7ff75ac81d67981b6d3c0c11d62b8a44561801 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 8 Nov 2005 21:30:34 +0000 Subject: [PATCH 007/175] Add a missing include. [SVN r2735] --- test/type_tests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/type_tests.cpp b/test/type_tests.cpp index 3c494b5c..56f8c490 100644 --- a/test/type_tests.cpp +++ b/test/type_tests.cpp @@ -5,6 +5,8 @@ #include #include + +#include #include #include #include From dc9951437b6745d202fc091e2801d1321a0b4e95 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 9 Nov 2005 21:11:28 +0000 Subject: [PATCH 008/175] Improve this test on Visual C++ 6, still fails though. [SVN r2737] --- test/container_tests.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/test/container_tests.cpp b/test/container_tests.cpp index 48aa47fb..dd4fd8f1 100644 --- a/test/container_tests.cpp +++ b/test/container_tests.cpp @@ -30,23 +30,32 @@ void container_tests(X*, T*) typedef typename X::difference_type difference_type; typedef typename X::size_type size_type; - BOOST_MPL_ASSERT((boost::is_same)); + typedef typename iterator::value_type iterator_value_type; + typedef typename const_iterator::value_type const_iterator_value_type; + typedef typename iterator::difference_type iterator_difference_type; + typedef typename const_iterator::difference_type const_iterator_difference_type; + + typedef typename X::value_type value_type; + typedef typename X::reference reference; + typedef typename X::const_reference const_reference; + + BOOST_MPL_ASSERT((boost::is_same)); // TODO: Actually 'lvalue of T'/'const lvalue of T' - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); // TODO: Iterator checks. - BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); BOOST_MPL_ASSERT((boost::is_convertible)); - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); BOOST_MPL_ASSERT((boost::mpl::bool_::is_signed>)); BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); BOOST_MPL_ASSERT_NOT((boost::mpl::bool_::is_signed>)); BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); @@ -68,6 +77,7 @@ void container_tests2(X& a) typedef typename X::const_iterator const_iterator; typedef typename X::difference_type difference_type; typedef typename X::size_type size_type; + typedef typename X::allocator_type allocator_type; test::unordered_equivalence_tester equivalent(a); @@ -169,7 +179,7 @@ void container_tests2(X& a) // TODO: 23.1/9 - Make sure this is checked for all constructors. { - check_return_type::equals(a.get_allocator()); + check_return_type::equals(a.get_allocator()); } // TODO: 23.1/11 - Exception safety: From 4c3417468bfded3fb9cffc71555aafa6794b2bf2 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 9 Nov 2005 21:15:19 +0000 Subject: [PATCH 009/175] Fix a constructor for Visual C++ 6. [SVN r2738] --- include/boost/unordered/detail/hash_table.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 9638e15e..65d6eac7 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -944,7 +944,7 @@ namespace boost { template hash_table(I i, I j, size_type n, hasher const& hf, key_equal const& eq, - node_allocator const& a) + value_allocator const& a) : data(initial_size(i, j, n), a), // throws, cleans itself up func1_(hf, eq), // throws " " func2_(hf, eq), // throws " " From ee93aad35a842a32df7350a3fc6833a907b13ae1 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 26 Feb 2006 18:33:49 +0000 Subject: [PATCH 010/175] New version of the unordered containers. Includes workarounds for older compilers. Also follows the exception requirements closer. [SVN r2875] --- include/boost/unordered/detail/allocator.hpp | 42 ++++++-- include/boost/unordered/detail/hash_table.hpp | 100 ++++++++++++------ include/boost/unordered_map.hpp | 18 +++- include/boost/unordered_set.hpp | 20 +++- 4 files changed, 137 insertions(+), 43 deletions(-) diff --git a/include/boost/unordered/detail/allocator.hpp b/include/boost/unordered/detail/allocator.hpp index 7725eb8b..aac39b3a 100644 --- a/include/boost/unordered/detail/allocator.hpp +++ b/include/boost/unordered/detail/allocator.hpp @@ -39,6 +39,18 @@ namespace boost { }; #endif +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) + template + inline void reset(T& x) { x = T(); } +#else + template + inline void reset_impl(T& x, ...) { x = T(); } + template + inline void reset_impl(T*& x, int) { x = 0; } + template + inline void reset(T& x) { reset_impl(x); } +#endif + // Work around for Microsoft's ETI bug. template struct allocator_value_type @@ -66,7 +78,7 @@ namespace boost { typedef typename Allocator::const_reference type; }; - #if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) +#if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) template <> struct allocator_value_type @@ -98,7 +110,7 @@ namespace boost { typedef int type; }; - #endif +#endif template struct allocator_constructor @@ -109,7 +121,12 @@ namespace boost { pointer ptr_; allocator_constructor(Allocator& a) - : alloc_(a), ptr_() {} + : alloc_(a), ptr_() + { +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + unordered_detail::reset(ptr_); +#endif + } ~allocator_constructor() { if (ptr_) alloc_.deallocate(ptr_, 1); @@ -117,10 +134,11 @@ namespace boost { template pointer construct(V const& v) { + BOOST_ASSERT(!ptr_); pointer p = alloc_.allocate(1); ptr_ = p; alloc_.construct(p, v); - ptr_ = pointer(); + reset(ptr_); return p; } @@ -128,7 +146,7 @@ namespace boost { pointer release() { pointer p = ptr_; - ptr_ = pointer(); + reset(ptr_); return p; } }; @@ -144,7 +162,13 @@ namespace boost { std::size_t length_; allocator_array_constructor(Allocator& a) - : alloc_(a), ptr_(), constructed_(), length_(0) {} + : alloc_(a), ptr_(), constructed_(), length_(0) + { +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + unordered_detail::reset(constructed_); + unordered_detail::reset(ptr_); +#endif + } ~allocator_array_constructor() { if (ptr_) { @@ -158,6 +182,7 @@ namespace boost { template void construct(V const& v, std::size_t l) { + BOOST_ASSERT(!ptr_); length_ = l; ptr_ = alloc_.allocate(length_); pointer end = ptr_ + length_; @@ -173,9 +198,12 @@ namespace boost { pointer release() { pointer p(ptr_); - ptr_ = pointer(); + reset(ptr_); return p; } + private: + allocator_array_constructor(allocator_array_constructor const&); + allocator_array_constructor& operator=(allocator_array_constructor const&); }; } } diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 65d6eac7..1e8c6cfc 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -33,10 +33,11 @@ #include #include #include +#include #include -#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) #include #endif @@ -49,6 +50,12 @@ #include #endif +#if BOOST_WORKAROUND(__BORLANDC__, <= 0x0551) +#define BOOST_HASH_BORLAND_BOOL(x) (bool)(x) +#else +#define BOOST_HASH_BORLAND_BOOL(x) x +#endif + namespace boost { namespace unordered_detail { template struct type_wrapper {}; @@ -56,18 +63,16 @@ namespace boost { const static std::size_t default_initial_bucket_count = 50; inline std::size_t next_prime(std::size_t n); - // I bet this is already in boost somewhere. - template - void hash_swap(T& x, T& y) + inline void hash_swap(T& x, T& y) { using namespace std; swap(x, y); } - std::size_t float_to_size_t(float f) + inline std::size_t float_to_size_t(float f) { - return f > (std::numeric_limits::max)() ? + return f > static_cast((std::numeric_limits::max)()) ? (std::numeric_limits::max)() : static_cast(f); } @@ -162,9 +167,12 @@ namespace boost { bucket() : next_() { +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + unordered_detail::reset(next_); +#endif } - bucket(bucket const& x) : next_() + bucket(bucket const& x) : next_(x.next_) { // Only copy construct when allocating. BOOST_ASSERT(!x.next_); @@ -205,6 +213,9 @@ namespace boost { : node_alloc_(n), bucket_alloc_(b), value_alloc_(n), ptr_(), value_allocated_(false), bucket_allocated_(false) { +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + unordered_detail::reset(ptr_); +#endif } ~node_constructor() @@ -224,7 +235,7 @@ namespace boost { template void construct(V const& v) { - assert(!ptr_); + BOOST_ASSERT(!ptr_); value_allocated_ = bucket_allocated_ = false; ptr_ = node_alloc_.allocate(1); @@ -242,9 +253,13 @@ namespace boost { link_ptr release() { node_ptr p = ptr_; - ptr_ = node_ptr(); + unordered_detail::reset(ptr_); return bucket_alloc_.address(*p); } + + private: + node_constructor(node_constructor const&); + node_constructor& operator=(node_constructor const&); }; #else class node_constructor @@ -262,14 +277,19 @@ namespace boost { link_ptr node_pointer_; local_iterator_base() - : node_pointer_() {} + : node_pointer_() + { +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + unordered_detail::reset(node_pointer_); +#endif + } explicit local_iterator_base(link_ptr n) : node_pointer_(n) {} bool not_finished() const { - return node_pointer_; + return node_pointer_ ? true : false; } bool operator==(local_iterator_base const& x) const @@ -318,7 +338,7 @@ namespace boost { bool not_finished() const { - return *prev_ptr; + return *prev_ptr ? true : false; } value_type& operator*() const @@ -435,7 +455,9 @@ namespace boost { void add_end_marker() { - BOOST_ASSERT(buckets_ && !buckets_[bucket_count_].next_); + BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && + !buckets_[bucket_count_].next_); + #if !defined(BOOST_UNORDERED_PARANOID) buckets_[bucket_count_].next_ = buckets_ + bucket_count_; #else @@ -454,8 +476,10 @@ namespace boost { void move_end_marker(hash_table_data& src) { - BOOST_ASSERT(buckets_ && !buckets_[bucket_count_].next_); - BOOST_ASSERT(src.buckets_ && src.buckets_[src.bucket_count_].next_); + BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && + !buckets_[bucket_count_].next_); + BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(src.buckets_) && + BOOST_HASH_BORLAND_BOOL(src.buckets_[src.bucket_count_].next_)); #if !defined(BOOST_UNORDERED_PARANOID) buckets_[bucket_count_].next_ = buckets_ + bucket_count_; @@ -469,12 +493,13 @@ namespace boost { } #endif - src.buckets_[src.bucket_count_].next_ = link_ptr(); + unordered_detail::reset(src.buckets_[src.bucket_count_].next_); } void remove_end_marker() { - BOOST_ASSERT(buckets_ && buckets_[bucket_count_].next_); + BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && + BOOST_HASH_BORLAND_BOOL(buckets_[bucket_count_].next_)); #if defined(BOOST_UNORDERED_PARANOID) if(!is_pointer_allocator) @@ -482,7 +507,7 @@ namespace boost { buckets_[bucket_count_].next_), 1); #endif - buckets_[bucket_count_].next_ = link_ptr(); + unordered_detail::reset(buckets_[bucket_count_].next_); } private: @@ -847,7 +872,7 @@ namespace boost { class functions { -#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) boost::compressed_pair functions_; #else std::pair functions_; @@ -860,7 +885,7 @@ namespace boost { hasher const& hash_function() const { -#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) return functions_.first(); #else return functions_.first; @@ -869,7 +894,7 @@ namespace boost { key_equal const& key_eq() const { -#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) return functions_.second(); #else return functions_.second; @@ -927,7 +952,7 @@ namespace boost { }; template - std::size_t initial_size(I i, I j, size_type n, + std::size_t initial_size(I, I, size_type n, boost::incrementable_traversal_tag) { return n; @@ -1110,8 +1135,9 @@ namespace boost { // accessors + // TODO: This creates an unnecessary copy. // no throw - node_allocator const& get_allocator() const + value_allocator get_allocator() const { return this->node_alloc_; } @@ -1336,10 +1362,8 @@ namespace boost { static void copy_buckets(data const& src, data& dst, functions const& f) { BOOST_ASSERT(dst.size_ == 0); - // no throw: bucket_ptr end = src.buckets_ + src.bucket_count_; - hasher const& hf = f.hash_function(); // no throw: @@ -1565,16 +1589,29 @@ namespace boost { private: - // basic exception safety + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise template void insert_for_range(I i, I j, boost::random_access_traversal_tag) { - reserve(size() + (j - i)); // basic/strong - for (; i != j; ++i) unchecked_insert(*i); // strong + if(EquivalentKeys) { + std::size_t distance = j - i; + if(distance == 1) { + insert(*i); + } + else { + reserve(size() + distance); // basic/strong + for (; i != j; ++i) unchecked_insert(*i); // strong + } + } + else { + for (; i != j; ++i) insert_unique(*i); // basic/strong + } } - // basic exception safety + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise template void insert_for_range(I i, I j, boost::incrementable_traversal_tag) @@ -1584,7 +1621,8 @@ namespace boost { public: - // basic exception safety + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise template void insert(InputIterator i, InputIterator j) { @@ -1927,5 +1965,7 @@ namespace boost { } // namespace boost::unordered_detail } // namespace boost +#undef BOOST_HASH_BORLAND_BOOL + #endif // BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 0e68f640..5ea8fc27 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -107,9 +107,16 @@ namespace boost * the hash function and eq as the key equality predicate, and inserts * elements from [i,j) into it. a is used as the allocator. */ + template + unordered_map(InputIterator f, InputIterator l) + : base(f, l, boost::unordered_detail::default_initial_bucket_count, + hasher(), key_equal(), allocator_type()) + { + } + template unordered_map(InputIterator f, InputIterator l, - size_type n = boost::unordered_detail::default_initial_bucket_count, + size_type n, const hasher &hf = hasher(), const key_equal &eql = key_equal(), const allocator_type &a = allocator_type()) @@ -388,9 +395,16 @@ namespace boost { } + template + unordered_multimap(InputIterator f, InputIterator l) + : base(f, l, boost::unordered_detail::default_initial_bucket_count, + hasher(), key_equal(), allocator_type()) + { + } + template unordered_multimap(InputIterator f, InputIterator l, - size_type n = boost::unordered_detail::default_initial_bucket_count, + size_type n, const hasher &hf = hasher(), const key_equal &eql = key_equal(), const allocator_type &a = allocator_type()) diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index b9b37cb7..c1c19e36 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -81,8 +81,14 @@ namespace boost } template - unordered_set(InputIterator f, InputIterator l, - size_type n = boost::unordered_detail::default_initial_bucket_count, + unordered_set(InputIterator f, InputIterator l) + : base(f, l, boost::unordered_detail::default_initial_bucket_count, + hasher(), key_equal(), allocator_type()) + { + } + + template + unordered_set(InputIterator f, InputIterator l, size_type n, const hasher &hf = hasher(), const key_equal &eql = key_equal(), const allocator_type &a = allocator_type()) @@ -355,8 +361,14 @@ namespace boost } template - unordered_multiset(InputIterator f, InputIterator l, - size_type n = boost::unordered_detail::default_initial_bucket_count, + unordered_multiset(InputIterator f, InputIterator l) + : base(f, l, boost::unordered_detail::default_initial_bucket_count, + hasher(), key_equal(), allocator_type()) + { + } + + template + unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher &hf = hasher(), const key_equal &eql = key_equal(), const allocator_type &a = allocator_type()) From 235ea1ee81fee240ade2b3abc9f25aaca7c0123f Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 26 Feb 2006 19:03:11 +0000 Subject: [PATCH 011/175] Add the work in progress documentation for the unordered associative containers. [SVN r2876] --- doc/Jamfile.v2 | 9 + doc/comparison.qbk | 67 + doc/intro.qbk | 34 + doc/ref.xml | 3022 ++++++++++++++++++++++++++++++++++++++++++++ doc/unordered.qbk | 18 + 5 files changed, 3150 insertions(+) create mode 100644 doc/Jamfile.v2 create mode 100644 doc/comparison.qbk create mode 100644 doc/intro.qbk create mode 100644 doc/ref.xml create mode 100644 doc/unordered.qbk diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 new file mode 100644 index 00000000..4ae69db4 --- /dev/null +++ b/doc/Jamfile.v2 @@ -0,0 +1,9 @@ + +# Copyright Daniel James 2005. Use, modification, and distribution are +# 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) + +using quickbook ; + +xml unordered : unordered.qbk ; +boostbook standalone : unordered ; diff --git a/doc/comparison.qbk b/doc/comparison.qbk new file mode 100644 index 00000000..b60c5ada --- /dev/null +++ b/doc/comparison.qbk @@ -0,0 +1,67 @@ +[section:comparison Comparison to Associative Containers] + +The unordered associative containers have a very similar interface to the +associative containers. For example: + + #include + + ... + + typedef ``[classref boost::unordered_map]`` map; + map x; + x["one"] = 1; + x["two"] = 2; + x["three"] = 3; + + std::cout<, + typename Pred = std::equal_to, + typename Alloc = std::allocator > + class ``[classref boost::unordered_set unordered_set]``; + +The hash function comes first as you might want to change the hash function +but not the equality predicate, while if you were to change the behaviour +of the equality predicate you would have to change the hash function to match +it. + +For example, if you wanted to use + +[endsect] + +[endsect] diff --git a/doc/intro.qbk b/doc/intro.qbk new file mode 100644 index 00000000..8457b2ff --- /dev/null +++ b/doc/intro.qbk @@ -0,0 +1,34 @@ +[def __tr1__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1745.pdf + C++ Standard Library Technical Report]] +[def __hash-function__ [@http://en.wikipedia.org/wiki/Hash_function + hash function]] + +[section:intro Introduction] + +For accessing data based on keys, the C++ standard library offers `std::set`, +`std::map`, `std::multiset` and `std::multimap`. These are generally +implemented using balanced binary trees so accessing data by key is +consistently of logarithmic complexity. Which is generally okay, but not great. + +Also, they require their elements to be ordered, and to supply a 'less than' +comparison object. For some data types this is impractacle, It might be slow +to calculate, or even impossible. + +So the __tr1__ provides unordered associative containers. These will store data +with no ordering, and typically allow for fast constant time access. Their +worst case complexity is linear, but this is generally rare, and with +care can be avoided. There are four containers to match the existing +associate containers: +[classref boost::unordered_set unordered_set], +[classref boost::unordered_map unordered_map], +[classref boost::unordered_multiset unordered_multiset] and +[classref boost::unordered_multimap unordered_multimap]. + +The fast lookup speeds are acheived using a __hash-function__. The basic idea +is that a function is called for the key value, which is used to index the +data. If this hash function is carefully chosen the different keys will usually +get different indicies, so there will only be a very small number of keys for +each index, so a key lookup won't have to look at many keys to find the correct +one. + +[endsect] diff --git a/doc/ref.xml b/doc/ref.xml new file mode 100644 index 00000000..eb6653ab --- /dev/null +++ b/doc/ref.xml @@ -0,0 +1,3022 @@ + + +
+ + +
+ + + + + + + An unordered associative container that stores unique values. + + + For the normative reference see section 6.3 of + TR1 [n1836]. + + + + Template Parameters + + + + + + Value + + Value must be Assignable and CopyConstructible + + + + + Hash + + A unary function object type that acts a hash function for a + Value. It takes a single argument of type + Value and returns a value of type std::size_t. + + + + + Pred + + A binary function object that implements an equivalence relation on values of type + Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + + + + + Alloc + + An allocator whose value type is the same as the container's value type. + + + + + + + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. + + The number of buckets is automatically increased whenever an insert will make the load factor greater than the maximum load factor. It can also change as result of calling rehash. + + When the number of buckets change: iterators are invalidated, the elements can change order, and move to different buckets, but pointers and references to elements remain valid. + + + + Value + + + Value + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + + lvalue of + value_type. + + + + + typename allocator_type::const_reference + + const lvalue of + value_type. + + + + + implementation-defined + unsigned integral type + + size_type can represent any non-negative value of + difference_type. + + + + + implementation-defined + signed integral type + Is identical to the difference type of + iterator and + const_iterator. + + + + + implementation-defined + + A constant iterator whose value type is + value_type. + + + Any iterator category except output iterator. + + Convertible to + const_iterator. + + + + + + implementation-defined + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + + + + + implementation-defined + An iterator with the same value type, difference type and pointer and reference type as + iterator. + + A local_iterator object can be used to iterate through a single bucket. + + + + implementation-defined + A constant iterator with the same value type, difference type and pointer and reference type as + const_iterator. + + A const_local_iterator object can be used to iterate through a single bucket. + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. + + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + + unordered_set const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + + unordered_set const& + + unordered_set&The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + + + size_type + + std::distance(begin(), end()) + + + + + value_type const& + + std::pair<iterator, bool> + + Inserts obj in the container if and only if there is no element in the container with an equivalent value. + + + + The bool component of the return type is true if an insert took place. + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + + value_type const& + + iterator + + + + const_iterator + + + value_type const& + + const_iterator + + + Inserts obj in the container if and only if there is no element in the container with an equivalent value. + + hint is a suggestion to where the element should be inserted. + + + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent value. + + + + When inserting a single element, if an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + iterator + + + + const_iterator + + const_iterator + + + Erase the element pointed to by + position. + + + + The iterator following + position before the erasure. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + + + key_type const& + + size_type + + + Erase all elements with key equivalent to + k. + + + + The number of elements erased. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + + + + + + iterator + + + iterator + + iterator + + + + const_iterator + + + const_iterator + + const_iterator + + + Erases the elements in the range from + first to + last. + + + + The iterator following the erased elements - i.e. + last. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + void + + Erases all elements in the container. + + + + + size() == 0 + + + + Never throws an exception. + + + + + + unordered_set& + + void + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of + key_equal or + hasher. + + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to + k, or + b.end() if no such element exists. + + + + + + key_type const& + + size_type + + The number of elements with key equivalent to + k. + + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<iterator, iterator> + + + A range with containing all elements with key equivalent to + k. If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + + size_type + + An upper bound on the number of buckets. + + + + + + size_type + + size_type + + + n < bucket_count() + + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key + k. + + + + The return value is less than + bucket_count() + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the first element in the bucket with index + n. + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the 'one past the end' element in the bucket with index + n. + + + + + + + float + + The average number of elements per bucket. + + + + + float + + Returns the current maximum load factor. + + + + + + float + + float + + Changes the container's maximum load factor,using + z as a hint. + + + + + + size_type + + void + + Changes the number of buckets so that there at least + n buckets, and so that the load factor is less thanthe maximum load factor. + + Invalidates iterators, and changes the order of elements + + + + The function has no effect if an exception is throw, unless it is thrown by the container’s hash function or comparison function. + + + + + + + + + unordered_set<Key, T, Hash, Pred, Alloc>& + + + unordered_set<Key, T, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of + Hash or + Pred. + + + + + + + + + An unordered associative container that stores values. The same key can be stored multiple times. + + + For the normative reference see section 6.3 of + TR1 [n1836]. + + + + Template Parameters + + + + + + Value + + Value must be Assignable and CopyConstructible + + + + + Hash + + A unary function object type that acts a hash function for a + Value. It takes a single argument of type + Value and returns a value of type std::size_t. + + + + + Pred + + A binary function object that implements an equivalence relation on values of type + Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + + + + + Alloc + + An allocator whose value type is the same as the container's value type. + + + + + + + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. + + The number of buckets is automatically increased whenever an insert will make the load factor greater than the maximum load factor. It can also change as result of calling rehash. + + When the number of buckets change: iterators are invalidated, the elements can change order, and move to different buckets, but pointers and references to elements remain valid. + + + + Value + + + Value + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + + lvalue of + value_type. + + + + + typename allocator_type::const_reference + + const lvalue of + value_type. + + + + + implementation-defined + unsigned integral type + + size_type can represent any non-negative value of + difference_type. + + + + + implementation-defined + signed integral type + Is identical to the difference type of + iterator and + const_iterator. + + + + + implementation-defined + + A constant iterator whose value type is + value_type. + + + Any iterator category except output iterator. + + Convertible to + const_iterator. + + + + + + implementation-defined + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + + + + + implementation-defined + An iterator with the same value type, difference type and pointer and reference type as + iterator. + + A local_iterator object can be used to iterate through a single bucket. + + + + implementation-defined + A constant iterator with the same value type, difference type and pointer and reference type as + const_iterator. + + A const_local_iterator object can be used to iterate through a single bucket. + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. + + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + + unordered_multiset const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + + unordered_multiset const& + + unordered_multiset&The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + + + size_type + + std::distance(begin(), end()) + + + + + value_type const& + + iterator + + Inserts obj in the container. + + + + An iterator pointing to the inserted element. + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + + value_type const& + + iterator + + + + const_iterator + + + value_type const& + + const_iterator + + + Inserts obj in the container. + + hint is a suggestion to where the element should be inserted. + + + + An iterator pointing to the inserted element. + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. + + + + When inserting a single element, if an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + iterator + + + + const_iterator + + const_iterator + + + Erase the element pointed to by + position. + + + + The iterator following + position before the erasure. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + + + key_type const& + + size_type + + + Erase all elements with key equivalent to + k. + + + + The number of elements erased. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + + + + + + iterator + + + iterator + + iterator + + + + const_iterator + + + const_iterator + + const_iterator + + + Erases the elements in the range from + first to + last. + + + + The iterator following the erased elements - i.e. + last. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + void + + Erases all elements in the container. + + + + + size() == 0 + + + + Never throws an exception. + + + + + + unordered_multiset& + + void + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of + key_equal or + hasher. + + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to + k, or + b.end() if no such element exists. + + + + + + key_type const& + + size_type + + The number of elements with key equivalent to + k. + + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<iterator, iterator> + + + A range with containing all elements with key equivalent to + k. If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + + size_type + + An upper bound on the number of buckets. + + + + + + size_type + + size_type + + + n < bucket_count() + + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key + k. + + + + The return value is less than + bucket_count() + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the first element in the bucket with index + n. + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the 'one past the end' element in the bucket with index + n. + + + + + + + float + + The average number of elements per bucket. + + + + + float + + Returns the current maximum load factor. + + + + + + float + + float + + Changes the container's maximum load factor,using + z as a hint. + + + + + + size_type + + void + + Changes the number of buckets so that there at least + n buckets, and so that the load factor is less thanthe maximum load factor. + + Invalidates iterators, and changes the order of elements + + + + The function has no effect if an exception is throw, unless it is thrown by the container’s hash function or comparison function. + + + + + + + + + unordered_multiset<Key, T, Hash, Pred, Alloc>& + + + unordered_multiset<Key, T, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of + Hash or + Pred. + + + + +
+ + +
+ + + + + + + An unordered associative container that associates unique keys with another value. + + + For the normative reference see section 6.3 of + TR1 [n1836]. + + + + Template Parameters + + + + + + Key + + Key must be Assignable and CopyConstructible. + + + + + T + + T must be CopyConstructible + + + + + Hash + + A unary function object type that acts a hash function for a + Key. It takes a single argument of type + Key and returns a value of type std::size_t. + + + + + Pred + + A binary function object that implements an equivalence relation on values of type + Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + + + + + Alloc + + An allocator whose value type is the same as the container's value type. + + + + + + + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. + + The number of buckets is automatically increased whenever an insert will make the load factor greater than the maximum load factor. It can also change as result of calling rehash. + + When the number of buckets change: iterators are invalidated, the elements can change order, and move to different buckets, but pointers and references to elements remain valid. + + + + Key + + + std::pair<Key const, Value> + + + T + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + + lvalue of + value_type. + + + + + typename allocator_type::const_reference + + const lvalue of + value_type. + + + + + implementation-defined + unsigned integral type + + size_type can represent any non-negative value of + difference_type. + + + + + implementation-defined + signed integral type + Is identical to the difference type of + iterator and + const_iterator. + + + + + implementation-defined + + A iterator whose value type is + value_type. + + + Any iterator category except output iterator. + + Convertible to + const_iterator. + + + + + + implementation-defined + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + + + + + implementation-defined + An iterator with the same value type, difference type and pointer and reference type as + iterator. + + A local_iterator object can be used to iterate through a single bucket. + + + + implementation-defined + A constant iterator with the same value type, difference type and pointer and reference type as + const_iterator. + + A const_local_iterator object can be used to iterate through a single bucket. + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. + + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + + unordered_map const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + + unordered_map const& + + unordered_map&The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + + + size_type + + std::distance(begin(), end()) + + + + + value_type const& + + std::pair<iterator, bool> + + Inserts obj in the container if and only if there is no element in the container with an equivalent key. + + + + The bool component of the return type is true if an insert took place. + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + + value_type const& + + iterator + + + + const_iterator + + + value_type const& + + const_iterator + + + Inserts obj in the container if and only if there is no element in the container with an equivalent key. + + hint is a suggestion to where the element should be inserted. + + + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key. + + + + When inserting a single element, if an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + iterator + + + + const_iterator + + const_iterator + + + Erase the element pointed to by + position. + + + + The iterator following + position before the erasure. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + + + key_type const& + + size_type + + + Erase all elements with key equivalent to + k. + + + + The number of elements erased. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + + + + + + iterator + + + iterator + + iterator + + + + const_iterator + + + const_iterator + + const_iterator + + + Erases the elements in the range from + first to + last. + + + + The iterator following the erased elements - i.e. + last. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + void + + Erases all elements in the container. + + + + + size() == 0 + + + + Never throws an exception. + + + + + + unordered_map& + + void + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of + key_equal or + hasher. + + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to + k, or + b.end() if no such element exists. + + + + + + key_type const& + + size_type + + The number of elements with key equivalent to + k. + + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<iterator, iterator> + + + A range with containing all elements with key equivalent to + k. If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + + + + + + key_type const& + + mapped_type& + + If the container does not already contain an elements with a key equivalent to + k, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type()) + + + + A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to + k + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + size_type + + The number of buckets. + + + + + size_type + + An upper bound on the number of buckets. + + + + + + size_type + + size_type + + + n < bucket_count() + + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key + k. + + + + The return value is less than + bucket_count() + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the first element in the bucket with index + n. + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the 'one past the end' element in the bucket with index + n. + + + + + + + float + + The average number of elements per bucket. + + + + + float + + Returns the current maximum load factor. + + + + + + float + + float + + Changes the container's maximum load factor,using + z as a hint. + + + + + + size_type + + void + + Changes the number of buckets so that there at least + n buckets, and so that the load factor is less thanthe maximum load factor. + + Invalidates iterators, and changes the order of elements + + + + The function has no effect if an exception is throw, unless it is thrown by the container’s hash function or comparison function. + + + + + + + + + unordered_map<Key, T, Hash, Pred, Alloc>& + + + unordered_map<Key, T, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of + Hash or + Pred. + + + + + + + + + An unordered associative container that associates keys with another value. The same key can be stored multiple times. + + + For the normative reference see section 6.3 of + TR1 [n1836]. + + + + Template Parameters + + + + + + Key + + Key must be Assignable and CopyConstructible. + + + + + T + + T must be CopyConstructible + + + + + Hash + + A unary function object type that acts a hash function for a + Key. It takes a single argument of type + Key and returns a value of type std::size_t. + + + + + Pred + + A binary function object that implements an equivalence relation on values of type + Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + + + + + Alloc + + An allocator whose value type is the same as the container's value type. + + + + + + + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. + + The number of buckets is automatically increased whenever an insert will make the load factor greater than the maximum load factor. It can also change as result of calling rehash. + + When the number of buckets change: iterators are invalidated, the elements can change order, and move to different buckets, but pointers and references to elements remain valid. + + + + Key + + + std::pair<Key const, Value> + + + T + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + + lvalue of + value_type. + + + + + typename allocator_type::const_reference + + const lvalue of + value_type. + + + + + implementation-defined + unsigned integral type + + size_type can represent any non-negative value of + difference_type. + + + + + implementation-defined + signed integral type + Is identical to the difference type of + iterator and + const_iterator. + + + + + implementation-defined + + A iterator whose value type is + value_type. + + + Any iterator category except output iterator. + + Convertible to + const_iterator. + + + + + + implementation-defined + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + + + + + implementation-defined + An iterator with the same value type, difference type and pointer and reference type as + iterator. + + A local_iterator object can be used to iterate through a single bucket. + + + + implementation-defined + A constant iterator with the same value type, difference type and pointer and reference type as + const_iterator. + + A const_local_iterator object can be used to iterate through a single bucket. + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. + + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + + unordered_multimap const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + + unordered_multimap const& + + unordered_multimap&The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + + + size_type + + std::distance(begin(), end()) + + + + + value_type const& + + iterator + + Inserts obj in the container. + + + + An iterator pointing to the inserted element. + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + + value_type const& + + iterator + + + + const_iterator + + + value_type const& + + const_iterator + + + Inserts obj in the container. + + hint is a suggestion to where the element should be inserted. + + + + An iterator pointing to the inserted element. + + + + If an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. + + + + When inserting a single element, if an exception is thrown by an operation other than a call to + hasher the function has no effect. + + + + Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + iterator + + + + const_iterator + + const_iterator + + + Erase the element pointed to by + position. + + + + The iterator following + position before the erasure. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + + + key_type const& + + size_type + + + Erase all elements with key equivalent to + k. + + + + The number of elements erased. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + + + + + + iterator + + + iterator + + iterator + + + + const_iterator + + + const_iterator + + const_iterator + + + Erases the elements in the range from + first to + last. + + + + The iterator following the erased elements - i.e. + last. + + + + Only throws an exception, if it is thrown by a call to + hasher or + key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + void + + Erases all elements in the container. + + + + + size() == 0 + + + + Never throws an exception. + + + + + + unordered_multimap& + + void + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of + key_equal or + hasher. + + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to + k, or + b.end() if no such element exists. + + + + + + key_type const& + + size_type + + The number of elements with key equivalent to + k. + + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<iterator, iterator> + + + A range with containing all elements with key equivalent to + k. If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + + size_type + + An upper bound on the number of buckets. + + + + + + size_type + + size_type + + + n < bucket_count() + + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key + k. + + + + The return value is less than + bucket_count() + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the first element in the bucket with index + n. + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the 'one past the end' element in the bucket with index + n. + + + + + + + float + + The average number of elements per bucket. + + + + + float + + Returns the current maximum load factor. + + + + + + float + + float + + Changes the container's maximum load factor,using + z as a hint. + + + + + + size_type + + void + + Changes the number of buckets so that there at least + n buckets, and so that the load factor is less thanthe maximum load factor. + + Invalidates iterators, and changes the order of elements + + + + The function has no effect if an exception is throw, unless it is thrown by the container’s hash function or comparison function. + + + + + + + + + unordered_multimap<Key, T, Hash, Pred, Alloc>& + + + unordered_multimap<Key, T, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of + Hash or + Pred. + + + + +
+
+
diff --git a/doc/unordered.qbk b/doc/unordered.qbk new file mode 100644 index 00000000..5470b07d --- /dev/null +++ b/doc/unordered.qbk @@ -0,0 +1,18 @@ +[library Unordered Associative Containers + [quickbook 1.3] + [authors [Maitin-Shepard, Jeremy B.], [James, Daniel]] + [copyright 2005 Daniel James] + [purpose std::tr1 compliant hash containers] + [id unordered] + [dirname unordered] + [license + Distributed under 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] + ] +] + +[include:unordered intro.qbk] +[include:unordered comparison.qbk] + +[xinclude ref.xml] From a912bbd9aceef0b5fd28aaf818616f91a9b4a67a Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 26 Feb 2006 23:28:03 +0000 Subject: [PATCH 012/175] Remove the old unordered container tests. [SVN r2877] --- test/Jamfile.v2 | 63 ------ test/assign_tests.cpp | 86 -------- test/basic_tests.cpp | 207 ------------------ test/check_return_type.hpp | 36 ---- test/clear_tests.cpp | 32 --- test/concept_test.cpp | 32 --- test/const_iterator_fail_test.cpp | 16 -- test/const_local_iterator_fail_test.cpp | 18 -- test/construct_tests.cpp | 273 ------------------------ test/container_tests.cpp | 238 --------------------- test/containers.hpp | 76 ------- test/copy_construct_tests.cpp | 119 ----------- test/count_tests.cpp | 65 ------ test/equal_range_tests.cpp | 100 --------- test/equivalent.hpp | 112 ---------- test/erase_tests.cpp | 190 ----------------- test/find_tests.cpp | 78 ------- test/insert_tests.cpp | 193 ----------------- test/invariant.hpp | 128 ----------- test/iterator_tests.cpp | 141 ------------ test/map_operator_tests.cpp | 69 ------ test/max_load_factor_test.cpp | 170 --------------- test/next_prime_tests.cpp | 18 -- test/set_assign_fail_test.cpp | 14 -- test/strong.hpp | 102 --------- test/swap_tests.hpp | 120 ----------- test/swap_tests.ipp | 102 --------- test/swap_tests1.cpp | 7 - test/swap_tests2.cpp | 7 - test/swap_tests3.cpp | 7 - test/type_tests.cpp | 208 ------------------ 31 files changed, 3027 deletions(-) delete mode 100644 test/Jamfile.v2 delete mode 100644 test/assign_tests.cpp delete mode 100644 test/basic_tests.cpp delete mode 100644 test/check_return_type.hpp delete mode 100644 test/clear_tests.cpp delete mode 100644 test/concept_test.cpp delete mode 100644 test/const_iterator_fail_test.cpp delete mode 100644 test/const_local_iterator_fail_test.cpp delete mode 100644 test/construct_tests.cpp delete mode 100644 test/container_tests.cpp delete mode 100644 test/containers.hpp delete mode 100644 test/copy_construct_tests.cpp delete mode 100644 test/count_tests.cpp delete mode 100644 test/equal_range_tests.cpp delete mode 100644 test/equivalent.hpp delete mode 100644 test/erase_tests.cpp delete mode 100644 test/find_tests.cpp delete mode 100644 test/insert_tests.cpp delete mode 100644 test/invariant.hpp delete mode 100644 test/iterator_tests.cpp delete mode 100644 test/map_operator_tests.cpp delete mode 100644 test/max_load_factor_test.cpp delete mode 100644 test/next_prime_tests.cpp delete mode 100644 test/set_assign_fail_test.cpp delete mode 100644 test/strong.hpp delete mode 100644 test/swap_tests.hpp delete mode 100644 test/swap_tests.ipp delete mode 100644 test/swap_tests1.cpp delete mode 100644 test/swap_tests2.cpp delete mode 100644 test/swap_tests3.cpp delete mode 100644 test/type_tests.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 deleted file mode 100644 index 7704f38c..00000000 --- a/test/Jamfile.v2 +++ /dev/null @@ -1,63 +0,0 @@ - -# Copyright Daniel James 2005. Use, modification, and distribution are -# 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) - -import testing ; - -project unordered-test - : requirements - intel-linux:"-strict_ansi -cxxlib-icc" - # off - # TODO: Make this an option: - REDUCED_TESTS - ; - -lib helpers - : - [ glob helpers/*.cpp ] - ; - -framework = helpers/static /boost/test//boost_unit_test_framework ; - -test-suite helpers-test - : - [ run-fail helpers-test/simple_test.cpp $(framework) ] - [ run-fail helpers-test/allocator_test.cpp $(framework) ] - [ run helpers-test/exception_test.cpp $(framework) ] - ; - -test-suite compile-tests - : - [ compile-fail const_local_iterator_fail_test.cpp ] - [ compile-fail const_iterator_fail_test.cpp ] - [ compile-fail set_assign_fail_test.cpp ] - [ compile type_tests.cpp ] - [ run concept_test.cpp ] - ; - -test-suite basic-tests - : - [ run container_tests.cpp $(framework) ] - [ run iterator_tests.cpp $(framework) ] - [ run next_prime_tests.cpp $(framework) ] - [ run max_load_factor_test.cpp $(framework) ] - ; - -test-suite less-base-tests - : - [ run construct_tests.cpp $(framework) ] - [ run find_tests.cpp $(framework) ] - [ run count_tests.cpp $(framework) ] - [ run equal_range_tests.cpp $(framework) ] - [ run copy_construct_tests.cpp $(framework) ] - [ run swap_tests1.cpp $(framework) ] - [ run swap_tests2.cpp $(framework) ] - [ run swap_tests3.cpp $(framework) ] - [ run assign_tests.cpp $(framework) ] - [ run clear_tests.cpp $(framework) ] - [ run erase_tests.cpp $(framework) ] - [ run insert_tests.cpp $(framework) ] - [ run map_operator_tests.cpp $(framework) ] - ; - diff --git a/test/assign_tests.cpp b/test/assign_tests.cpp deleted file mode 100644 index 8d3e45ae..00000000 --- a/test/assign_tests.cpp +++ /dev/null @@ -1,86 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include "./helpers/unit_test.hpp" -#include "./helpers/exception_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/constructors.hpp" -#include "./helpers/constructors.hpp" -#include "./helpers/equivalent.hpp" -#include "./invariant.hpp" - -const int num_values = 50; - -META_FUNC_TEST_CASE(assign_test1, Container) -{ - test::constructors constructor; - - test::random_values values(num_values); - Container x(values.begin(), values.end(), 0, - constructor.hasher(55), constructor.key_equal(55), - constructor.allocator(10)); - x.max_load_factor(0.1); - - EXCEPTION_TEST(10000) - { - DEACTIVATE_EXCEPTIONS; - Container y; - INVARIANT_CHECK(y); - ACTIVATE_EXCEPTIONS; - - BOOST_CHECKPOINT("y = x"); - y = x; - - { - DEACTIVATE_EXCEPTIONS; - BOOST_CHECK_EQUAL(y.size(), x.size()); - BOOST_CHECK(test::equivalent(y.hash_function(), x.hash_function())); - BOOST_CHECK(test::equivalent(y.key_eq(), x.key_eq())); - BOOST_CHECK_EQUAL(y.max_load_factor(), x.max_load_factor()); - BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator())); - BOOST_CHECK(y.load_factor() <= y.max_load_factor()); - test::check_invariants(); - } - - BOOST_CHECKPOINT("y = y"); - y = y; - - { - DEACTIVATE_EXCEPTIONS; - BOOST_CHECK_EQUAL(y.size(), x.size()); - BOOST_CHECK(test::equivalent(y.hash_function(), x.hash_function())); - BOOST_CHECK(test::equivalent(y.key_eq(), x.key_eq())); - BOOST_CHECK_EQUAL(y.max_load_factor(), x.max_load_factor()); - BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator())); - BOOST_CHECK(y.load_factor() <= y.max_load_factor()); - test::check_invariants(); - } - - BOOST_CHECKPOINT("y = Container(values.begin(), values.end())"); - y = Container(values.begin(), values.end()); - - { - DEACTIVATE_EXCEPTIONS; - BOOST_CHECK_EQUAL(y.size(), x.size()); - BOOST_CHECK(test::equivalent(y.hash_function(), constructor.hasher())); - BOOST_CHECK(test::equivalent(y.key_eq(), constructor.key_equal())); - BOOST_CHECK_EQUAL(y.max_load_factor(), 1.0); - BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator())); - BOOST_CHECK(y.load_factor() <= 1.0); - test::check_invariants(); - } - } - EXCEPTION_TEST_END -} - -AUTO_META_TESTS( - (assign_test1), - CONTAINER_SEQ -) diff --git a/test/basic_tests.cpp b/test/basic_tests.cpp deleted file mode 100644 index 177edbcb..00000000 --- a/test/basic_tests.cpp +++ /dev/null @@ -1,207 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include -#include -#include -#include -#include -#include -#include "./helpers/unit_test.hpp" -#include "./helpers/random_values.hpp" - -typedef double comparison_type; - -template -struct check_return_type -{ - template - static void equals(T2) - { - BOOST_MPL_ASSERT((boost::is_same)); - } - - template - static void equals_ref(T2&) - { - BOOST_MPL_ASSERT((boost::is_same)); - } - - template - static void convertible(T2) - { - BOOST_MPL_ASSERT((boost::is_convertible)); - } -}; - -// 23.1.5 -template -void container_tests(X*, T*) -{ - typedef typename X::iterator iterator; - typedef typename X::const_iterator const_iterator; - typedef typename X::difference_type difference_type; - typedef typename X::size_type size_type; - - BOOST_MPL_ASSERT((boost::is_same)); - // TODO: Actually 'lvalue of T' - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT((boost::is_same)); - - // TODO: Iterator checks. - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); - BOOST_MPL_ASSERT((boost::is_convertible)); - - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); - - BOOST_MPL_ASSERT((boost::mpl::bool_::is_signed>)); - BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT((boost::is_same)); - - BOOST_MPL_ASSERT_NOT((boost::mpl::bool_::is_signed>)); - BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); - BOOST_CHECK((comparison_type)(std::numeric_limits::max)() - > (comparison_type)(std::numeric_limits::max)()); - - { - X u; - BOOST_CHECK(u.size() == 0); - } -} - -template -void container_tests2(X& a) -{ - typedef typename X::iterator iterator; - typedef typename X::const_iterator const_iterator; - typedef typename X::difference_type difference_type; - typedef typename X::size_type size_type; - - { - X u(a); - // BOOST_CHECK_EQUAL(a, u); - } - - { - X u = a; - // BOOST_CHECK_EQUAL(a, u); - } - - // Test that destructor destructs all elements - - { - X const a_const = a; - - check_return_type::equals(a.begin()); - check_return_type::equals(a_const.begin()); - check_return_type::equals(a.end()); - check_return_type::equals(a_const.end()); - } - - // No tests for ==, != since they're not required for unordered containers. - - { - X b; - a.swap(b); - a.swap(b); - } - - { - X u; - X& r = u; - - check_return_type::equals_ref(r = a); - } - - { - check_return_type::equals(a.size()); - BOOST_CHECK_EQUAL(a.size(), (size_type) std::distance(a.begin(), a.end())); - } - - { - check_return_type::equals(a.max_size()); - } - - { - check_return_type::convertible(a.empty()); - BOOST_CHECK_EQUAL(a.empty(), a.size() == 0); - } - - // TODO: member function size return number of elements. - // Semantics determined by constructors/inserts/erases. - // ie. will be tested in their tests. - - // begin() returns first element. - // end() return past the end iterator. - // Can't really test this for unordered containers. - - { - if(a.empty()) - BOOST_CHECK(a.begin() == a.end()); - } - - // TODO: test that const_iterator can replace iterator in comparisons. - - // TODO: test allocators - - // No need to test reversible. - - // Exceptions: - // No copy constructor or assignment operator of a returned iterator throws an exception. - - // No swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. - // Unless otherwise specified, iterators are not invalidated, and the values of objects aren't changed. -} - -BOOST_AUTO_UNIT_TEST(basic_tests) -{ - // I don't use the normal template mechanism here, as I want to specify the - // member type explicitly. - container_tests((boost::unordered_set*) 0, (int*) 0); - container_tests((boost::unordered_map*) 0, (std::pair*) 0); - container_tests((boost::unordered_multiset*) 0, (std::string*) 0); - container_tests((boost::unordered_multimap*) 0, - (std::pair*) 0); -} - -struct test_structure { int* x; }; - -META_FUNC_TEST_CASE(basic_tests_2, Container) -{ - - Container a; - container_tests2(a); - - { - test::random_values values1((std::min)(10u, a.max_size())); - Container b(values1.begin(), values1.end()); - container_tests2(b); - } - - { - test::random_values values2((std::min)(1000u, a.max_size())); - Container c(values2.begin(), values2.end()); - container_tests2(c); - } - - { - test::random_values values3((std::min)(100000u, a.max_size())); - Container d(values3.begin(), values3.end()); - container_tests2(d); - } -} - -AUTO_META_TESTS( - (basic_tests_2), - CONTAINER_SEQ -) diff --git a/test/check_return_type.hpp b/test/check_return_type.hpp deleted file mode 100644 index a201e75d..00000000 --- a/test/check_return_type.hpp +++ /dev/null @@ -1,36 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TESTS_CHECK_RETURN_TYPE_HEADER) -#define BOOST_UNORDERED_TESTS_CHECK_RETURN_TYPE_HEADER - -#include - -template -struct check_return_type -{ - template - static int equals(T2) - { - BOOST_MPL_ASSERT((boost::is_same)); - return 0; - } - - template - static int equals_ref(T2&) - { - BOOST_MPL_ASSERT((boost::is_same)); - return 0; - } - - template - static int convertible(T2) - { - BOOST_MPL_ASSERT((boost::is_convertible)); - return 0; - } -}; - -#endif diff --git a/test/clear_tests.cpp b/test/clear_tests.cpp deleted file mode 100644 index 558ed539..00000000 --- a/test/clear_tests.cpp +++ /dev/null @@ -1,32 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include "./helpers/unit_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/constructors.hpp" -#include "./invariant.hpp" - -META_FUNC_TEST_CASE(clear_test, Container) -{ - test::constructors constructor; - test::random_values values(100); - Container x(values.begin(), values.end(), 0, - constructor.hasher(55), constructor.key_equal(55), - constructor.allocator(10)); - - x.clear(); - BOOST_CHECK(x.empty()); - test::invariant_check(x); -} - -AUTO_META_TESTS( - (clear_test), - CONTAINER_SEQ -) diff --git a/test/concept_test.cpp b/test/concept_test.cpp deleted file mode 100644 index ddd98845..00000000 --- a/test/concept_test.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include - -#include -#include - -int main() -{ - using namespace boost; - - typedef boost::unordered_set UnorderedSet; - typedef boost::unordered_multiset UnorderedMultiSet; - typedef boost::unordered_map UnorderedMap; - typedef boost::unordered_multimap UnorderedMultiMap; - - function_requires< UnorderedAssociativeContainerConcept >(); - function_requires< SimpleAssociativeContainerConcept >(); - function_requires< UniqueAssociativeContainerConcept >(); - - function_requires< UnorderedAssociativeContainerConcept >(); - function_requires< SimpleAssociativeContainerConcept >(); - function_requires< MultipleAssociativeContainerConcept >(); - - function_requires< UnorderedAssociativeContainerConcept >(); - function_requires< UniqueAssociativeContainerConcept >(); - function_requires< PairAssociativeContainerConcept >(); - - function_requires< UnorderedAssociativeContainerConcept >(); - function_requires< MultipleAssociativeContainerConcept >(); - function_requires< PairAssociativeContainerConcept >(); - - return 0; -} diff --git a/test/const_iterator_fail_test.cpp b/test/const_iterator_fail_test.cpp deleted file mode 100644 index fcd7b82a..00000000 --- a/test/const_iterator_fail_test.cpp +++ /dev/null @@ -1,16 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include - -void func() -{ - typedef boost::unordered_map map; - typedef map::iterator iterator; - typedef map::const_iterator const_iterator; - - const_iterator x; - iterator y(x); -} diff --git a/test/const_local_iterator_fail_test.cpp b/test/const_local_iterator_fail_test.cpp deleted file mode 100644 index 348c9b5b..00000000 --- a/test/const_local_iterator_fail_test.cpp +++ /dev/null @@ -1,18 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include - -void func() -{ - // This is only required to fail for unordered maps & multimaps as for sets - // and multisets both iterator and const_iterator are const. - typedef boost::unordered_map map; - typedef map::local_iterator local_iterator; - typedef map::const_local_iterator const_local_iterator; - - const_local_iterator x; - local_iterator y(x); -} diff --git a/test/construct_tests.cpp b/test/construct_tests.cpp deleted file mode 100644 index 2c9cfb99..00000000 --- a/test/construct_tests.cpp +++ /dev/null @@ -1,273 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include "./helpers/unit_test.hpp" -#include "./helpers/exception_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/input_iterator_adaptor.hpp" -#include "./helpers/constructors.hpp" -#include "./helpers/equivalent.hpp" -#include "./invariant.hpp" - -META_FUNC_TEST_CASE(empty_construct_test1, Container) -{ - test::constructors constructor; - - EXCEPTION_TEST(1000) - { - // TR1 6.3.1/9 row 4 - Container x(100, constructor.hasher(55), constructor.key_equal(55)); - BOOST_CHECK(x.bucket_count() >= 100); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); - - // TODO: Where? - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(empty_construct_test2, Container) -{ - test::constructors constructor; - - EXCEPTION_TEST(1000) - { - // TR1 6.3.1/9 row 5 - - // I can only use the default hasher here - as it'll match the default - // key_equal. - Container x(100, constructor.hasher()); - BOOST_CHECK(x.empty()); - BOOST_CHECK(x.bucket_count() >= 100); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); - - // TODO: Where? - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(empty_construct_test3, Container) -{ - test::constructors constructor; - - EXCEPTION_TEST(1000) - { - // TR1 6.3.1/9 row 6 - - Container x(200); - BOOST_CHECK(x.empty()); - BOOST_CHECK(x.bucket_count() >= 200); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); - - // TODO: Where? - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(empty_construct_test4, Container) -{ - test::constructors constructor; - - EXCEPTION_TEST(1000) - { - // TR1 6.3.1/9 row 7 - - Container x; - BOOST_CHECK(x.empty()); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); - - // TODO: Where? - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(empty_construct_test5, Container) -{ - test::constructors constructor; - - EXCEPTION_TEST(1000) - { - // TODO: Where? - Container x(100, constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); - BOOST_CHECK(x.empty()); - BOOST_CHECK(x.bucket_count() >= 100); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(range_construct_test1, Container) -{ - test::constructors constructor; - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - // TR1 6.3.1/9 row 8 - - Container x(values.begin(), values.end(), 100, - constructor.hasher(55), constructor.key_equal(55)); - BOOST_CHECK(x.bucket_count() >= 100); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); - // TODO: Check that values are in container. - - // TODO: Where? - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(range_construct_test2, Container) -{ - test::constructors constructor; - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - // TR1 6.3.1/9 row 9 - - Container x(values.begin(), values.end(), 100, constructor.hasher()); - BOOST_CHECK(x.bucket_count() >= 100); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); - // TODO: Check that values are in container. - - // TODO: Where? - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(range_construct_test3, Container) -{ - test::constructors constructor; - test::random_values values(20); - - EXCEPTION_TEST(1000) - { - // TR1 6.3.1/9 row 10 - - Container x(values.begin(), values.end(), 10); - BOOST_CHECK(x.bucket_count() >= 10); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); - // TODO: Check that values are in container. - - // TODO: Where? - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(range_construct_test4, Container) -{ - test::constructors constructor; - test::random_values values(20); - - EXCEPTION_TEST(1000) - { - // TR1 6.3.1/9 row 11 - - Container x(values.begin(), values.end()); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher())); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal())); - // TODO: Check that values are in container. - - // TODO: Where? - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator())); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(range_construct_test5, Container) -{ - test::constructors constructor; - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - // TODO: Where? - Container x(values.begin(), values.end(), 10, - constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); - BOOST_CHECK(x.bucket_count() >= 10); - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END -} - -// TODO: I should probably just make all the tests run from an input iterator. -META_FUNC_TEST_CASE(input_iterator_construct_test1, Container) -{ - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - Container x( - test::make_input_iterator(values.begin()), - test::make_input_iterator(values.end()) - ); - BOOST_CHECK_EQUAL(x.max_load_factor(), 1.0); - - test::invariant_check(x); - } - EXCEPTION_TEST_END - -} - -AUTO_META_TESTS( - (empty_construct_test1)(empty_construct_test2)(empty_construct_test3) - (empty_construct_test4)(empty_construct_test5) - (range_construct_test1)(range_construct_test2)(range_construct_test3) - (range_construct_test4)(range_construct_test5) - (input_iterator_construct_test1), - CONTAINER_SEQ -) diff --git a/test/container_tests.cpp b/test/container_tests.cpp deleted file mode 100644 index dd4fd8f1..00000000 --- a/test/container_tests.cpp +++ /dev/null @@ -1,238 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include -#include -#include -#include -#include -#include -#include "./helpers/unit_test.hpp" -#include "./helpers/random_values.hpp" -#include "./equivalent.hpp" -#include "./check_return_type.hpp" - -typedef double comparison_type; - -// 23.1/5 -template -void container_tests(X*, T*) -{ - typedef typename X::iterator iterator; - typedef typename X::const_iterator const_iterator; - typedef typename X::difference_type difference_type; - typedef typename X::size_type size_type; - - typedef typename iterator::value_type iterator_value_type; - typedef typename const_iterator::value_type const_iterator_value_type; - typedef typename iterator::difference_type iterator_difference_type; - typedef typename const_iterator::difference_type const_iterator_difference_type; - - typedef typename X::value_type value_type; - typedef typename X::reference reference; - typedef typename X::const_reference const_reference; - - BOOST_MPL_ASSERT((boost::is_same)); - // TODO: Actually 'lvalue of T'/'const lvalue of T' - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT((boost::is_same)); - - // TODO: Iterator checks. - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); - BOOST_MPL_ASSERT((boost::is_convertible)); - - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT_NOT((boost::is_same, std::output_iterator_tag>)); - - BOOST_MPL_ASSERT((boost::mpl::bool_::is_signed>)); - BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); - BOOST_MPL_ASSERT((boost::is_same)); - BOOST_MPL_ASSERT((boost::is_same)); - - BOOST_MPL_ASSERT_NOT((boost::mpl::bool_::is_signed>)); - BOOST_MPL_ASSERT((boost::mpl::bool_::is_integer>)); - BOOST_CHECK((comparison_type)(std::numeric_limits::max)() - > (comparison_type)(std::numeric_limits::max)()); - - { - X u; - BOOST_CHECK(u.size() == 0); - BOOST_CHECK(X().size() == 0); - } -} - -template -void container_tests2(X& a) -{ - // 23.1/5 continued - typedef typename X::iterator iterator; - typedef typename X::const_iterator const_iterator; - typedef typename X::difference_type difference_type; - typedef typename X::size_type size_type; - typedef typename X::allocator_type allocator_type; - - test::unordered_equivalence_tester equivalent(a); - - { - X u(a); - equivalent.test(u); - } - - { - X u = a; - equivalent.test(u); - } - - // Test that destructor destructs all elements (already done by test::allocator/test::member?). - - { - X const& a_const = a; - - check_return_type::equals(a.begin()); - check_return_type::equals(a_const.begin()); - check_return_type::equals(a.end()); - check_return_type::equals(a_const.end()); - } - - // No tests for ==, != since they're not required for unordered containers. - - { - X b; - a.swap(b); - BOOST_CHECK(a.empty()); - equivalent.test(b); - a.swap(b); - equivalent.test(a); - BOOST_CHECK(b.empty()); - } - - { - X u; - X& r = u; - - check_return_type::equals_ref(r = a); - equivalent.test(r); - } - - { - check_return_type::equals(a.size()); - BOOST_CHECK_EQUAL(a.size(), (size_type) std::distance(a.begin(), a.end())); - } - - { - check_return_type::equals(a.max_size()); - // TODO: Check that a.max_size() == size of the largest possible container - // How do I do that? test::allocator checks that allocations don't exceed - // that allocator's maximum size. Could check that max_size() works, and - // that max_size() + 1 doesn't. Only practicle for small max_size though - - // and it might be possible to implement unordered containers such that - // max_size() > alloc.max_size(). Or is it? - } - - { - check_return_type::convertible(a.empty()); - BOOST_CHECK_EQUAL(a.empty(), a.size() == 0); - } - - // 23.1/7 - { - if(a.empty()) - BOOST_CHECK(a.begin() == a.end()); - } - - // 23.1/8 - { - iterator i = a.begin(), j = a.end(); - const_iterator ci = a.begin(), cj = a.end(); - - if(a.empty()) { - BOOST_CHECK(i == j); - BOOST_CHECK(i == cj); - BOOST_CHECK(ci == j); - BOOST_CHECK(ci == cj); - - BOOST_CHECK(!(i != j)); - BOOST_CHECK(!(i != cj)); - BOOST_CHECK(!(ci != j)); - BOOST_CHECK(!(ci != cj)); - } - else { - BOOST_CHECK(!(i == j)); - BOOST_CHECK(!(i == cj)); - BOOST_CHECK(!(ci == j)); - BOOST_CHECK(!(ci == cj)); - - BOOST_CHECK(i != j); - BOOST_CHECK(i != cj); - BOOST_CHECK(ci != j); - BOOST_CHECK(ci != cj); - } - } - - // TODO: 23.1/9 - Make sure this is checked for all constructors. - { - check_return_type::equals(a.get_allocator()); - } - - // TODO: 23.1/11 - Exception safety: - // No erase function throws an exception. - // No copy constructor or assignment operator of a returned iterator throws an exception. - - // No swap() function throws an exception unless that exception is thrown by the copy constructor of assignment operator of the container's Compare object. - // No swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. - // - // TODO: 21.1/12 - // - // Unless otherwise specified - iterators not invalidated, values not changed. -} - -BOOST_AUTO_UNIT_TEST(basic_tests) -{ - // I don't use the normal template mechanism here, as I want to specify the - // member type explicitly. - container_tests((boost::unordered_set*) 0, (int*) 0); - container_tests((boost::unordered_map*) 0, (std::pair*) 0); - container_tests((boost::unordered_multiset*) 0, (std::string*) 0); - container_tests((boost::unordered_multimap*) 0, - (std::pair*) 0); -} - -struct test_structure { int* x; }; - -META_FUNC_TEST_CASE(basic_tests_2, Container) -{ - - Container a; - container_tests2(a); - - { - test::random_values values1((std::min)(10u, a.max_size())); - Container b(values1.begin(), values1.end()); - container_tests2(b); - } - - { - test::random_values values2((std::min)(1000u, a.max_size())); - Container c(values2.begin(), values2.end()); - container_tests2(c); - } - - { - test::random_values values3((std::min)(100000u, a.max_size())); - Container d(values3.begin(), values3.end()); - container_tests2(d); - } -} - -AUTO_META_TESTS( - (basic_tests_2), - CONTAINER_SEQ -) diff --git a/test/containers.hpp b/test/containers.hpp deleted file mode 100644 index 674fe743..00000000 --- a/test/containers.hpp +++ /dev/null @@ -1,76 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TESTS_CONTAINERS_HEADER) -#define BOOST_UNORDERED_TESTS_CONTAINERS_HEADER - -#include -#include -#include -#include -#include "./helpers/allocator.hpp" -#include "./helpers/functional.hpp" -#include "./helpers/member.hpp" - -typedef boost::unordered_set< - test::member - > test_set; -typedef boost::unordered_multiset< - test::member, test::hash, test::equals, - test::allocator - > test_multiset; -typedef boost::unordered_map< - test::member, test::member, test::hash, test::equals, - test::allocator > - > test_map; -typedef boost::unordered_multimap< - test::member, test::member, test::hash, test::equals, - test::minimal_allocator > - > test_multimap; - -typedef boost::unordered_set< - int, test::hash, test::equals, - test::allocator - > set_int; -typedef boost::unordered_multiset< - std::string, test::hash, test::equals, - test::allocator - > multiset_string; -typedef boost::unordered_map< - test::member, std::string, test::hash, test::equals, - test::allocator > - > map_member_string; -typedef boost::unordered_multimap< - int, test::member, test::hash, test::equals, - test::allocator > - > multimap_int_member; -typedef boost::unordered_map< - char, test::member, test::hash, test::equals, - test::allocator > - > map_char_member; -typedef boost::unordered_multiset< - char, test::hash, test::equals, - test::allocator - > multiset_char; - -typedef std::pair pair1; -typedef std::pair pair2; -typedef std::pair pair3; -BOOST_TT_BROKEN_COMPILER_SPEC(std::string) -BOOST_TT_BROKEN_COMPILER_SPEC(pair1) -BOOST_TT_BROKEN_COMPILER_SPEC(pair2) -BOOST_TT_BROKEN_COMPILER_SPEC(pair3) -BOOST_TT_BROKEN_COMPILER_SPEC(test::member) - -#ifdef REDUCED_TESTS -#define CONTAINER_SEQ \ - (test_set)(test_multiset)(test_map)(test_multimap) -#else -#define CONTAINER_SEQ \ - (set_int)(multiset_string)(map_member_string) \ - (multimap_int_member)(map_char_member)(multiset_char) -#endif - -#endif diff --git a/test/copy_construct_tests.cpp b/test/copy_construct_tests.cpp deleted file mode 100644 index 2bedd4e8..00000000 --- a/test/copy_construct_tests.cpp +++ /dev/null @@ -1,119 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include "./helpers/unit_test.hpp" -#include "./helpers/exception_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/constructors.hpp" -#include "./equivalent.hpp" -#include "./invariant.hpp" - -// 23.1/5 + TR1 6.3.1/9 row 12 -META_FUNC_TEST_CASE(empty_copy_test1, X) -{ - X x; - test::unordered_equivalence_tester equivalent(x); - - EXCEPTION_TEST(1000) - { - X y(x); - equivalent.test(y); - test::invariant_check(y); - } - EXCEPTION_TEST_END - - test::invariant_check(x); -} - -META_FUNC_TEST_CASE(empty_copy_test2, X) -{ - test::constructors constructor; - X x(100, constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); - x.max_load_factor(4.0); - test::unordered_equivalence_tester equivalent(x); - - EXCEPTION_TEST(1000) - { - X y(x); - equivalent.test(y); - test::invariant_check(y); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(range_copy_construct,X) -{ - test::constructors constructor; - test::random_values values(10); - - X x(values.begin(), values.end(), 100, - constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); - x.max_load_factor(4.0); - test::unordered_equivalence_tester equivalent(x); - - EXCEPTION_TEST(1000) - { - X y(x); - equivalent.test(y); - test::invariant_check(y); - } - EXCEPTION_TEST_END -} - -template -void check_container(X const& x, test::unordered_equivalence_tester const& equivalent) -{ - equivalent.test(x); - test::invariant_check(x); -} - -META_FUNC_TEST_CASE(anon_copy_construct, X) -{ - test::constructors constructor; - test::random_values values(10); - - X x(values.begin(), values.end(), 100, - constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); - x.max_load_factor(4.0); - test::unordered_equivalence_tester equivalent(x); - - EXCEPTION_TEST(1000) - { - check_container(X(x), equivalent); - } - EXCEPTION_TEST_END -} - -template -X return_container(X const& x) -{ - return x; -} - -META_FUNC_TEST_CASE(copy_from_return,X) -{ - test::random_values values(10); - X x(values.begin(), values.end()); - test::unordered_equivalence_tester equivalent(x); - - EXCEPTION_TEST(1000) - { - X y(return_container(x)); - equivalent.test(y); - test::invariant_check(y); - } - EXCEPTION_TEST_END -} - -AUTO_META_TESTS( - (empty_copy_test1)(empty_copy_test2)(range_copy_construct) - (anon_copy_construct)(copy_from_return), - CONTAINER_SEQ -) diff --git a/test/count_tests.cpp b/test/count_tests.cpp deleted file mode 100644 index b900556c..00000000 --- a/test/count_tests.cpp +++ /dev/null @@ -1,65 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include "./helpers/unit_test.hpp" -#include "./helpers/random_values.hpp" - -META_FUNC_TEST_CASE(count_const_test, Container) -{ - test::random_values values(500); - typedef typename test::random_values::iterator iterator; - - Container const x(values.begin(), values.end()); - - for(iterator it = values.begin(); it != values.end(); ++it) - { - BOOST_CHECK_EQUAL(x.count(values.get_key(*it)), values.key_count(*it)); - } - - typedef typename test::random_values::value_type value_type; - test::generator generator; - - for(int i = 0; i < 500; ++i) - { - value_type value = generator(); - BOOST_CHECK_EQUAL(x.count(values.get_key(value)), values.key_count(value)); - } -} - -META_FUNC_TEST_CASE(count_nonconst_test, Container) -{ - test::random_values values(500); - typedef typename test::random_values::iterator iterator; - - Container x(values.begin(), values.end()); - - for(iterator it = values.begin(); it != values.end(); ++it) - { - BOOST_CHECK_EQUAL(x.count(values.get_key(*it)), values.key_count(*it)); - } -} - -META_FUNC_TEST_CASE(empty_test, Container) -{ - typedef test::random_values random_values; - typedef typename random_values::value_type value_type; - test::generator generator; - Container x; - - for(int i = 0; i < 500; ++i) - { - BOOST_CHECK_EQUAL(x.count(random_values::get_key(generator())), 0u); - } -} - -AUTO_META_TESTS( - (count_const_test)(count_nonconst_test)(empty_test), - CONTAINER_SEQ -) diff --git a/test/equal_range_tests.cpp b/test/equal_range_tests.cpp deleted file mode 100644 index 0f1fedbf..00000000 --- a/test/equal_range_tests.cpp +++ /dev/null @@ -1,100 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include "./helpers/unit_test.hpp" -#include "./helpers/random_values.hpp" - -template -OutputIt copy_if(InputIt begin, InputIt end, OutputIt out, Condition cond) -{ - for(;begin != end; ++begin) - { - if(cond(*begin)) { - *out = *begin; - ++out; - } - } - - return out; -} - -template -void compare(std::pair const& range, - RandomValues const& values, Value const& v) -{ - typedef typename RandomValues::value_type value_type; - typedef std::vector value_container; - value_container range_values(range.first, range.second); - value_container orig_values; - copy_if(values.begin(), values.end(), std::back_inserter(orig_values), - values.key_match(v)); - - if(values.is_unique()) { - if(orig_values.empty()) { - BOOST_CHECK_EQUAL(range_values.size(), 0u); - } - else { - BOOST_CHECK_EQUAL(range_values.size(), 1u); - BOOST_CHECK(orig_values.front() == *range_values.begin()); - } - } - else { - std::sort(range_values.begin(), range_values.end()); - std::sort(orig_values.begin(), orig_values.end()); - BOOST_CHECK_EQUAL(range_values.size(), orig_values.size()); - if(range_values.size() == orig_values.size()) - BOOST_CHECK(std::equal(range_values.begin(), range_values.end(), - orig_values.begin())); - } -} - -META_FUNC_TEST_CASE(const_test, Container) -{ - test::random_values values(500); - typedef test::random_values random_values; - typedef typename random_values::iterator iterator; - - Container const x(values.begin(), values.end()); - - for(iterator it = values.begin(); it != values.end(); ++it) - { - compare(x.equal_range(values.get_key(*it)), - values, values.get_key(*it)); - } - - typedef typename random_values::value_type value_type; - test::generator generator; - - for(int i = 0; i < 500; ++i) - { - value_type v = generator(); - compare(x.equal_range(values.get_key(v)), - values, values.get_key(v)); - } -} - -META_FUNC_TEST_CASE(nonconst_test, Container) -{ - test::random_values values(500); - typedef typename test::random_values::iterator iterator; - - Container const x(values.begin(), values.end()); - - for(iterator it = values.begin(); it != values.end(); ++it) - { - compare(x.equal_range(values.get_key(*it)), - values, values.get_key(*it)); - } -} - -AUTO_META_TESTS( - (const_test)(nonconst_test), - CONTAINER_SEQ -) diff --git a/test/equivalent.hpp b/test/equivalent.hpp deleted file mode 100644 index ce8808a3..00000000 --- a/test/equivalent.hpp +++ /dev/null @@ -1,112 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER) -#define BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER - -#include "./helpers/strong.hpp" -#include "./helpers/equivalent.hpp" -#include "./helpers/metafunctions.hpp" -#include "./helpers/less.hpp" -#include -#include -#include -#include - -namespace test -{ - struct equals2_t - { - template - bool operator()(X const& x, Y const& y) - { - return x == y; - } - - template - bool operator()(std::pair const& x, std::pair const& y) - { - return x.first == y.first && x.second == y.second; - } - } equals2; - - template - class unordered_equivalence_tester - { - typename Container::size_type size_; - typename Container::hasher hasher_; - typename Container::key_equal key_equal_; - float max_load_factor_; - std::vector::type> values_; - public: - unordered_equivalence_tester(Container const &x) - : size_(x.size()), - hasher_(x.hash_function()), key_equal_(x.key_eq()), - max_load_factor_(x.max_load_factor()), - values_(x.begin(), x.end()) - { - std::sort(values_.begin(), values_.end(), - test::compare()); - } - - void test(Container const& x) const - { - BOOST_CHECK(size_ == x.size()); - BOOST_CHECK(test::equivalent(hasher_, x.hash_function())); - BOOST_CHECK(test::equivalent(key_equal_, x.key_eq())); - BOOST_CHECK(max_load_factor_ == x.max_load_factor()); - BOOST_CHECK(values_.size() == x.size()); - if(values_.size() == x.size()) { - std::vector::type> - copy(x.begin(), x.end()); - std::sort(copy.begin(), copy.end(), test::compare()); - BOOST_CHECK( - std::equal(values_.begin(), values_.end(), copy.begin(), - equals2)); - } - } - private: - unordered_equivalence_tester(); - }; -} - -#if 0 -namespace boost -{ - template - test::strong_tester_ptr create_tester_impl( - boost::unordered_set const& x, int) - { - return test::strong_tester_ptr(new test::unordered_strong_tester< - boost::unordered_set >(x)); - } - - template - test::strong_tester_ptr create_tester_impl( - boost::unordered_multiset const& x, int) - { - return test::strong_tester_ptr(new test::unordered_strong_tester< - boost::unordered_multiset >(x)); - } - - template - test::strong_tester_ptr create_tester_impl( - boost::unordered_map const& x, int) - { - return test::strong_tester_ptr(new test::unordered_strong_tester< - boost::unordered_map >(x)); - } - - template - test::strong_tester_ptr create_tester_impl( - boost::unordered_multimap const& x, int) - { - return test::strong_tester_ptr(new test::unordered_strong_tester< - boost::unordered_multimap >(x)); - } -} -#endif - -#endif diff --git a/test/erase_tests.cpp b/test/erase_tests.cpp deleted file mode 100644 index 306ef5e9..00000000 --- a/test/erase_tests.cpp +++ /dev/null @@ -1,190 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include -#include "./helpers/unit_test.hpp" -#include "./helpers/exception_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/constructors.hpp" -#include "./invariant.hpp" - -META_FUNC_TEST_CASE(range_erase_test,Container) -{ - test::constructors constructor; - - test::random_values values(100); - typedef typename Container::iterator iterator; - - { - Container x(values.begin(), values.end(), 0, - constructor.hasher(55), - constructor.key_equal(55), - constructor.allocator(10)); - const std::size_t size = x.size(); - INVARIANT_CHECK(x); - iterator pos; - - // Should be no throw. - ACTIVATE_EXCEPTIONS; - - BOOST_CHECKPOINT("Erase nothing from the beginning"); - BOOST_CHECK(x.begin() == x.erase(x.begin(), x.begin())); - BOOST_CHECK_EQUAL(x.size(), size); - test::check_invariants(); - - BOOST_CHECKPOINT("Erase nothing from the end"); - BOOST_CHECK(x.end() == x.erase(x.end(), x.end())); - BOOST_CHECK_EQUAL(x.size(), size); - test::check_invariants(); - - BOOST_CHECKPOINT("Erase nothing from the middle"); - BOOST_CHECK(boost::next(x.begin(), 4) == x.erase( - boost::next(x.begin(), 4), - boost::next(x.begin(), 4))); - BOOST_CHECK_EQUAL(x.size(), size); - test::check_invariants(); - - BOOST_CHECKPOINT("Erase 3 from the middle"); - pos = x.erase(boost::next(x.begin(), 1), boost::next(x.begin(), 4)); - BOOST_CHECK(boost::next(x.begin(), 1) == pos); - BOOST_CHECK_EQUAL(x.size(), size - 3); - test::check_invariants(); - - BOOST_CHECKPOINT("Erase all but the first 1"); - pos = x.erase(boost::next(x.begin(), 1), x.end()); - BOOST_CHECK(x.end() == pos); - BOOST_CHECK_EQUAL(x.size(), 1u); - test::check_invariants(); - } - - { - Container x(values.begin(), values.end()); - const std::size_t size = x.size(); - INVARIANT_CHECK(x); - iterator pos; - - // Should be no throw. - ACTIVATE_EXCEPTIONS; - - BOOST_CHECKPOINT("Erase first 2"); - pos = x.erase(x.begin(), boost::next(x.begin(), 2)); - BOOST_CHECK(x.begin() == pos); - BOOST_CHECK_EQUAL(x.size(), size - 2); - test::check_invariants(); - } - - { - Container x(values.begin(), values.end()); - INVARIANT_CHECK(x); - iterator pos; - - // Should be no throw. - ACTIVATE_EXCEPTIONS; - - BOOST_CHECKPOINT("Erase all"); - pos = x.erase(x.begin(), x.end()); - BOOST_CHECK(x.begin() == pos && x.end() == pos); - BOOST_CHECK(x.empty()); - test::check_invariants(); - } -} - -META_FUNC_TEST_CASE(erase_by_key_test,Container) -{ - test::constructors constructor; - test::sorted_random_values values(10); - - // Exceptions only from the hash function. - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - Container x(values.begin(), values.end(), 0, - constructor.hasher(55), - constructor.key_equal(55), - constructor.allocator(10)); - INVARIANT_CHECK(x); - - for(int i = 0; i < 10; i += values.count(values[i])) { - std::size_t key_count = values.key_count(values[i]); - { - ACTIVATE_EXCEPTIONS; - BOOST_CHECK_EQUAL(key_count, - x.erase(values.get_key(values[i]))); - } - BOOST_CHECK(x.find(values.get_key(values[i])) == x.end()); - BOOST_CHECK_EQUAL(0u, x.erase(values.get_key(values[i]))); - } - - BOOST_CHECK(x.empty()); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(erase_subrange_test,Container) -{ - test::random_values values(100); - Container x(values.begin(), values.end()); - - // Should be no throw. - ACTIVATE_EXCEPTIONS; - - typedef typename Container::const_iterator const_iterator; - typedef typename Container::iterator iterator; - - std::size_t length = x.size(); - std::size_t begin_index = length / 2; - std::size_t end_index = (length + begin_index) / 2; - std::size_t sub_begin_index = (end_index - begin_index) / 4; - std::size_t sub_end_index = sub_begin_index * 3; - - const_iterator begin = boost::next(x.begin(), begin_index); - const_iterator end = boost::next(x.begin(), end_index); - - iterator pos = x.erase(boost::next(begin, sub_begin_index), - boost::next(begin, sub_end_index)); - - BOOST_CHECK(pos == boost::next(x.begin(), begin_index + sub_begin_index)); - BOOST_CHECK(pos == boost::next(begin, sub_begin_index)); - BOOST_CHECK_EQUAL( - (end_index - begin_index) - (sub_end_index - sub_begin_index), - static_cast(std::distance(begin, end))); - - test::invariant_check(x); -} - -META_FUNC_TEST_CASE(erase_by_iterator_test,Container) -{ - test::random_values values(100); - Container x(values.begin(), values.end()); - INVARIANT_CHECK(x); - std::size_t size = x.size(); - - typedef typename Container::iterator iterator; - - // Should be no throw. - ACTIVATE_EXCEPTIONS; - - while(!x.empty()) { - using namespace std; - int index = rand() % x.size(); - iterator pos = x.erase(boost::next(x.begin(), index)); - --size; - BOOST_CHECK_EQUAL(size, x.size()); - - BOOST_CHECK(boost::next(x.begin(), index) == pos); - test::check_invariants(); - } -} - -AUTO_META_TESTS( - (range_erase_test)(erase_by_key_test)(erase_subrange_test) - (erase_by_iterator_test), - CONTAINER_SEQ -) diff --git a/test/find_tests.cpp b/test/find_tests.cpp deleted file mode 100644 index cab61bd9..00000000 --- a/test/find_tests.cpp +++ /dev/null @@ -1,78 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include "./helpers/unit_test.hpp" -#include "./helpers/random_values.hpp" - -META_FUNC_TEST_CASE(find_const_test,Container) -{ - test::random_values values(500); - typedef typename test::random_values::iterator iterator; - - Container const x(values.begin(), values.end()); - - for(iterator it = values.begin(); it != values.end(); ++it) - { - typename Container::const_iterator pos = x.find(values.get_key(*it)); - BOOST_CHECK(pos != x.end()); - BOOST_CHECK(values.get_key(*pos) == values.get_key(*it)); - } -} - -META_FUNC_TEST_CASE(find_nonconst_test,Container) -{ - test::random_values values(500); - typedef typename test::random_values::iterator iterator; - - Container x(values.begin(), values.end()); - - for(iterator it = values.begin(); it != values.end(); ++it) - { - typename Container::iterator pos = x.find(values.get_key(*it)); - BOOST_CHECK(pos != x.end()); - BOOST_CHECK(values.get_key(*pos) == values.get_key(*it)); - } -} - -META_FUNC_TEST_CASE(missing_test,Container) -{ - test::random_values values(10); - Container x(values.begin(), values.end()); - - typedef typename test::random_values::value_type value_type; - test::generator generator; - - for(int i = 0; i < 500; ++i) - { - value_type const value = generator(); - bool const present_in_values = values.find(value) != values.end(); - bool const present_in_container = x.find(values.get_key(value)) - != x.end(); - BOOST_CHECK(present_in_values == present_in_container); - } -} - -META_FUNC_TEST_CASE(empty_test,Container) -{ - typedef test::random_values random_values; - typedef typename random_values::value_type value_type; - test::generator generator; - Container x; - - for(int i = 0; i < 500; ++i) - { - BOOST_CHECK(x.find(random_values::get_key(generator())) == x.end()); - } -} - -AUTO_META_TESTS( - (find_const_test)(find_nonconst_test)(missing_test)(empty_test), - CONTAINER_SEQ -) diff --git a/test/insert_tests.cpp b/test/insert_tests.cpp deleted file mode 100644 index 8166349e..00000000 --- a/test/insert_tests.cpp +++ /dev/null @@ -1,193 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include -#include "./helpers/unit_test.hpp" -#include "./helpers/exception_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/input_iterator_adaptor.hpp" -#include "./strong.hpp" -#include "./invariant.hpp" - -META_FUNC_TEST_CASE(insert_individual,Container) -{ - test::random_values values(100); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - Container x; - - INVARIANT_CHECK(x); - - ACTIVATE_EXCEPTIONS; - - for(typename test::random_values::iterator - it = values.begin(); it != values.end(); ++it) - { - STRONG_TEST(tester, x) { - try { - x.insert(*it); - } catch(test::hash_exception) { - tester.dismiss(); - throw; - } - } - - DEACTIVATE_EXCEPTIONS; - typename Container::iterator pos = x.find(values.get_key(*it)); - BOOST_CHECK(pos != x.end() && - x.key_eq()(values.get_key(*pos), values.get_key(*it))); - } - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(insert_with_previous_item_hint,Container) -{ - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - - Container x; - INVARIANT_CHECK(x); - - ACTIVATE_EXCEPTIONS; - - typename Container::const_iterator prev = x.begin(); - - for(typename test::random_values::iterator - it = values.begin(); it != values.end(); ++it) - { - x.insert(prev, *it); - - typename Container::iterator pos = x.find(values.get_key(*it)); - BOOST_CHECK(pos != x.end() && - x.key_eq()(values.get_key(*pos), values.get_key(*it))); - prev = pos; - } - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(insert_with_begin_hint,Container) -{ - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - Container x; - INVARIANT_CHECK(x); - ACTIVATE_EXCEPTIONS; - - for(typename test::random_values::iterator - it = values.begin(); it != values.end(); ++it) - { - x.insert(x.begin(), *it); - - typename Container::iterator pos = x.find(values.get_key(*it)); - BOOST_CHECK(pos != x.end() && - x.key_eq()(values.get_key(*pos), values.get_key(*it))); - } - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(insert_with_end_hint,Container) -{ - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - Container x; - INVARIANT_CHECK(x); - ACTIVATE_EXCEPTIONS; - - for(typename test::random_values::iterator - it = values.begin(); it != values.end(); ++it) - { - x.insert(x.end(), *it); - - typename Container::iterator pos = x.find(values.get_key(*it)); - BOOST_CHECK(pos != x.end() && - x.key_eq()(values.get_key(*pos), values.get_key(*it))); - } - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(insert_with_random_hint,Container) -{ - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - Container x; - INVARIANT_CHECK(x); - ACTIVATE_EXCEPTIONS; - - for(typename test::random_values::iterator - it = values.begin(); it != values.end(); ++it) - { - using namespace std; - x.insert(boost::next(x.begin(), rand() % (x.size() + 1)), *it); - - typename Container::iterator pos = x.find(values.get_key(*it)); - BOOST_CHECK(pos != x.end() && - x.key_eq()(values.get_key(*pos), values.get_key(*it))); - } - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(insert_range,Container) -{ - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - Container x; - INVARIANT_CHECK(x); - ACTIVATE_EXCEPTIONS; - - x.insert(values.begin(), values.end()); - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(insert_range_input_iterator,Container) -{ - test::random_values values(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - Container x; - INVARIANT_CHECK(x); - ACTIVATE_EXCEPTIONS; - - x.insert(test::make_input_iterator(values.begin()), - test::make_input_iterator(values.end())); - } - EXCEPTION_TEST_END -} - -AUTO_META_TESTS( - (insert_individual)(insert_with_previous_item_hint)(insert_with_begin_hint) - (insert_with_end_hint)(insert_with_random_hint)(insert_range) - (insert_range_input_iterator), - CONTAINER_SEQ -) diff --git a/test/invariant.hpp b/test/invariant.hpp deleted file mode 100644 index a91714cb..00000000 --- a/test/invariant.hpp +++ /dev/null @@ -1,128 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_CONTAINER_INVARIANT_HEADER) -#define BOOST_UNORDERED_TEST_CONTAINER_INVARIANT_HEADER - -#include -#include -#include -#include "./helpers/invariant_checker.hpp" -#include "./helpers/functional.hpp" - -namespace test -{ - template - bool check_matches(Key const&, Pred const&) - { - return true; - } - - bool check_matches(hash const& h, equals const& p) - { - // TODO: This isn't actually true - change functional so that it is. - BOOST_CHECK_EQUAL(h.offset, p.tag); - return true; - } - - template - void invariant_check_container(Container const& x) - { - // Check that the begin and end iterators match the container size. - // (And also that you can iterate through all the elements). - BOOST_CHECK_EQUAL((std::size_t) std::distance(x.begin(), x.end()), - x.size()); - - BOOST_CHECK(check_matches(x.hash_function(), x.key_eq())); - - // It is possible for this to legally fail, eg. if you - // set max_load_factor to lower than the current load factor and - // don't give the table a chance to rehash. - BOOST_WARN(x.load_factor() <= x.max_load_factor()); - } - - template - void invariant_check_set(Container const& x) - { - invariant_check_container(x); - - // Check that the elements are in the correct buckets. - std::size_t count = 0; - std::size_t bucket_count = x.bucket_count(); - for(std::size_t i = 0; i < bucket_count; ++i) { - std::size_t bucket_size = 0; - for(typename Container::const_local_iterator j = x.begin(i); - j != x.end(i); ++j) - { - ++bucket_size; - BOOST_CHECK_EQUAL(i, x.bucket(*j)); - BOOST_CHECK_EQUAL(i, - x.hash_function()(*j) % bucket_count); - } - BOOST_CHECK_EQUAL(bucket_size, x.bucket_size(i)); - - count += bucket_size; - } - - // Check that the size matches what we've just seen. - BOOST_CHECK_EQUAL(count, x.size()); - } - - template - void invariant_check_map(Container const& x) - { - invariant_check_container(x); - - // Check that the elements are in the correct buckets. - std::size_t count = 0; - std::size_t bucket_count = x.bucket_count(); - for(std::size_t i = 0; i < bucket_count; ++i) { - std::size_t bucket_size = 0; - for(typename Container::const_local_iterator j = x.begin(i); - j != x.end(i); ++j) - { - ++bucket_size; - BOOST_CHECK_EQUAL(i, x.bucket(j->first)); - BOOST_CHECK_EQUAL(i, - x.hash_function()(j->first) % bucket_count); - } - BOOST_CHECK_EQUAL(bucket_size, x.bucket_size(i)); - - count += bucket_size; - } - - // Check that the size matches what we've just seen. - BOOST_CHECK_EQUAL(count, x.size()); - } -} - -namespace boost -{ - template - void invariant_impl(boost::unordered_set const& x) - { - test::invariant_check_set(x); - } - - template - void invariant_impl(boost::unordered_multiset const& x) - { - test::invariant_check_set(x); - } - - template - void invariant_impl(boost::unordered_map const& x) - { - test::invariant_check_map(x); - } - - template - void invariant_impl(boost::unordered_multimap const& x) - { - test::invariant_check_map(x); - } -} - -#endif diff --git a/test/iterator_tests.cpp b/test/iterator_tests.cpp deleted file mode 100644 index 19a12277..00000000 --- a/test/iterator_tests.cpp +++ /dev/null @@ -1,141 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include -#include - -#define BOOST_AUTO_TEST_MAIN -#include - -// TODO: Add these headers to new_iterator_tests.hpp -#include -#include -#include - -#include -#include -#include -#include "./helpers/unit_test.hpp" - -BOOST_AUTO_UNIT_TEST(iterator_tests) -{ - boost::unordered_set > set; - boost::unordered_multiset multiset; - boost::unordered_map map; - boost::unordered_multimap > multimap; - - set.insert(std::pair("Anthony","Cleopatra")); - set.insert(std::pair("Victoria","Albert")); - set.insert(std::pair("Pete","Dud")); - set.insert(std::pair("Blair","Brown")); - set.insert(std::pair("John","Yoko")); - set.insert(std::pair("Charles","Diana")); - set.insert(std::pair("Marx","Engels")); - set.insert(std::pair("Sid","Nancy")); - set.insert(std::pair("Lucy","Ricky")); - set.insert(std::pair("George","Mildred")); - set.insert(std::pair("Fanny","Alexander")); - set.insert(std::pair("Den","Angie")); - set.insert(std::pair("Sonny","Cher")); - set.insert(std::pair("Bonnie","Clyde")); - set.insert(std::pair("Punch","Judy")); - set.insert(std::pair("Powell","Pressburger")); - set.insert(std::pair("Jekyll","Hyde")); - - multiset.insert(0.434321); - multiset.insert(443421); - multiset.insert(0.434321); - - map[98] = 3; - map[99] = 4; - map[2000] = 788421; - map[2001] = 2; - - static char* strings1 = "Banjo\0Banjo\0Ukulele"; - - static char* strings[] = { - strings1, - strings1 + 6, - strings1 + 12 - }; - - BOOST_CHECK(std::string(strings[0]) == std::string(strings[1])); - BOOST_CHECK(strings[0] != strings[1]); - - multimap.insert(std::make_pair(strings[0], std::make_pair(5,6))); - multimap.insert(std::make_pair(strings[1], std::make_pair(85,32))); - multimap.insert(std::make_pair(strings[1], std::make_pair(91,142))); - multimap.insert(std::make_pair(strings[2], std::make_pair(12,3))); - multimap.insert(std::make_pair(strings[2], std::make_pair(10,94))); - multimap.insert(std::make_pair(strings[2], std::make_pair(345,42))); - - BOOST_CHECK_EQUAL(multimap.count(strings[0]), 1); - BOOST_CHECK_EQUAL(multimap.count(strings[1]), 2); - BOOST_CHECK_EQUAL(multimap.count(strings[2]), 3); - - boost::forward_readable_iterator_test(set.begin(), set.end(), - *set.begin(), *boost::next(set.begin())); - boost::forward_readable_iterator_test(multiset.begin(), multiset.end(), - *multiset.begin(), *boost::next(multiset.begin())); - boost::forward_readable_iterator_test(map.begin(), map.end(), - *map.begin(), *boost::next(map.begin())); - boost::forward_readable_iterator_test(multimap.begin(), multimap.end(), - *multimap.begin(), *boost::next(multimap.begin())); -} - -BOOST_AUTO_UNIT_TEST(rubbish_iterator_test) -{ - typedef boost::unordered_map map; - typedef map::local_iterator local_iterator; - typedef map::const_local_iterator const_local_iterator; - typedef map::iterator iterator; - typedef map::const_iterator const_iterator; - - map m; - iterator it; - const_iterator const_it(it); - local_iterator local; - const_local_iterator const_local(local); - - m[10] = 3; - m[25] = 5; - m[2] = 21; - - it = m.begin(); - const_it = m.begin(); - map::size_type index = m.bucket(10); - local = m.begin(index); - const_local = m.begin(index); - - BOOST_CHECK(it == const_it); - BOOST_CHECK(const_it == it); - BOOST_CHECK(local == const_local); - BOOST_CHECK(const_local == local); - - BOOST_CHECK(it++ == const_it); - BOOST_CHECK(local++ == const_local); - - BOOST_CHECK(it != const_it); - BOOST_CHECK(const_it != it); - BOOST_CHECK(local != const_local); - BOOST_CHECK(const_local != local); - - BOOST_CHECK(++const_it == it); - BOOST_CHECK(++const_local == local); - - it = m.begin(); - int values[3]; - std::pair const& r1= *it++; - values[0] = r1.second; - values[1] = it++->second; - values[2] = it++->second; - BOOST_CHECK(it == m.end()); - - std::sort(values, values+3); - - BOOST_CHECK_EQUAL(values[0], 3); - BOOST_CHECK_EQUAL(values[1], 5); - BOOST_CHECK_EQUAL(values[2], 21); -} diff --git a/test/map_operator_tests.cpp b/test/map_operator_tests.cpp deleted file mode 100644 index 238d2860..00000000 --- a/test/map_operator_tests.cpp +++ /dev/null @@ -1,69 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include -#include "./helpers/unit_test.hpp" -#include "./helpers/exception_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/input_iterator_adaptor.hpp" -#include "./strong.hpp" -#include "./invariant.hpp" - -// TODO: This is just the first insert test slightly modified, should do better. - -META_FUNC_TEST_CASE(insert_individual,Container) -{ - test::random_values values(100); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - Container x; - - INVARIANT_CHECK(x); - - ACTIVATE_EXCEPTIONS; - - for(typename test::random_values::iterator - it = values.begin(); it != values.end(); ++it) - { - typename Container::value_type::second_type* ref; - - // Looking at TR1 I can't find this requirement anywhere, but it - // would seem silly not to require it so I think it's either an - // omission or I haven't looked hard enough. - STRONG_TEST(tester, x) { - try { - ref = &x[it->first]; - } catch(test::hash_exception) { - tester.dismiss(); - throw; - } - } - - DEACTIVATE_EXCEPTIONS; - *ref = it->second; - - typename Container::iterator pos = x.find(values.get_key(*it)); - BOOST_CHECK(pos != x.end() && - x.key_eq()(values.get_key(*pos), values.get_key(*it)) && - test::equivalent(*it, *pos)); - } - } - EXCEPTION_TEST_END -} - -typedef boost::unordered_map map1; -typedef boost::unordered_map > map2; - -AUTO_META_TESTS( - (insert_individual), - (map1)(map2) -) diff --git a/test/max_load_factor_test.cpp b/test/max_load_factor_test.cpp deleted file mode 100644 index c4a0570b..00000000 --- a/test/max_load_factor_test.cpp +++ /dev/null @@ -1,170 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include -#include - -#define BOOST_AUTO_TEST_MAIN -#include -#include - -#include - -// If the bucket count is higher than the upper bounds in this file, it's not a -// bug (but it's not that great either). There are also some tests on when the -// number of buckets is increased on an insert, these tests are checking that -// some implementation details are working - if the implementation is changed -// they can be removed. -// -// TODO: the argument to max_load_factor is just a hint, so test against the -// container's actual max_load_factor. - -BOOST_AUTO_UNIT_TEST(test_rehash) -{ - boost::counting_iterator begin(0); - boost::counting_iterator end(1000); - - boost::unordered_set x1(begin, end); - BOOST_CHECK(x1.bucket_count() >= 1000); - BOOST_WARN(x1.bucket_count() < 2000); - - x1.max_load_factor(0.5); - x1.rehash(0); - BOOST_CHECK(x1.bucket_count() >= 2000); - BOOST_WARN(x1.bucket_count() < 4000); - x1.rehash(0); - BOOST_CHECK(x1.bucket_count() >= 2000); - BOOST_WARN(x1.bucket_count() < 4000); - - x1.max_load_factor(2.0); - x1.rehash(0); - BOOST_CHECK(x1.bucket_count() >= 500); - BOOST_WARN(x1.bucket_count() < 1000); - x1.rehash(1500); - BOOST_CHECK(x1.bucket_count() >= 1500); - BOOST_WARN(x1.bucket_count() < 3000); - - x1.max_load_factor(0.5); - x1.rehash(0); - BOOST_CHECK(x1.bucket_count() >= 2000); - BOOST_WARN(x1.bucket_count() < 4000); -} - -BOOST_AUTO_UNIT_TEST(test_insert_range) -{ - boost::counting_iterator begin(0); - boost::counting_iterator end(1000); - - boost::unordered_set x1(begin, end); - BOOST_CHECK(x1.bucket_count() >= 1000); - BOOST_WARN(x1.bucket_count() < 2000); - - x1.clear(); - x1.max_load_factor(0.5); - x1.rehash(0); - x1.insert(begin, end); - BOOST_CHECK(x1.bucket_count() >= 2000); - BOOST_WARN(x1.bucket_count() < 4000); - - x1.clear(); - x1.max_load_factor(2.0); - x1.rehash(0); - x1.insert(begin, end); - BOOST_CHECK(x1.bucket_count() >= 500); - BOOST_WARN(x1.bucket_count() < 1000); -} - -BOOST_AUTO_UNIT_TEST(test_insert) -{ - boost::unordered_map x1; - - size_t i; - for(i = 0; i < 1000; ++i) - { - size_t old_bucket_count = x1.bucket_count(); - x1[i] = i; - BOOST_CHECK(i <= x1.bucket_count()); - BOOST_CHECK(x1.bucket_count() == old_bucket_count || - x1.size() >= old_bucket_count); - - // This isn't really required: - BOOST_WARN(x1.size() < x1.bucket_count()); - } - - x1.clear(); - x1.max_load_factor(2.0); - x1.rehash(0); - - for(i = 0; i < 1000; ++i) - { - size_t old_bucket_count = x1.bucket_count(); - x1[i] = i; - BOOST_CHECK(i <= x1.bucket_count() * 2); - BOOST_CHECK(x1.bucket_count() == old_bucket_count || - x1.size() >= old_bucket_count * 2); - - // This isn't really required: - BOOST_WARN(x1.size() < x1.bucket_count() * 2); - } - - x1.clear(); - x1.rehash(0); - x1.max_load_factor(0.5); - - for(i = 0; i < 1000; ++i) - { - size_t old_bucket_count = x1.bucket_count(); - x1[i] = i; - BOOST_CHECK(i * 2 <= x1.bucket_count()); - BOOST_CHECK(x1.bucket_count() == old_bucket_count || - x1.size() * 2 >= old_bucket_count); - - // This isn't really required: - BOOST_WARN(x1.size() * 2 < x1.bucket_count()); - } -} - -BOOST_AUTO_UNIT_TEST(test_large_mlf) -{ - boost::unordered_set x1; - x1.max_load_factor(static_cast( - (std::numeric_limits::size_type>::max)() - ) * 10); - - boost::unordered_map::size_type bucket_count = x1.bucket_count(); - - for(int i = 0; i < 1000; ++i) - { - x1.insert(i); - BOOST_CHECK(x1.bucket_count() == bucket_count); - } - - boost::counting_iterator begin(1000); - boost::counting_iterator end(2000); - x1.insert(begin, end); - BOOST_CHECK(x1.bucket_count() == bucket_count); -} - -BOOST_AUTO_UNIT_TEST(test_infinite_mlf) -{ - if(std::numeric_limits::has_infinity) - { - boost::unordered_set x1; - x1.max_load_factor(std::numeric_limits::infinity()); - - boost::unordered_map::size_type bucket_count = x1.bucket_count(); - - for(int i = 0; i < 1000; ++i) - { - x1.insert(i); - BOOST_CHECK(x1.bucket_count() == bucket_count); - } - - boost::counting_iterator begin(1000); - boost::counting_iterator end(2000); - x1.insert(begin, end); - BOOST_CHECK(x1.bucket_count() == bucket_count); - } -} diff --git a/test/next_prime_tests.cpp b/test/next_prime_tests.cpp deleted file mode 100644 index b3a81f81..00000000 --- a/test/next_prime_tests.cpp +++ /dev/null @@ -1,18 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include - -#define BOOST_AUTO_TEST_MAIN -#include - -BOOST_AUTO_UNIT_TEST(next_prime_test) -{ - BOOST_CHECK_EQUAL(53ul, boost::unordered_detail::next_prime(0)); - BOOST_CHECK_EQUAL(53ul, boost::unordered_detail::next_prime(52)); - BOOST_CHECK_EQUAL(53ul, boost::unordered_detail::next_prime(53)); - BOOST_CHECK_EQUAL(97ul, boost::unordered_detail::next_prime(54)); - BOOST_CHECK_EQUAL(98317ul, boost::unordered_detail::next_prime(50000ul)); -} diff --git a/test/set_assign_fail_test.cpp b/test/set_assign_fail_test.cpp deleted file mode 100644 index ccf4e93d..00000000 --- a/test/set_assign_fail_test.cpp +++ /dev/null @@ -1,14 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include - -void func() -{ - boost::unordered_set x; - x.insert(10); - boost::unordered_set::iterator it = x.begin(); - *x = 25; -} diff --git a/test/strong.hpp b/test/strong.hpp deleted file mode 100644 index 86218568..00000000 --- a/test/strong.hpp +++ /dev/null @@ -1,102 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TESTS_STRONG_HEADER) -#define BOOST_UNORDERED_TESTS_STRONG_HEADER - -#include "./helpers/strong.hpp" -#include "./helpers/equivalent.hpp" -#include "./helpers/metafunctions.hpp" -#include -#include -#include -#include - -namespace test -{ - struct equals_t - { - template - bool operator()(X const& x, Y const& y) - { - return x == y; - } - - template - bool operator()(std::pair const& x, std::pair const& y) - { - return x.first == y.first && x.second == y.second; - } - } equals1; - - template - class unordered_strong_tester - : public strong_tester - { - Container const& reference_; - - typename Container::size_type size_; - typename Container::hasher hasher_; - typename Container::key_equal key_equal_; - - std::deque::type> values_; - public: - unordered_strong_tester(Container const &x) - : reference_(x), size_(x.size()), - hasher_(x.hash_function()), key_equal_(x.key_eq()), - values_(x.begin(), x.end()) - { - } - - void test() - { - BOOST_CHECK(size_ == reference_.size()); - BOOST_CHECK(test::equivalent(hasher_, reference_.hash_function())); - BOOST_CHECK(test::equivalent(key_equal_, reference_.key_eq())); - BOOST_CHECK(values_.size() == reference_.size()); - BOOST_CHECK(values_.size() == reference_.size() && - std::equal(values_.begin(), values_.end(), reference_.begin(), equals1)); - } - private: - unordered_strong_tester(); - }; -} - -namespace boost -{ - template - test::strong_tester_ptr create_tester_impl( - boost::unordered_set const& x, int) - { - return test::strong_tester_ptr(new test::unordered_strong_tester< - boost::unordered_set >(x)); - } - - template - test::strong_tester_ptr create_tester_impl( - boost::unordered_multiset const& x, int) - { - return test::strong_tester_ptr(new test::unordered_strong_tester< - boost::unordered_multiset >(x)); - } - - template - test::strong_tester_ptr create_tester_impl( - boost::unordered_map const& x, int) - { - return test::strong_tester_ptr(new test::unordered_strong_tester< - boost::unordered_map >(x)); - } - - template - test::strong_tester_ptr create_tester_impl( - boost::unordered_multimap const& x, int) - { - return test::strong_tester_ptr(new test::unordered_strong_tester< - boost::unordered_multimap >(x)); - } -} - -#endif diff --git a/test/swap_tests.hpp b/test/swap_tests.hpp deleted file mode 100644 index 1c4bbbf3..00000000 --- a/test/swap_tests.hpp +++ /dev/null @@ -1,120 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include -#include "./helpers/unit_test.hpp" -#include "./helpers/exception_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/constructors.hpp" -#include "./helpers/equivalent.hpp" -#include "./invariant.hpp" - -META_FUNC_TEST_CASE(swap_test1, Container) -{ - test::constructors constructor; - - test::random_values values_x(10); - test::random_values values_y(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - - Container x(values_x.begin(), values_x.end(), 0, - constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); - x.max_load_factor(0.5); - Container y(values_y.begin(), values_y.end(), 0, - constructor.hasher(23), constructor.key_equal(23), constructor.allocator(12)); - y.max_load_factor(2.0); - - INVARIANT_CHECK(x); - INVARIANT_CHECK(y); - - ACTIVATE_EXCEPTIONS; - - if(BOOST_UNORDERED_SWAP_METHOD == 1 - && !test::equivalent(x.get_allocator(), y.get_allocator())) { - BOOST_CHECK_THROW(x.swap(y), std::runtime_error); - } - else { -#if BOOST_UNORDERED_SWAP_METHOD == 2 - // Swap Method 2 can't meet TR1's exception specifications. Sigh. - x.swap(y); -#else - try { - x.swap(y); - } - catch (test::hash_copy_exception&) { - throw; - } - catch (test::pred_copy_exception&) { - throw; - } - catch (...) { - BOOST_ERROR("Only the copy constructor/assigned of the hash " - "and predicate objects are allowed to throw in swap."); - throw; - } -#endif - - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(23))); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(23))); - BOOST_CHECK_EQUAL(x.max_load_factor(), 2.0); -#if BOOST_UNORDERED_SWAP_METHOD == 2 - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); -#else - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(12))); -#endif - - BOOST_CHECK(test::equivalent(y.hash_function(), constructor.hasher(55))); - BOOST_CHECK(test::equivalent(y.key_eq(), constructor.key_equal(55))); - BOOST_CHECK_EQUAL(y.max_load_factor(), 0.5); -#if BOOST_UNORDERED_SWAP_METHOD == 2 - BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator(12))); -#else - BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator(10))); -#endif - } - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(self_swap, Container) -{ - test::constructors constructor; - test::random_values values_x(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - - Container x(values_x.begin(), values_x.end(), 0, - constructor.hasher(55), constructor.key_equal(55), - constructor.allocator(10)); - x.max_load_factor(0.5); - - INVARIANT_CHECK(x); - - ACTIVATE_EXCEPTIONS; - - x.swap(x); - - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); - BOOST_CHECK_EQUAL(x.max_load_factor(), 0.5); - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); - } - EXCEPTION_TEST_END -} - -AUTO_META_TESTS( - (swap_test1)(self_swap), - CONTAINER_SEQ -) diff --git a/test/swap_tests.ipp b/test/swap_tests.ipp deleted file mode 100644 index 67623d99..00000000 --- a/test/swap_tests.ipp +++ /dev/null @@ -1,102 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./containers.hpp" - -#define BOOST_AUTO_TEST_MAIN -#include - -#include -#include "./helpers/unit_test.hpp" -#include "./helpers/exception_test.hpp" -#include "./helpers/random_values.hpp" -#include "./helpers/constructors.hpp" -#include "./helpers/equivalent.hpp" -#include "./invariant.hpp" - -META_FUNC_TEST_CASE(swap_test1, Container) -{ - test::constructors constructor; - - test::random_values values_x(10); - test::random_values values_y(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - - Container x(values_x.begin(), values_x.end(), 0, - constructor.hasher(55), constructor.key_equal(55), constructor.allocator(10)); - x.max_load_factor(0.5); - Container y(values_y.begin(), values_y.end(), 0, - constructor.hasher(23), constructor.key_equal(23), constructor.allocator(12)); - y.max_load_factor(2.0); - - INVARIANT_CHECK(x); - INVARIANT_CHECK(y); - - ACTIVATE_EXCEPTIONS; - - if(BOOST_UNORDERED_SWAP_METHOD == 1 - && !test::equivalent(x.get_allocator(), y.get_allocator())) { - BOOST_CHECK_THROW(x.swap(y), std::runtime_error); - } - else { - x.swap(y); - - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(23))); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(23))); - BOOST_CHECK_EQUAL(x.max_load_factor(), 2.0); -#if BOOST_UNORDERED_SWAP_METHOD == 2 - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); -#else - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(12))); -#endif - - BOOST_CHECK(test::equivalent(y.hash_function(), constructor.hasher(55))); - BOOST_CHECK(test::equivalent(y.key_eq(), constructor.key_equal(55))); - BOOST_CHECK_EQUAL(y.max_load_factor(), 0.5); -#if BOOST_UNORDERED_SWAP_METHOD == 2 - BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator(12))); -#else - BOOST_CHECK(test::equivalent(y.get_allocator(), constructor.allocator(10))); -#endif - } - } - EXCEPTION_TEST_END -} - -META_FUNC_TEST_CASE(self_swap, Container) -{ - test::constructors constructor; - test::random_values values_x(10); - - EXCEPTION_TEST(1000) - { - DEACTIVATE_EXCEPTIONS; - - Container x(values_x.begin(), values_x.end(), 0, - constructor.hasher(55), constructor.key_equal(55), - constructor.allocator(10)); - x.max_load_factor(0.5); - - INVARIANT_CHECK(x); - - ACTIVATE_EXCEPTIONS; - - x.swap(x); - - BOOST_CHECK(test::equivalent(x.hash_function(), constructor.hasher(55))); - BOOST_CHECK(test::equivalent(x.key_eq(), constructor.key_equal(55))); - BOOST_CHECK_EQUAL(x.max_load_factor(), 0.5); - BOOST_CHECK(test::equivalent(x.get_allocator(), constructor.allocator(10))); - } - EXCEPTION_TEST_END -} - -AUTO_META_TESTS( - (swap_test1)(self_swap), - CONTAINER_SEQ -) diff --git a/test/swap_tests1.cpp b/test/swap_tests1.cpp deleted file mode 100644 index 5a61be4f..00000000 --- a/test/swap_tests1.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#define BOOST_UNORDERED_SWAP_METHOD 1 -#include "./swap_tests.hpp" diff --git a/test/swap_tests2.cpp b/test/swap_tests2.cpp deleted file mode 100644 index 56ccbd27..00000000 --- a/test/swap_tests2.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#define BOOST_UNORDERED_SWAP_METHOD 2 -#include "./swap_tests.hpp" diff --git a/test/swap_tests3.cpp b/test/swap_tests3.cpp deleted file mode 100644 index 13a5951f..00000000 --- a/test/swap_tests3.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#define BOOST_UNORDERED_SWAP_METHOD 3 -#include "./swap_tests.hpp" diff --git a/test/type_tests.cpp b/test/type_tests.cpp deleted file mode 100644 index 56f8c490..00000000 --- a/test/type_tests.cpp +++ /dev/null @@ -1,208 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include -#include - -#include -#include -#include -#include -#include "./helpers/functional.hpp" - -template -struct iterator_checks -{ - typedef Iterator iterator; - - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_value::type, - typename Container::value_type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_reference::type, - typename Container::reference>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_pointer::type, - typename Container::pointer>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_difference::type, - typename Container::difference_type>)); -}; - -template -struct const_iterator_checks -{ - typedef ConstIterator const_iterator; - - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_value::type, - typename Container::value_type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_reference::type, - typename Container::const_reference>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_pointer::type, - typename Container::const_pointer>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_difference::type, - typename Container::difference_type>)); -}; - -template -struct unordered_typedef_checks -{ - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; - typedef typename Container::local_iterator local_iterator; - typedef typename Container::const_local_iterator const_local_iterator; - - // 6.3.1/9 rows 1-3 - BOOST_MPL_ASSERT((boost::is_same< - Key, - typename Container::key_type>)); - BOOST_MPL_ASSERT((boost::is_same< - Hash, - typename Container::hasher>)); - BOOST_MPL_ASSERT((boost::is_same< - Pred, - typename Container::key_equal>)); - - // 6.3.1/9 rows 4-5 - // TODO: A local_iterator may be used to iterate through a single - // bucket but may not be used to iterate across buckets. - BOOST_MPL_ASSERT((boost::is_same< - typename boost::BOOST_ITERATOR_CATEGORY::type, - typename boost::BOOST_ITERATOR_CATEGORY::type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_value::type, - typename boost::iterator_value::type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_difference::type, - typename boost::iterator_difference::type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_pointer::type, - typename boost::iterator_pointer::type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_reference::type, - typename boost::iterator_reference::type>)); - - BOOST_MPL_ASSERT((boost::is_same< - typename boost::BOOST_ITERATOR_CATEGORY::type, - typename boost::BOOST_ITERATOR_CATEGORY::type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_value::type, - typename boost::iterator_value::type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_difference::type, - typename boost::iterator_difference::type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_pointer::type, - typename boost::iterator_pointer::type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_reference::type, - typename boost::iterator_reference::type>)); - - // TODO: Is this ever specified? - BOOST_MPL_ASSERT((boost::is_same< - AllocatorType, - typename Container::allocator_type>)); - BOOST_MPL_ASSERT((boost::is_same< - typename AllocatorType::pointer, - typename Container::pointer>)); - BOOST_MPL_ASSERT((boost::is_same< - typename AllocatorType::const_pointer, - typename Container::const_pointer>)); - BOOST_MPL_ASSERT((boost::is_same< - typename AllocatorType::reference, - typename Container::reference>)); - BOOST_MPL_ASSERT((boost::is_same< - typename AllocatorType::const_reference, - typename Container::const_reference>)); -}; - -template -struct unordered_map_checks -{ - unordered_typedef_checks c1; - iterator_checks c2; - const_iterator_checks c3; - iterator_checks c4; - const_iterator_checks c5; - - // 6.3.1/7 - BOOST_MPL_ASSERT((boost::is_same< - typename Container::value_type, - std::pair >)); -}; - -template -struct unordered_set_checks -{ - unordered_typedef_checks c1; - const_iterator_checks c2; - const_iterator_checks c3; - const_iterator_checks c4; - const_iterator_checks c5; - - // 6.3.1/7 - BOOST_MPL_ASSERT((boost::is_same< - typename Container::value_type, - typename Container::key_type>)); -}; - -unordered_set_checks< - boost::unordered_multiset, int, - boost::hash, std::equal_to, std::allocator -> int_multiset_check; - -unordered_set_checks< - boost::unordered_multiset, - std::string, test::hash, test::less, std::allocator -> custom_string_multiset_check; - -unordered_set_checks< - boost::unordered_set, int, - boost::hash, std::equal_to, std::allocator -> int_set_check; - -unordered_set_checks< - boost::unordered_set, - std::string, test::hash, test::less, std::allocator -> custom_string_set_check; - -unordered_map_checks< - boost::unordered_map, std::string, int, - boost::hash, std::equal_to, - std::allocator > -> string_int_check; - -unordered_map_checks< - boost::unordered_map, - char const*, std::string, - test::hash, test::less, - std::allocator > -> custom_check; - -unordered_map_checks< - boost::unordered_multimap, - int, int, - boost::hash, std::equal_to, - std::allocator > -> int_int_multi_check; - -unordered_map_checks< - boost::unordered_multimap, - std::string, int, - boost::hash, std::equal_to, - std::allocator > -> string_int_multi_check; - -unordered_map_checks< - boost::unordered_multimap, - float, std::string, - test::hash, test::less, - std::allocator > -> custom_multi_check; From 351cd6e8a1c404eb6fc68b2e96d63b24bb8b6582 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 27 Feb 2006 00:54:08 +0000 Subject: [PATCH 013/175] Remove more of the old unordered containers test files. [SVN r2878] --- test/helpers/accessors.hpp | 12 - test/helpers/allocator.cpp | 209 ------------- test/helpers/allocator.hpp | 376 ------------------------ test/helpers/base.cpp | 31 -- test/helpers/base.hpp | 15 - test/helpers/config.hpp | 18 -- test/helpers/constructors.hpp | 69 ----- test/helpers/equivalent.hpp | 45 --- test/helpers/exception.cpp | 85 ------ test/helpers/exception.hpp | 60 ---- test/helpers/exception_test.cpp | 108 ------- test/helpers/exception_test.hpp | 33 --- test/helpers/exception_trigger.hpp | 49 --- test/helpers/functional.cpp | 140 --------- test/helpers/functional.hpp | 80 ----- test/helpers/generators.cpp | 43 --- test/helpers/generators.hpp | 41 --- test/helpers/input_iterator_adaptor.hpp | 35 --- test/helpers/invariant_checker.cpp | 54 ---- test/helpers/invariant_checker.hpp | 78 ----- test/helpers/less.hpp | 37 --- test/helpers/member.cpp | 79 ----- test/helpers/member.hpp | 40 --- test/helpers/metafunctions.hpp | 100 ------- test/helpers/random_values.hpp | 203 ------------- test/helpers/strong.cpp | 32 -- test/helpers/strong.hpp | 78 ----- test/helpers/unit_test.hpp | 77 ----- 28 files changed, 2227 deletions(-) delete mode 100644 test/helpers/accessors.hpp delete mode 100644 test/helpers/allocator.cpp delete mode 100644 test/helpers/allocator.hpp delete mode 100644 test/helpers/base.cpp delete mode 100644 test/helpers/base.hpp delete mode 100644 test/helpers/config.hpp delete mode 100644 test/helpers/constructors.hpp delete mode 100644 test/helpers/equivalent.hpp delete mode 100644 test/helpers/exception.cpp delete mode 100644 test/helpers/exception.hpp delete mode 100644 test/helpers/exception_test.cpp delete mode 100644 test/helpers/exception_test.hpp delete mode 100644 test/helpers/exception_trigger.hpp delete mode 100644 test/helpers/functional.cpp delete mode 100644 test/helpers/functional.hpp delete mode 100644 test/helpers/generators.cpp delete mode 100644 test/helpers/generators.hpp delete mode 100644 test/helpers/input_iterator_adaptor.hpp delete mode 100644 test/helpers/invariant_checker.cpp delete mode 100644 test/helpers/invariant_checker.hpp delete mode 100644 test/helpers/less.hpp delete mode 100644 test/helpers/member.cpp delete mode 100644 test/helpers/member.hpp delete mode 100644 test/helpers/metafunctions.hpp delete mode 100644 test/helpers/random_values.hpp delete mode 100644 test/helpers/strong.cpp delete mode 100644 test/helpers/strong.hpp delete mode 100644 test/helpers/unit_test.hpp diff --git a/test/helpers/accessors.hpp b/test/helpers/accessors.hpp deleted file mode 100644 index f5abfefd..00000000 --- a/test/helpers/accessors.hpp +++ /dev/null @@ -1,12 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_ACCESSORS_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_ACCESSORS_HEADER - -namespace test -{ - -} diff --git a/test/helpers/allocator.cpp b/test/helpers/allocator.cpp deleted file mode 100644 index 830dd7bb..00000000 --- a/test/helpers/allocator.cpp +++ /dev/null @@ -1,209 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./config.hpp" -#include "./allocator.hpp" -#include "./exception_trigger.hpp" -#include "./exception.hpp" -#include -#include - -#if !defined(BOOST_OLD_IOSTREAMS) -# include -#else -# include -#endif - -namespace test -{ - namespace - { - const unsigned int max_track = 1000; - - struct allocate_details - { - int tag; - std::size_t length; - - allocate_details() - : tag(0), length(0) {} - - allocate_details(int t, int l) - : tag(t), length(l) {} - }; - - std::map allocate_map; - unsigned int reference_count = 0; - unsigned int alloc_count = 0; - - static void ref() - { - ++reference_count; - } - - static void unref() - { - if(--reference_count == 0) { - BOOST_CHECK_MESSAGE(alloc_count == 0 && allocate_map.empty(), - "Memory leak found"); - allocate_map.clear(); - } - } - } - - allocator_base::allocator_base(int x) - : tag(x) - { - ref(); - } - - allocator_base::allocator_base(allocator_base const& x) - : tag(x.tag) - { - ref(); - } - - allocator_base::~allocator_base() - { - unref(); - } - - allocator_base::size_type allocator_base::max_size() const - { - return (std::numeric_limits::max)(); - } - - void* allocator_base::allocate(size_type n, void const*, size_type size) - { - BOOST_CHECK(n <= max_size()); - - exception_trigger((allocator_exception*) 0); - - // TODO: This is not exception safe. - void* ptr = ::operator new(n * size); - ++alloc_count; - if(allocate_map.size() < max_track) - allocate_map[ptr] = allocate_details(tag, n); - - return ptr; - } - - void allocator_base::construct(void* ptr) - { - exception_trigger((allocator_exception*) 0); - } - - void allocator_base::destroy(void* ptr) - { - } - - void allocator_base::deallocate(void* ptr, size_type n) - { - BOOST_CHECK(n <= max_size()); - if(allocate_map.find(ptr) == allocate_map.end()) { - if(alloc_count <= allocate_map.size()) - BOOST_ERROR("Deallocating unknown pointer."); - } else { - // TODO: This is not exception safe. - BOOST_CHECK_EQUAL(allocate_map[ptr].tag, tag); - BOOST_CHECK_EQUAL(allocate_map[ptr].length, n); - allocate_map.erase(ptr); - ::operator delete(ptr); - } - --alloc_count; - } - - void allocator_base::swap(allocator_base& x) - { - std::swap(tag, x. tag); - } - - std::ostream& operator<<(std::ostream& out, allocator_base const& x) - { - out<<"Test Allocator("<::max)() / 4; - } - - void* minimal_allocator_base::allocate(size_type n, void const*, size_type size) - { - BOOST_CHECK(n <= max_size()); - - exception_trigger((allocator_exception*) 0); - - // TODO: This is not exception safe. - void* ptr = ::operator new(n * size); - allocate_map[ptr] = allocate_details(tag, n); - - return ptr; - } - - void minimal_allocator_base::construct(void* ptr) - { - exception_trigger((allocator_exception*) 0); - } - - void minimal_allocator_base::destroy(void* ptr) - { - } - - void minimal_allocator_base::deallocate(void* ptr, size_type n) - { - BOOST_CHECK(n <= max_size()); - if(allocate_map.find(ptr) == allocate_map.end()) { - BOOST_ERROR("Deallocating unknown pointer."); - } else { - // TODO: This is not exception safe. - BOOST_CHECK_EQUAL(allocate_map[ptr].tag, tag); - BOOST_CHECK_EQUAL(allocate_map[ptr].length, n); - allocate_map.erase(ptr); - ::operator delete(ptr); - } - } - - void minimal_allocator_base::swap(minimal_allocator_base& x) - { - std::swap(tag, x. tag); - } - - std::ostream& operator<<(std::ostream& out, minimal_allocator_base const& x) - { - out<<"Minimal Allocator("< -#include -#include - -namespace test -{ - struct allocator_base - { - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - - int tag; - - allocator_base(int x); - allocator_base(allocator_base const&); - ~allocator_base(); - - size_type max_size() const; - - void* allocate(size_type, void const*, size_type); - void construct(void*); - void destroy(void*); - void deallocate(void*, size_type); - void swap(allocator_base&); - private: - allocator_base& operator=(allocator_base const&); - }; - - std::ostream& operator<<(std::ostream&, allocator_base const&); - bool allocator_equals(allocator_base const& x, allocator_base const& y); - - template - struct allocator : allocator_base - { - typedef T* pointer; - typedef T const* const_pointer; - typedef T& reference; - typedef T const& const_reference; - typedef T value_type; - - template - struct rebind - { - typedef allocator other; - }; - - pointer address(reference x) const - { - return &x; - } - - const_pointer address(const_reference x) const - { - return &x; - } - - allocator(int x = 1) : allocator_base(x) {} - template - allocator(allocator const& x) : allocator_base(x) {} - allocator(allocator const& x) : allocator_base(x) {} - ~allocator() {} - - pointer allocate(size_type n, T const* hint = 0) - { - return static_cast( - allocator_base::allocate(n, hint, sizeof(T))); - } - - void construct(pointer ptr, T const& x) - { - allocator_base::construct(ptr); - new((void*)ptr)T(x); - } - - void destroy(pointer ptr) - { - allocator_base::destroy(ptr); - ptr->~T(); - } - - void deallocate(pointer ptr, size_type n) - { - allocator_base::deallocate(ptr, n); - } - }; - - template - inline bool operator==(allocator const& x, allocator const& y) - { - return test::allocator_equals(x, y); - } - - template - inline bool operator!=(allocator const& x, allocator const& y) - { - return !test::allocator_equals(x, y); - } - - template - void swap(allocator& x, allocator& y) - { - x.swap(y); - } - - template - allocator create_allocator(allocator*) - { - return allocator(); - } - - template - allocator create_allocator(allocator*, int x) - { - return allocator(x); - } - - template struct minimal_allocator; - typedef unsigned short minimal_size_type; - - template - class minimal_pointer_base - { - protected: - typedef minimal_pointer_base pointer_base; - minimal_pointer_base() : ptr_(0) {} - explicit minimal_pointer_base(T* ptr) : ptr_(ptr) {} - ~minimal_pointer_base() {} - Ptr& get() { return *static_cast(this); } - T* ptr_; - public: - typedef void (minimal_pointer_base::*bool_type)() const; - void this_type_does_not_support_comparisons() const {} - - T& operator*() const { return *ptr_; } - T* operator->() const { return ptr_; } - Ptr& operator++() { ++ptr_; return get(); } - Ptr operator++(int) { Ptr tmp(get()); ++ptr_; return tmp; } - - Ptr operator+(minimal_size_type s) const - { - return Ptr(ptr_ + s); - } - - T& operator[](minimal_size_type s) const - { - return ptr_[s]; - } - - operator bool_type() const - { - return ptr_ ? - &minimal_pointer_base::this_type_does_not_support_comparisons - : 0; - } - - bool operator!() const { return !ptr_; } - bool operator==(Ptr const& x) const { return ptr_ == x.ptr_; } - bool operator!=(Ptr const& x) const { return ptr_ != x.ptr_; } - bool operator<(Ptr const& x) const { return ptr_ < x.ptr_; } - bool operator>(Ptr const& x) const { return ptr_ > x.ptr_; } - bool operator<=(Ptr const& x) const { return ptr_ <= x.ptr_; } - bool operator>=(Ptr const& x) const { return ptr_ >= x.ptr_; } - - friend std::ostream& operator<<(std::ostream& out, minimal_pointer_base const& x) - { - out< class minimal_pointer; - template class minimal_const_pointer; - - template - class minimal_pointer - : public minimal_pointer_base, T> - { - friend struct minimal_allocator; - friend class minimal_pointer_base, T>; - friend class minimal_const_pointer; - typedef typename minimal_pointer::pointer_base base; - minimal_pointer(T* ptr) : base(ptr) {} - - typedef minimal_const_pointer const_pointer; - typedef minimal_pointer pointer; - public: - minimal_pointer() : base() {} - - bool operator==(pointer const& x) const { return base::operator==(x); } - bool operator!=(pointer const& x) const { return base::operator!=(x); } - bool operator<(pointer const& x) const { return base::operator<(x);} - bool operator>(pointer const& x) const { return base::operator>(x);} - bool operator<=(pointer const& x) const { return base::operator<=(x);} - bool operator>=(pointer const& x) const { return base::operator<=(x);} - - bool operator==(const_pointer const& x) const { return x == *this; } - bool operator!=(const_pointer const& x) const { return x != *this; } - bool operator<(const_pointer const& x) const { return x > *this; } - bool operator>(const_pointer const& x) const { return x < *this; } - bool operator<=(const_pointer const& x) const { return x >= *this; } - bool operator>=(const_pointer const& x) const { return x <= *this; } - }; - - template - class minimal_const_pointer - : public minimal_pointer_base, T const> - { - friend struct minimal_allocator; - friend class minimal_pointer_base, T const>; - typedef typename minimal_const_pointer::pointer_base base; - minimal_const_pointer(T* ptr) : base(ptr) {} - - typedef minimal_const_pointer const_pointer; - typedef minimal_pointer pointer; - public: - minimal_const_pointer() : base() {} - minimal_const_pointer(minimal_pointer const& x) : base(x.ptr_) {} - - bool operator==(const_pointer const& x) const { return base::operator==(x); } - bool operator!=(const_pointer const& x) const { return base::operator!=(x); } - bool operator<(const_pointer const& x) const { return base::operator<(x);} - bool operator>(const_pointer const& x) const { return base::operator>(x);} - bool operator<=(const_pointer const& x) const { return base::operator<=(x);} - bool operator>=(const_pointer const& x) const { return base::operator<=(x);} - - bool operator==(pointer const& x) const { return operator==(const_pointer(x)); } - bool operator!=(pointer const& x) const { return operator!=(const_pointer(x)); } - bool operator<(pointer const& x) const { return operator<(const_pointer(x));} - bool operator>(pointer const& x) const { return operator>(const_pointer(x));} - bool operator<=(pointer const& x) const { return operator<=(const_pointer(x));} - bool operator>=(pointer const& x) const { return operator<=(const_pointer(x));} - }; - - struct minimal_allocator_base - { - typedef minimal_size_type size_type; - typedef std::ptrdiff_t difference_type; - - int tag; - - minimal_allocator_base(int x); - minimal_allocator_base(minimal_allocator_base const&); - ~minimal_allocator_base(); - - size_type max_size() const; - - void* allocate(size_type, void const*, size_type); - void construct(void*); - void destroy(void*); - void deallocate(void*, size_type); - void swap(minimal_allocator_base&); - private: - minimal_allocator_base& operator=(minimal_allocator_base const&); - }; - - std::ostream& operator<<(std::ostream&, minimal_allocator_base const&); - bool allocator_equals(minimal_allocator_base const&, minimal_allocator_base const&); - - template - struct minimal_allocator : minimal_allocator_base - { - typedef minimal_pointer pointer; - typedef minimal_const_pointer const_pointer; - typedef T& reference; - typedef T const& const_reference; - typedef T value_type; - - template - struct rebind - { - typedef minimal_allocator other; - }; - - pointer address(reference r) - { - return pointer(&r); - } - - const_pointer address(const_reference r) - { - return const_pointer(&r); - } - - pointer allocate(size_type n) - { - return pointer(static_cast( - minimal_allocator_base::allocate(n, 0, sizeof(T)))); - } - - pointer allocate(size_type n, const_pointer u) - { - return pointer(static_cast( - minimal_allocator_base::allocate(n, u.ptr_, sizeof(T)))); - } - - void deallocate(pointer p, size_type n) - { - minimal_allocator_base::deallocate(p.ptr_, n); - } - - minimal_allocator() - : minimal_allocator_base(0) - { - } - - explicit minimal_allocator(int tag) - : minimal_allocator_base(tag) - { - } - - template - minimal_allocator(minimal_allocator const& x) - : minimal_allocator_base(x) - { - } - - minimal_allocator(minimal_allocator const& x) - : minimal_allocator_base(x) - { - } - - void construct(pointer p, T const& t) - { - minimal_allocator_base::construct(p.ptr_); - new((void*)p.ptr_) T(t); - } - - void destroy(pointer p) - { - minimal_allocator_base::destroy(p.ptr_); - ((T*)p.ptr_)->~T(); - } - private: - minimal_allocator& operator=(minimal_allocator const&); - }; - - template - inline bool operator==(minimal_allocator const& x, minimal_allocator const& y) - { - return test::allocator_equals(x, y); - } - - template - inline bool operator!=(minimal_allocator const& x, minimal_allocator const& y) - { - return !test::allocator_equals(x, y); - } - - template - void swap(minimal_allocator& x, minimal_allocator& y) - { - x.swap(y); - } - - template - minimal_allocator create_allocator(minimal_allocator*) - { - return minimal_allocator(); - } - - template - minimal_allocator create_allocator(minimal_allocator*, int x) - { - return minimal_allocator(x); - } -} - -#endif diff --git a/test/helpers/base.cpp b/test/helpers/base.cpp deleted file mode 100644 index d351b08e..00000000 --- a/test/helpers/base.cpp +++ /dev/null @@ -1,31 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./base.hpp" -#include -#include - -namespace test -{ - namespace - { - std::vector end_checks; - } - - void register_end_check(void(*check)()) - { - end_checks.push_back(check); - } - - void call_check(void(*check)()) - { - check(); - } - - void end() - { - std::for_each(end_checks.begin(), end_checks.end(), call_check); - } -} diff --git a/test/helpers/base.hpp b/test/helpers/base.hpp deleted file mode 100644 index 5cc58b1a..00000000 --- a/test/helpers/base.hpp +++ /dev/null @@ -1,15 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_BASE_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_BASE_HEADER - -namespace test -{ - void register_end_check(void(*)()); - void end(); -} - -#endif diff --git a/test/helpers/config.hpp b/test/helpers/config.hpp deleted file mode 100644 index 5ac1a11e..00000000 --- a/test/helpers/config.hpp +++ /dev/null @@ -1,18 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_CONFIG_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_CONFIG_HEADER - -#include - -// From Boost.Dynamic Bitset: - -// support for pre 3.0 libstdc++ - thanks Phil Edwards! -#if defined (__STL_CONFIG_H) && !defined (__STL_USE_NEW_IOSTREAMS) -# define BOOST_OLD_IOSTREAMS -#endif - -#endif diff --git a/test/helpers/constructors.hpp b/test/helpers/constructors.hpp deleted file mode 100644 index 51925509..00000000 --- a/test/helpers/constructors.hpp +++ /dev/null @@ -1,69 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_CONSTRUCTORS_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_CONSTRUCTORS_HEADER - -namespace test -{ - template - Allocator create_allocator(Allocator*, int = 0) - { - return Allocator(); - } - - template - Hasher create_hasher(Hasher*, int = 0) - { - return Hasher(); - } - - template - KeyEqual create_key_equal(KeyEqual*, int = 0) - { - return KeyEqual(); - } - - template - class constructors - { - public: - typedef typename Container::allocator_type allocator_type; - typedef typename Container::hasher hasher_type; - typedef typename Container::key_equal key_equal_type; - - allocator_type allocator() const - { - return create_allocator((allocator_type*) 0); - } - - allocator_type allocator(int x) const - { - return create_allocator((allocator_type*) 0, x); - } - - hasher_type hasher() const - { - return create_hasher((hasher_type*) 0); - } - - hasher_type hasher(int x) const - { - return create_hasher((hasher_type*) 0, x); - } - - key_equal_type key_equal() const - { - return create_key_equal((key_equal_type*) 0); - } - - key_equal_type key_equal(int x) const - { - return create_key_equal((key_equal_type*) 0, x); - } - }; -} - -#endif diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp deleted file mode 100644 index 3416a838..00000000 --- a/test/helpers/equivalent.hpp +++ /dev/null @@ -1,45 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_EQUIVALENT_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_EQUIVALENT_HEADER - -#include -#include - -namespace test -{ - template - bool equivalent_impl(std::equal_to, std::equal_to, int) - { - return true; - } - - template - bool equivalent_impl(boost::hash, boost::hash, int) - { - return true; - } - - template - bool equivalent_impl(T const& x, T const& y, float) - { - return x == y; - } - - template - bool equivalent(T const& x, T const& y) - { - return equivalent_impl(x, y, 0); - } - - template - bool equivalent(std::pair const& x, std::pair const& y) - { - return equivalent(x.first, y.first) && equivalent(x.second, y.second); - } -} - -#endif diff --git a/test/helpers/exception.cpp b/test/helpers/exception.cpp deleted file mode 100644 index b50638cd..00000000 --- a/test/helpers/exception.cpp +++ /dev/null @@ -1,85 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./exception.hpp" - -namespace test -{ - exception::exception() - : message_("Triggered test exception") - { - } - - exception::exception(char const* message) - : message_(message) - { - } - - char const* exception::what() const throw() - { - return message_; - } - - hash_exception::hash_exception() - : exception("Triggered hash exception") - { - } - - hash_exception::hash_exception(char const* message) - : exception(message) - { - } - - hash_copy_exception::hash_copy_exception() - : hash_exception("Triggered hash copy exception") - { - } - - hash_copy_exception::hash_copy_exception(char const* message) - : hash_exception(message) - { - } - - pred_exception::pred_exception() - : exception("Triggered pred exception") - { - } - - pred_exception::pred_exception(char const* message) - : exception(message) - { - } - - pred_copy_exception::pred_copy_exception() - : pred_exception("Triggered pred copy exception") - { - } - - pred_copy_exception::pred_copy_exception(char const* message) - : pred_exception(message) - { - } - - allocator_exception::allocator_exception() - : exception("Triggered pred exception") - { - } - - allocator_exception::allocator_exception(char const* message) - : exception(message) - { - } - - allocator_copy_exception::allocator_copy_exception() - : allocator_exception("Triggered pred copy exception") - { - } - - allocator_copy_exception::allocator_copy_exception(char const* message) - : allocator_exception(message) - { - } -} - diff --git a/test/helpers/exception.hpp b/test/helpers/exception.hpp deleted file mode 100644 index 692ceaf2..00000000 --- a/test/helpers/exception.hpp +++ /dev/null @@ -1,60 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_HEADER - -#include - -namespace test -{ - // Exception Handling - - struct exception : std::exception - { - char const* message_; - exception(); - exception(char const*); - char const* what() const throw(); - }; - - struct hash_exception : exception - { - hash_exception(); - hash_exception(char const*); - }; - - struct hash_copy_exception : hash_exception - { - hash_copy_exception(); - hash_copy_exception(char const*); - }; - - struct pred_exception : exception - { - pred_exception(); - pred_exception(char const*); - }; - - struct pred_copy_exception : pred_exception - { - pred_copy_exception(); - pred_copy_exception(char const*); - }; - - struct allocator_exception : exception - { - allocator_exception(); - allocator_exception(char const*); - }; - - struct allocator_copy_exception : allocator_exception - { - allocator_copy_exception(); - allocator_copy_exception(char const*); - }; -} - -#endif diff --git a/test/helpers/exception_test.cpp b/test/helpers/exception_test.cpp deleted file mode 100644 index a8ec3e12..00000000 --- a/test/helpers/exception_test.cpp +++ /dev/null @@ -1,108 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./exception_test.hpp" -#include -#include -#include - -namespace test -{ - // TODO: (Writing this here instead of the headers to avoid recompiling - // the world) - // - // There are some major design flaws with the exception testing code, - // apart from global variables that is. - - namespace - { - int num_iterations = 0; - int current_iteration = 0; - int trigger_count = 0; - int max_trigger_count = 0; - bool failed = false; - bool exception_testing = false; - bool exceptions_active = false; - } - - void exception_start(int n) - { - num_iterations = n; - current_iteration = 0; - max_trigger_count = 0; - trigger_count = 0; - failed = false; - exception_testing = true; - exceptions_active = true; - } - - void exception_loop() - { - BOOST_CHECK(exceptions_active); - - ++current_iteration; - max_trigger_count = trigger_count; - exception_testing = failed; - exceptions_active = failed; - trigger_count = 0; - failed = false; - } - - bool exception_loop_test() - { - if(exception_testing && current_iteration == num_iterations) { - BOOST_ERROR("Too many iterations"); - return false; - } - else { - return exception_testing; - } - } - - void exception_failure() - { - failed = true; - } - - bool true_once() - { - ++trigger_count; - return !exception_testing || trigger_count > max_trigger_count; - } - - bool exception_trigger_test() - { - ++trigger_count; - return exception_testing && exceptions_active - && trigger_count > max_trigger_count; - } - - void exception_trigger() - { - if(exception_trigger_test()) throw exception(); - } - - void exception_trigger(char const* message) - { - if(exception_trigger_test()) throw exception(message); - } - - bool exceptions_activate(bool value) - { - bool old = exceptions_active; - exceptions_active = value; - return old; - } - - exception_control::exception_control(bool value) - : old_value(exceptions_activate(value)) - { - } - - exception_control::~exception_control() - { - exceptions_activate(old_value); - } -} diff --git a/test/helpers/exception_test.hpp b/test/helpers/exception_test.hpp deleted file mode 100644 index 80fb3db1..00000000 --- a/test/helpers/exception_test.hpp +++ /dev/null @@ -1,33 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_TEST_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_TEST_HEADER - -#include -#include -#include "./exception.hpp" -#include "./exception_trigger.hpp" -#include "./base.hpp" - -namespace test -{ - void exception_start(int); - bool exception_loop_test(); - void exception_loop(); - void exception_failure(); - - bool true_once(); -} - -#define EXCEPTION_TEST(count) \ - for(::test::exception_start(count); ::test::exception_loop_test(); \ - ::test::exception_loop()) \ - try - -#define EXCEPTION_TEST_END \ - catch(::test::exception const&) { ::test::exception_failure(); } - -#endif diff --git a/test/helpers/exception_trigger.hpp b/test/helpers/exception_trigger.hpp deleted file mode 100644 index 358ea7f6..00000000 --- a/test/helpers/exception_trigger.hpp +++ /dev/null @@ -1,49 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_TRIGGER_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_EXCEPTION_TRIGGER_HEADER - -#include - -namespace test -{ - // Exception Handling - - bool exception_trigger_test(); - void exception_trigger(); - void exception_trigger(char const*); - - template - void exception_trigger(T*) - { - if(exception_trigger_test()) throw T(); - } - - template - void exception_trigger(T*, char const* msg) - { - if(exception_trigger_test()) throw T(msg); - } - - struct exception_control - { - bool old_value; - - exception_control(bool); - ~exception_control(); - }; - -} - -#define ACTIVATE_EXCEPTIONS \ - ::test::exception_control BOOST_PP_CAT(ACTIVATE_EXCEPTIONS_, __LINE__) \ - (true) -#define DEACTIVATE_EXCEPTIONS \ - ::test::exception_control BOOST_PP_CAT(ACTIVATE_EXCEPTIONS_, __LINE__) \ - (false) - -#endif - diff --git a/test/helpers/functional.cpp b/test/helpers/functional.cpp deleted file mode 100644 index 03458b41..00000000 --- a/test/helpers/functional.cpp +++ /dev/null @@ -1,140 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./config.hpp" -#include "./functional.hpp" -#include "./exception.hpp" -#include "./exception_trigger.hpp" -#include -#include - -#if !defined(BOOST_OLD_IOSTREAMS) -# include -#else -# include -#endif - -namespace test -{ - // Hash - - hash::hash(int x) - : offset(x) {} - - hash::hash(hash const& x) - : offset(x.offset) - { - exception_trigger((hash_copy_exception*) 0); - } - - hash& hash::operator=(hash const& x) - { - exception_trigger((hash_copy_exception*) 0); - offset = x.offset; - exception_trigger((hash_copy_exception*) 0); - - return *this; - } - - std::size_t hash::calculate_hash(std::size_t x) const - { - exception_trigger((hash_exception*) 0); - return x + offset; - } - - std::size_t hash::operator()(char const* x) const - { - return calculate_hash(boost::hash()(x)); - } - - bool hash::operator==(hash const& x) const - { - return offset == x.offset; - } - - std::ostream& operator<<(std::ostream& out, hash x) - { - out<<"Test Hash("< -#include - -namespace test -{ - struct hash - { - int offset; - - explicit hash(int x = 1); - hash(hash const&); - hash& operator=(hash const&); - - std::size_t calculate_hash(std::size_t) const; - - template - std::size_t operator()(T const& x) const - { - return calculate_hash(boost::hash()(x)); - } - - std::size_t operator()(char const* x) const; - bool operator==(hash const& x) const; - }; - - std::ostream& operator<<(std::ostream& out, hash x); - hash create_hasher(hash*); - hash create_hasher(hash*, int x); - - struct equals - { - int tag; - - explicit equals(int x = 1); - equals(equals const&); - equals& operator=(equals const&); - - bool calculate_equals(bool) const; - - template - bool operator()(T1 const& x, T2 const& y) const - { - return calculate_equals(x == y); - } - - bool operator()(char const*, char const*) const; - bool operator==(equals const& x) const; - }; - - std::ostream& operator<<(std::ostream& out, equals x); - equals create_key_equal(equals*); - equals create_key_equal(equals*, int x); - - struct less - { - int tag; - - explicit less(int x = 0); - - template - bool operator()(T1 const& x, T2 const& y) const - { - return x < y; - } - - bool operator()(char const*, char const*) const; - }; - - std::ostream& operator<<(std::ostream& out, less x); -} - -#endif diff --git a/test/helpers/generators.cpp b/test/helpers/generators.cpp deleted file mode 100644 index 247a795c..00000000 --- a/test/helpers/generators.cpp +++ /dev/null @@ -1,43 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "generators.hpp" - -// Generators - -namespace test -{ - int generate(int const*) - { - using namespace std; - return rand(); - } - - char generate(char const*) - { - using namespace std; - return (char)(rand() % 26) + 'a'; - } - - std::string generate(std::string const*) - { - using namespace std; - - static test::generator char_gen; - - std::string result; - - int length = rand() % 10; - for(int i = 0; i < length; ++i) - result += char_gen(); - - //std::generate_n( - // std::back_inserter(result), - // rand() % 10, - // char_gen); - - return result; - } -} diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp deleted file mode 100644 index 3cbb9e37..00000000 --- a/test/helpers/generators.hpp +++ /dev/null @@ -1,41 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER - -#include -#include -#include - -namespace test -{ - int generate(int const*); - char generate(char const*); - std::string generate(std::string const*); - template std::pair generate( - std::pair const*); - - template - struct generator - { - typedef T value_type; - value_type operator()() - { - return generate((T const*) 0); - } - }; - - template - std::pair generate(std::pair const*) - { - static generator g1; - static generator g2; - - return std::pair(g1(), g2()); - } -} - -#endif diff --git a/test/helpers/input_iterator_adaptor.hpp b/test/helpers/input_iterator_adaptor.hpp deleted file mode 100644 index 715e123a..00000000 --- a/test/helpers/input_iterator_adaptor.hpp +++ /dev/null @@ -1,35 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER - -#include - -namespace test -{ - template - struct input_iterator_adaptor - : boost::iterator_adaptor< - input_iterator_adaptor, Iterator, - boost::use_default, std::input_iterator_tag> - { - typedef boost::iterator_adaptor< - input_iterator_adaptor, Iterator, - boost::use_default, std::input_iterator_tag> base; - - explicit input_iterator_adaptor(Iterator it = Iterator()) - : base(it) {} - }; - - template - input_iterator_adaptor make_input_iterator(Iterator it) - { - return input_iterator_adaptor(it); - } -} - -#endif - diff --git a/test/helpers/invariant_checker.cpp b/test/helpers/invariant_checker.cpp deleted file mode 100644 index 2f74750a..00000000 --- a/test/helpers/invariant_checker.cpp +++ /dev/null @@ -1,54 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include -#include "./invariant_checker.hpp" -#include -#include -#include - -namespace test -{ - namespace - { - typedef std::set check_set; - check_set checks; - } - - invariant_checker_base::invariant_checker_base() - { - } - - invariant_checker_base::~invariant_checker_base() - { - } - - void check_invariants() - { - // This was causing compile errors on Visual C++, because check - // has return type void. - //std::for_each(checks.begin(), checks.end(), - // boost::mem_fun(&invariant_checker_base::check)); - - check_set::iterator end = checks.end(); - for(check_set::iterator it = checks.begin(); it != end; ++it) - (*it)->check(); - } - - void invariant_add(invariant_checker_base* check) - { - checks.insert(check); - } - - void invariant_remove(invariant_checker_base* check) - { - checks.erase(check); - } - - void initial_check(invariant_checker_base const& x) - { - x.check(); - } -} diff --git a/test/helpers/invariant_checker.hpp b/test/helpers/invariant_checker.hpp deleted file mode 100644 index a678e3b3..00000000 --- a/test/helpers/invariant_checker.hpp +++ /dev/null @@ -1,78 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER - -#include -#include "./exception_trigger.hpp" - -namespace test -{ - struct invariant_checker_base - { - invariant_checker_base(); - virtual void check() const = 0; - protected: - virtual ~invariant_checker_base(); - }; - - void check_invariants(); - void invariant_add(invariant_checker_base*); - void invariant_remove(invariant_checker_base*); - - template - struct invariant_checker : invariant_checker_base - { - T& object_; - - invariant_checker(T& o) : object_(o) - { - invariant_add(this); - } - - ~invariant_checker() - { - check(); - invariant_remove(this); - } - - void check() const - { - DEACTIVATE_EXCEPTIONS; - invariant_impl(object_); - } - }; - - // On compilers without RVO check() will be called multiple times. - // No big deal I suppose. - template - invariant_checker make_invariant_checker(T& o) - { - return invariant_checker(o); - } - - // Calling this also prevents an unused variable warning when using - // an invariant checker. - void initial_check(::test::invariant_checker_base const&); - - // A one time invariant check. - template - void invariant_check(T const& x) - { - DEACTIVATE_EXCEPTIONS; - invariant_impl(x); - } -} - -#define INVARIANT_CHECK(o) \ - INVARIANT_CHECK2(o, BOOST_PP_CAT(invariant_checker_, __LINE__)) - -#define INVARIANT_CHECK2(o, name) \ - ::test::invariant_checker_base const& name = \ - ::test::make_invariant_checker(o); \ - ::test::initial_check(name) - -#endif diff --git a/test/helpers/less.hpp b/test/helpers/less.hpp deleted file mode 100644 index 9151685f..00000000 --- a/test/helpers/less.hpp +++ /dev/null @@ -1,37 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_LESS_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_LESS_HEADER - -#include - -namespace test -{ - template - bool compare_impl(T const& x, T const& y, float) - { - return x < y; - } - - template - bool compare_impl(std::pair const& x, - std::pair const& y, int) - { - return x.first < y.first || - (x.first == y.first && x.second < y.second); - } - - struct compare { - template - bool operator()(T const& x, T const& y) - { - return compare_impl(x, y, 0); - } - }; -} - -#endif - diff --git a/test/helpers/member.cpp b/test/helpers/member.cpp deleted file mode 100644 index 7edfc27e..00000000 --- a/test/helpers/member.cpp +++ /dev/null @@ -1,79 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./config.hpp" -#include "./member.hpp" -#include "./exception_trigger.hpp" -#include "./generators.hpp" - -#if !defined(BOOST_OLD_IOSTREAMS) -# include -#else -# include -#endif - -namespace test -{ - member::member(int x) - : value(x) - { - exception_trigger(); - } - - member::member(member const& x) - : value(x.value) - { - exception_trigger(); - } - - member& member::operator=(member const& x) - { - exception_trigger(); - value = x.value; - exception_trigger(); - - return *this; - } - - member::~member() - { - value = -1; - } - - bool member::operator==(member const& x) const - { - exception_trigger(); - return value == x.value; - } - - bool member::operator<(member const& x) const - { - exception_trigger(); - return value < x.value; - } - - std::ostream& operator<<(std::ostream& out, member const& x) - { - out<<"Test class("<()()); - } -} - -#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) -namespace test -#else -namespace boost -#endif -{ - std::size_t hash_value(test::member const& x) - { - return x.value; - } -} diff --git a/test/helpers/member.hpp b/test/helpers/member.hpp deleted file mode 100644 index 2ea51c11..00000000 --- a/test/helpers/member.hpp +++ /dev/null @@ -1,40 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_MEMBER_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_MEMBER_HEADER - -#include -#include - -namespace test -{ - struct member - { - int value; - - explicit member(int x = 0); - member(member const&); - member& operator=(member const&); - ~member(); - - bool operator==(member const&) const; - bool operator<(member const&) const; - }; - - std::ostream& operator<<(std::ostream&, member const&); - test::member generate(test::member const*); -} - -#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) -namespace test -#else -namespace boost -#endif -{ - std::size_t hash_value(test::member const& x); -} - -#endif diff --git a/test/helpers/metafunctions.hpp b/test/helpers/metafunctions.hpp deleted file mode 100644 index 26702abb..00000000 --- a/test/helpers/metafunctions.hpp +++ /dev/null @@ -1,100 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER - -#include -#include -#include -#include -#include - -namespace test -{ - struct unordered_set_type { char x[100]; }; - struct unordered_multiset_type { char x[200]; }; - struct unordered_map_type { char x[300]; }; - struct unordered_multimap_type { char x[400]; }; - - template - unordered_set_type container_type( - boost::unordered_set const*); - template - unordered_multiset_type container_type( - boost::unordered_multiset const*); - template - unordered_map_type container_type( - boost::unordered_map const*); - template - unordered_multimap_type container_type( - boost::unordered_multimap const*); - - template - struct is_set - { - BOOST_STATIC_CONSTANT(bool, value = - sizeof(container_type((Container const*)0)) - == sizeof(unordered_set_type) || - sizeof(container_type((Container const*)0)) - == sizeof(unordered_multiset_type) - ); - }; - - template - struct is_map - { - BOOST_STATIC_CONSTANT(bool, value = - sizeof(container_type((Container const*)0)) - == sizeof(unordered_map_type) || - sizeof(container_type((Container const*)0)) - == sizeof(unordered_multimap_type) - ); - }; - - template - struct has_unique_keys - { - BOOST_STATIC_CONSTANT(bool, value = - sizeof(container_type((Container const*)0)) - == sizeof(unordered_set_type) || - sizeof(container_type((Container const*)0)) - == sizeof(unordered_map_type) - ); - }; - - template - struct has_equivalent_keys - { - BOOST_STATIC_CONSTANT(bool, value = - sizeof(container_type((Container const*)0)) - == sizeof(unordered_multiset_type) || - sizeof(container_type((Container const*)0)) - == sizeof(unordered_multimap_type) - ); - }; - - // Non Const Value Type - - template - struct map_non_const_value_type - { - typedef std::pair< - typename Container::key_type, - typename Container::mapped_type> type; - }; - - - template - struct non_const_value_type - : boost::mpl::eval_if, - map_non_const_value_type, - boost::mpl::identity > - { - }; -} - -#endif - diff --git a/test/helpers/random_values.hpp b/test/helpers/random_values.hpp deleted file mode 100644 index 4ba605c3..00000000 --- a/test/helpers/random_values.hpp +++ /dev/null @@ -1,203 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER - -#include "./generators.hpp" -#include "./metafunctions.hpp" -#include -#include -#include - -namespace test -{ - template - struct accessors - { - // get_key - // - // Given either the value_type or the key_type returns the key. - static typename Container::key_type const& - get_key(typename Container::key_type const& x) - { - return x; - } - - template - static typename Container::key_type const& - get_key(std::pair const& x) - { - return x.first; - } - - static typename Container::value_type const& - get_mapped(typename Container::key_type const& x) - { - return x; - } - - template - static M const& - get_mapped(std::pair const& x) - { - return x.second; - } - }; - - template - struct random_values : public accessors - { - typedef accessors base; - - typedef typename non_const_value_type::type value_type; - typedef typename Container::key_type key_type; - - std::vector values_; - typedef typename std::vector::iterator iterator; - typedef typename std::vector::const_iterator const_iterator; - - explicit random_values(std::size_t count) - { - values_.reserve(count); - std::generate_n(std::back_inserter(values_), - count, test::generator()); - } - - iterator begin() { return values_.begin(); } - iterator end() { return values_.end(); } - const_iterator begin() const { return values_.begin(); } - const_iterator end() const { return values_.end(); } - - value_type const& operator[](std::size_t i) const { return values_[i]; } - - struct key_matcher0 - { - template - bool operator()(X const& x, Y const& y) const - { - return base::get_key(x) == base::get_key(y); - } - }; - - // No, I don't know why didn't I just use bind. - struct key_matcher1 - { - key_type x; - - key_matcher1(key_type const& x) : x(x) {} - - bool operator()(key_type const& y) - { - return x == y; - } - - template - bool operator()(std::pair const& y) - { - return x == y.first; - } - }; - - static key_matcher0 key_match() - { - return key_matcher0(); - } - - static key_matcher1 key_match(key_type const& x) - { - return key_matcher1(x); - } - - template - static key_matcher1 key_match(std::pair const& x) - { - return key_matcher1(x.first); - } - - template - iterator find(K const& x) - { - return std::find_if(values_.begin(), values_.end(), key_match(x)); - } - - template - std::size_t count(K const& x) - { - return std::count_if(values_.begin(), values_.end(), - key_match(x)); - } - - template - std::size_t key_count(K const& x) - { - if(has_unique_keys::value) - return find(x) != values_.end(); - else - return count(x); - } - - static bool is_unique() - { - return has_unique_keys::value; - } - }; - - template - struct sorted_random_values : public random_values - { - typedef random_values base; - typedef typename base::value_type value_type; - typedef typename base::key_type key_type; - typedef typename base::iterator iterator; - typedef typename base::const_iterator const_iterator; - - explicit sorted_random_values(std::size_t count) - : base(count) - { - std::stable_sort(this->begin(), this->end()); - } - - struct key_compare0 - { - template - bool operator()(X const& x, Y const& y) const - { - return base::get_key(x) < base::get_key(y); - } - }; - - static key_compare0 key_compare() - { - return key_compare0(); - } - - template - iterator find(K const& x) - { - iterator pos = std::lower_bound(this->begin(), this->end(), x, key_compare()); - return this->key_match()(x, *pos) ? pos : this->end(); - } - - template - std::size_t count(K const& x) - { - std::pair range = - std::equal_range(this->begin(), this->end(), x, key_compare()); - return range.second - range.first; - } - - template - std::size_t key_count(K const& x) - { - if(has_unique_keys::value) - return find(x) != this->end(); - else - return count(x); - } - }; -} - -#endif diff --git a/test/helpers/strong.cpp b/test/helpers/strong.cpp deleted file mode 100644 index 0290f42f..00000000 --- a/test/helpers/strong.cpp +++ /dev/null @@ -1,32 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#include "./strong.hpp" -#include - -namespace test -{ - strong_tester::strong_tester() : dismissed_(false) {} - strong_tester::~strong_tester() { BOOST_CHECK(dismissed_); } - void strong_tester::dismiss() const { dismissed_ = true; } - bool strong_tester::is_dismissed() const { return dismissed_; } - void strong_tester::call_test() { - if(!is_dismissed()) - { - DEACTIVATE_EXCEPTIONS; - try { - test(); - } catch(...) { - BOOST_ERROR("Exception thrown in strong test."); - } - dismissed_ = true; - } - } - - strong_test_holder::strong_test_holder(strong_tester_ptr const& x) : ptr_(x) {} - strong_test_holder::~strong_test_holder() { ptr_->call_test(); } - bool strong_test_holder::is_dismissed() const { return ptr_->is_dismissed(); } - void strong_test_holder::dismiss() { ptr_->dismiss(); } -} diff --git a/test/helpers/strong.hpp b/test/helpers/strong.hpp deleted file mode 100644 index c90c80e8..00000000 --- a/test/helpers/strong.hpp +++ /dev/null @@ -1,78 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER - -#include -#include "./exception_trigger.hpp" - -namespace test -{ - class strong_tester - { - mutable bool dismissed_; - protected: - strong_tester(); - public: - virtual ~strong_tester(); - void dismiss() const; - bool is_dismissed() const; - void call_test(); - virtual void test() = 0; - }; - - template - class default_strong_tester - : public strong_tester - { - T const& reference; - T copy; - public: - default_strong_tester(T const& x) : reference(x), copy(x) {} - - void test() - { - BOOST_CHECK(reference == copy); - } - }; - - typedef boost::shared_ptr strong_tester_ptr; - - //template - //strong_tester_ptr create_tester_impl(T const& x, float) - //{ - // return strong_tester_ptr(new default_strong_tester(x)); - //} - - template - strong_tester_ptr create_tester(T const& x) - { - DEACTIVATE_EXCEPTIONS; - return create_tester_impl(x, 0); - } - - class strong_test_holder - { - strong_tester_ptr ptr_; - public: - strong_test_holder(strong_tester_ptr const&); - ~strong_test_holder(); - bool is_dismissed() const; - void dismiss(); - private: - strong_test_holder(strong_test_holder const&); - strong_test_holder& operator=(strong_test_holder const&); - }; -} - -#define STRONG_TEST_ANON(x) \ - STRONG_TEST(BOOST_PP_CAT(STRONG_TEST_tester, __LINE__), x) - -#define STRONG_TEST(tester, x) \ - for(::test::strong_test_holder tester(::test::create_tester(x)); \ - !tester.is_dismissed(); tester.dismiss()) - -#endif diff --git a/test/helpers/unit_test.hpp b/test/helpers/unit_test.hpp deleted file mode 100644 index 3fe6efee..00000000 --- a/test/helpers/unit_test.hpp +++ /dev/null @@ -1,77 +0,0 @@ - -// Copyright Daniel James 2005. Use, modification, and distribution are -// 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) - -#if !defined(BOOST_UNORDERED_TEST_HELPERS_AUTO_UNIT_TEST_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_AUTO_UNIT_TEST_HEADER - -#include "./base.hpp" -#include -#include -#include -#include -#include - - -#define AUTO_UNIT_TEST(name) \ - AUTO_UNIT_TEST2(name, BOOST_PP_CAT(name##_, impl)) - -#define AUTO_UNIT_TEST2(name, impl_name) \ - void impl_name(); \ - BOOST_AUTO_UNIT_TEST(name) \ - { \ - impl_name(); \ - ::test::end(); \ - } \ - void impl_name() - -#define AUTO_TEMPLATE_TEST(name, T, type_seq) \ - AUTO_TEMPLATE_TEST2(name, BOOST_PP_CAT(name##_, impl), T, type_seq) - -#define AUTO_TEMPLATE_TEST2(name, impl_name, T, type_seq) \ - template \ - void impl_name(); \ - BOOST_PP_SEQ_FOR_EACH(AUTO_TEMPLATE_TEST_OP, name, type_seq) \ - template \ - void impl_name() - -#define AUTO_TEMPLATE_TEST_OP(r, name, type) \ - static boost::unit_test::ut_detail::auto_unit_test_registrar \ - BOOST_PP_CAT(test_registrar_##name##_, type) \ - ( BOOST_TEST_CASE( BOOST_PP_CAT(name##_, impl) ) ); - -#define META_FUNC_TEST_CASE(name, T) \ - META_FUNC_TEST_CASE2(name, T, BOOST_PP_CAT(name##_, impl)) - -#define META_FUNC_TEST_CASE2(name, T, impl_name) \ - template \ - void impl_name(T* = 0); \ - template \ - void name(T* x = 0) { \ - impl_name(x); \ - ::test::end(); \ - } \ - template \ - void impl_name(T*) - -#define RUN_TEST_OP(r, product) \ - RUN_TEST_OP2( \ - BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \ - BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(1, product)) \ - ), \ - BOOST_PP_SEQ_ELEM(0, product), \ - BOOST_PP_SEQ_ELEM(1, product) \ - ) - -#define RUN_TEST_OP2(name, test_func, type) \ - BOOST_AUTO_UNIT_TEST(name) \ - { \ - test_func((type*) 0); \ - ::test::end(); \ - } - -#define AUTO_META_TESTS(test_seq, param_seq) \ - BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_TEST_OP, (test_seq)(param_seq)) - -#endif From d0ff815c458edb8076320fe13d0f8d697c7773de Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 7 Mar 2006 21:32:20 +0000 Subject: [PATCH 014/175] Starting to write an implementation rationale for the unordered containers, and added a warning about the quality of the documentation (i.e. that you shouldn't read it yet). [SVN r2879] --- doc/rationale.qbk | 32 ++++++++++++++++++++++++++++++++ doc/unordered.qbk | 6 ++++++ 2 files changed, 38 insertions(+) create mode 100644 doc/rationale.qbk diff --git a/doc/rationale.qbk b/doc/rationale.qbk new file mode 100644 index 00000000..7ba0554b --- /dev/null +++ b/doc/rationale.qbk @@ -0,0 +1,32 @@ +[def __wang__ + [@http://www.concentric.net/~Ttwang/tech/inthash.htm + Thomas Wang's article on integer hash functions]] + +[section:rationale Implementation Rationale] + +From the start the intent of this library was to implement the unordred +containers in TR1, so the interface was fixed. But there are still some +implementation desicions to make. The priorities for the library are +conformance to the standard and portability. + +[section Number of Buckets] + +There are two popular methods for choosing the number of buckets in a hash +table. One is to have a prime number of buckets. This allows .... (TODO) + +The other is to always use a power of two. This has a potential efficiency +advantage, since it avoids the costly modulus calculation. It also allows for ... (TODO) + +For a power of two hash table to work the hash values need to be +evenly distributed for the subset of the bits it is going to use - and since +the container can take an arbitrary hash function it must do this itself. +For some methods for doing this see __wang__ (TODO: Other references?). +Unfortunately, the most effective methods require the input to be an integer +with a certain number of bits, while ``std::size_t`` can have an arbitrary +range. This leaves the more expensive methods, such as Knuth's Multiplicative +Method which don't tend to work as well as taking the modulous of a prime, +have little efficiency advantage and don't work well for (TODO: what are they called?). + +[endsect] + +[endsect] diff --git a/doc/unordered.qbk b/doc/unordered.qbk index 5470b07d..fb88e703 100644 --- a/doc/unordered.qbk +++ b/doc/unordered.qbk @@ -12,7 +12,13 @@ ] ] +[section Warning] +This documentation is a work in progress, and is often incomplete, incoherent +and, worst of all, incorrect. Don't take anything in it seriously. +[endsect] + [include:unordered intro.qbk] [include:unordered comparison.qbk] +[include:unordered rationale.qbk] [xinclude ref.xml] From 432527dd2a4663d973dd5e57957c0792d0299feb Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 14 Mar 2006 22:41:39 +0000 Subject: [PATCH 015/175] Some work on the unordered containers documentation, fixed some mistakes and added a description of the data structure and its behaviour. [SVN r2882] --- doc/buckets.qbk | 144 +++++++++++++++++++++++++++++++++++++++ doc/comparison.qbk | 19 +++--- doc/diagrams/buckets.dia | Bin 0 -> 1858 bytes doc/diagrams/buckets.png | Bin 0 -> 13856 bytes doc/intro.qbk | 83 ++++++++++++++++------ doc/ref.xml | 93 +++++++++++++------------ doc/unordered.qbk | 1 + 7 files changed, 270 insertions(+), 70 deletions(-) create mode 100644 doc/buckets.qbk create mode 100644 doc/diagrams/buckets.dia create mode 100644 doc/diagrams/buckets.png diff --git a/doc/buckets.qbk b/doc/buckets.qbk new file mode 100644 index 00000000..7fe0f95f --- /dev/null +++ b/doc/buckets.qbk @@ -0,0 +1,144 @@ +[section:buckets The Data Structure] + +The containers are made up of a number of 'buckets', each of which can contain +any number of elements. For example, the following +diagram shows an [classref boost::unordered_set unordered_set] with 7 +buckets containing 5 elements, `A`, `B`, `C`, `D` and `E` +(this is just for illustrations, the containers have more buckets, even when +empty). + +[$../diagrams/buckets.png] + +In order to decide which bucket to place an element in, the container +applies `Hash` to the element (for maps it applies it to the element's `Key` +part). This gives a `std::size_t`. `std::size_t` has a much greater range of +values then the number of buckets, so that container applies another +transformation to that value to choose a bucket (in the case of +[classref boost::unordered_set] this is just the modulous of the number of +buckets). + +If at a later date the container wants to find an element in the container +it just has to apply the same process to the element (or key for maps) to +discover which bucket to find it in. This means that you only have to look at +the elements within a bucket when searching, and if the hash function has +worked well an evenly distributed the elements among the buckets, this should +be a small number. + +You can see in the diagram that `A` & `D` have been placed in the same bucket. +This means that when looking in this bucket, up to 2 comparison have to be +made, making searching slower. This is known as a collision. To keep things +fast we try to keep these to a minimum. + +[table Methods for Accessing Buckets + [[Method] [Description]] + + [ + [``size_type bucket_count() const``] + [The number of buckets.] + ] + [ + [``size_type max_bucket_count() const``] + [An upper bound on the number of buckets.] + ] + [ + [``size_type bucket_size(size_type n) const``] + [The number of elements in bucket `n`.] + ] + [ + [`` + local_iterator begin(size_type n); + local_iterator end(size_type n); + const_local_iterator begin(size_type n) const; + const_local_iterator end(size_type n) const; + ``] + [Return begin and end iterators for bucket `n`.] + ] +] + +[h2 Controlling the number of buckets] + +As more elements are added to an unordered associative container, the number +of elements in the buckets will increase causing performance to get worse. To +combat this the containers increase the bucket count as elements are inserted. + +The standard gives you two methods to influence the bucket count. First you can +specify the minimum number of buckets in the constructor, and later, by calling +`rehash`. + +The other method is the `max_load_factor` member function. This lets you +/hint/ at the maximum load that the buckets should hold. +The 'load factor' is the average number of elements per bucket, +the container tries to keep this below the maximum load factor, which is +initially set to 1.0. +`max_load_factor` tells the container to change the maximum load factor, +using your supplied hint as a suggestion. + +TR1 doesn't actually require the container to pay much attention to this +value. The only time the load factor is required to be less than the maximum +is following a call to `rehash`. + +It is not specified anywhere how other member functions affect the bucket count. +But most implementations will invalidate the iterators whenever they change +the bucket count - which is only allowed when an +`insert` causes the load factor to be more than or equal to the maximum. +But it is possible to implement the containers such that the iterators are +never invalidated. + +(TODO: This might not be right. I'm not sure what is allowed for +std::unordered_set and std::unordered_map when insert is called with enough +elements to exceed the maximum, but the maximum isn't exceeded because +the elements are already in the container) + +This all sounds quite gloomy, but it's not that bad. Most implementations +will probably respect the maximum load factor hint. This implementation +certainly does. + +[table Methods for Controlling Bucket Size + [[Method] [Description]] + + [ + [``float load_factor() const``] + [The average number of elements per bucket.] + ] + [ + [``float max_load_factor() const``] + [Returns the current maximum load factor.] + ] + [ + [``float max_load_factor(float z)``] + [Changes the container's maximum load factor, using `z` as a hint.] + ] + [ + [``void rehash(size_type n)``] + [Changes the number of buckets so that there at least n buckets, and + so that the load factor is less than the maximum load factor.] + ] + +] + +[h2 Rehash Techniques] + +If the container has a load factor much smaller than the maximum, `rehash` +might decrease the number of buckets, reducing the memory usage. This isn't +guaranteed by the standard but this implementation will do it. + +When inserting many elements, it is a good idea to first call `rehash` to +make sure you have enough buckets. This will get the expensive rehashing out +of the way and let you store iterators, safe in the knowledge that they +won't be invalidated. If you are inserting `n` elements into container `x`, +you could first call: + + x.rehash((x.size() + n) / x.max_load_factor() + 1); + +If you want to stop the table from ever rehashing due to an insert, you can +set the maximum load factor to infinity (or perhaps a load factor that it'll +never reach - say `x.max_size()`. As you can only give a 'hint' for the maximum +load factor, this isn't guaranteed to work. But again, it'll work in this +implementation. + +If you do this and want to make the container rehash, `rehash` will still work. +But be careful that you only ever call it with a sufficient number of buckets +- otherwise it's very likely that the container will decrease the bucket +count to an overly small amount. + +[endsect] diff --git a/doc/comparison.qbk b/doc/comparison.qbk index b60c5ada..b9cbc562 100644 --- a/doc/comparison.qbk +++ b/doc/comparison.qbk @@ -27,20 +27,25 @@ might output: two,2 one,1 three,3 + missing,0 -while if `std::set` was used it would be ordered lexicographically. +or the same elements in any other order. If `std::set` was used it would always +be ordered lexicographically. The containers automatically grow as more elements are inserted, this can cause the order to change and iterators to be invalidated (unlike associative containers whose iterators are only invalidated when their elements are erased). -So containers containing identical elements aren't guaranteed to -contain them in the same order. For this reason equality and inequality -operators (which in the STL are defined in terms of sequence equality) -aren't available. +In the STL, the comparion operators for containers are defined in terms +of comparing the sequence of elements. As the elements' order is unpredictable +this would be nonsensical, so the comparison operators aren't defined. -[section Equality Predicate and Hash Functions] + + +[endsect] + +[section Equality Predicates and Hash Functions] [/TODO: A better introduction to hash functions?] @@ -63,5 +68,3 @@ it. For example, if you wanted to use [endsect] - -[endsect] diff --git a/doc/diagrams/buckets.dia b/doc/diagrams/buckets.dia new file mode 100644 index 0000000000000000000000000000000000000000..f5e76e69d49815005a023fcbe27b1c322801fcf2 GIT binary patch literal 1858 zcmb2|=3oE==C^aBi*H#-w7vhVEm7s&sv@53|zV2act;o(fW@dTk zO@92zGK(JqOef?w2pXR~5>m5Xwf@rY2%&S&_5D9@_^vR?q+{`GJI5XOg)geiW4rif z*Z=SR_wB0w>YInpU*7oN^WSXMe|yyJ1(&{hv8#T5YPbL4hmRjCbIlL7?-mwe^PbZ6 zaDSb|FXzaN8~b;@`fA4P^Y={n`>F4LMNVURoT^qe(O&%e-;MS^ExjM5n!FWXda7u$ zMbG4W&GK>I?e9EY|8E12@tv;EUT<3Mg&({8u)pn`{PWSD2^Y90h8#X8UMO`!>E`D} zVM|o|e(elgrZe@F)5SN>4u}7CF7$dT{W`)|{g&3Gvwu^>TuNncnk8No5UHFJbW?)w zyG5FrvVQKS{dXk7^jfBRCp>NXQYNS#`G`F-wn*mq!Vim`OueT)DwtdHsNLvj-0EwB zTX%48(z#doue@%?zNJh@Z?C?$E&u#n)q`KY_U#T`q+$|GZAwwAZGCJXa-`Q+&(LjN=~2GqX4%YX9^X?p zK3o^Jep+?*VUhgMGgi|%{-#fy@^Js!++E>u4~mZ5%xJCn(?266_3OV=e==UFU%q$z z-u53W_b=l+DRc14uK()y^5$i=rWja$x_T>JaQ!3>{)nx+z8+kp`=uhLcg~68?~5Zf zx?ac|9y)ix^^Rnq`(LlzpPvmJ1-pK}Rq-3v$)?SmF zBUACnQ6>6G*=~NbFHTq4k2XG^fA4tzcCHCKZ#&Me{#`Klh4jqzP5Rr;CGX$oP{zgl z^{cO7wrsBxGB%(Q_2w-2FDgVf|{HeJ~E_0`B%~r0r0dnq(TR`y z^62pX?hlSB2anv(Id=Hm;)SnGO5}TN_Wa${KYiV@r_w>64Hm9CzSl^$@#Dv$PjPu- zXI*c96}?y#x9yc&RIkg*;)b8f9WI9%l3yCSHznSgxc;u7{L{Y47F~I+CTFuZ#=m`~ zdS~$~{m-eJEI!q(W1QP2T6XDB(6+vkLms@W9A+f3EjuT^Oh)$PYsLdp!VgT5 zd89XO*@}gl6|+9^_|BcSEXMX5hePnYu4P{JEQd9>2`}fUHMcps<-vAVh7cp)Fqfx} ze*TecE6<6qw9);U&1^7LM03HZy)i2`@q`?74mGvr3Uv$?y`D0OAtH2#%KZY*9hq9$ z<`I?e+17Aen`9!>v}TWJk$GMb6ca6x2O^O~iT4--k?VHTK4A*~Aq!H{mP4 z^cE}KD8W@H)f8`>iryIda&ke=(eh2ATkrEnXGWS#n?B)!OqoTomA@Q!;r5Wl=Q@6& z5*B@yR&%E|T4~=d2{Zlans{T))@@%G#rPkN5m}uXIVo%j7e{st_q8h}5-Mw7u3B?& zYhR)7`A3$FPq%VevgRDwRghH7wQ+8O=Ol>~Yw48FTcmWDO(V9YZfLl0E$)8XrgPRu z%xtso>1k}!oXM59m}SQ8n8Uf^ALqK+dVl7bcKe3e_K)1p&C+(fWw~>VXZu|4?SYG~ zCQ7b3yh*pcmBsw~+XrqZ|4ZK4*Pgh+Lps>|@*=AuiL>E5bGQFKGbw9Jh_cqkJ2A7B zj*EPOA=*AKHeWFXj>0xZ&xln8_3F)%LCbVo6vf2JwU2;qM@{e~me~F3x?IYvun;s#NVB~5wfBBIWlkHg#Ze-X8J$MogJ+zT{d+x*hyW#y;3=^Ot{lz$bmY||wEh*XGD~w#UtC%J{%WxQ>`t>=!_oacB4Q`3wvS44y8IAr*7p-mP@-{rTVi!}qnftD?6k=1y~&=E;?; zzS!`lQngyJak6wur=y|CqD+@*cE%jZ-kaRAW&~xr3F&SVFm(0Y;(6EWds%(`oSDC0 zXYaiGuIk;(pFfVjwR&EA?|G>0`B)A3n84g5rR4_0cb0S`8 zlJ{mK)72au{+n)Y4R}7yX|5=9X&6st+S9{&`li$8mVML_ydhqCMY`R-QTyiLD%fI zWaW3qj~SuQfBg6nxY+IGix)Fy%&4fU+E?>4=-%8wiKweT54CciJ9qBZty{;A9Wzcl zGa+q`@UDfSb2XV76HZJ}H1XkoDu0xlg{jdYATaRZvu9?-&wQ4Ccj=U!p#AGgBam+ve{qyJS+uPQzTlenFOylgVtd_S1Ujt%gN>+THU;odt>PrS+ z`{ix9wFN3I-MiddTP|L|9=|ckwXAI0n(skBUj(E(c~n+b zIyf9?WM)senYHColJ(Z}xY+ar(I) zS?jXz@9uVs>#Mb}u(GD6q@<*#PMtC(BQtYt)Yh&)=AQ&N_g`~e96eW9NQjM3M&qj9 zMWMB?VuFJ=*Z%%CQQ6&Vcla0Q$yUFA{?v`yvLbqW-lm({Gk<+|_MBBXe`)9GW%IkL zs;V?KHU0ejuKf0$En1hEnmTpHjEwa3?|#a&&GYYVOm_G2;n`}XxFq>_UtwXPZS^;m zmMK%FSXfx>srdNl?=<@xs+Bbi0o7er=2K_Rw5=tF6l4`P9CA@IXLF=+M)m?yjz9 z=jZFkoH>2^^zq}%H*DCjX;ab3Nvemw>+0$T2L*k5fB*mI^Y+2-&&;>4KR3s+va-@f zPG4Q!z2fX%-`Qq6ckb-ErF(0k^IT_V=kRqgGbc-_k{YI+ol0<>BE`QdZ`dxAWP%v;6(N&FSZ*q@+OE z=jP4G9Up#uef{?BTUi+ytMYeuRt7I$_O|53g@>P>o_6n(;pE{_X;~Aux9Y}*L_Iyd zUAuP8n>TN=n(rd9=#5FP_xIJ>|M0y1rFcf3{LxE`{pU{CkN@}O(;HC zb~#R4uJ+ZNXV1)Pf0f9}%DTHB-?VAdiWM4`Z#M;9SbolA`IF+0Yuk&?$8M8bu{mkM zxvqnAia#zXxdtjphpgoa+db}cMCeD=(lk&%&+(b3V7k-qco_FnzxaXEE+ zrA=H-O-)o(R9M)x!|nVl?>sEpDJCWs78bT`+crNxzr}97udc0~y{UTDAr_{CTeGkC z$=S}DGNt7IzrX#u$C5VM{rz$|aIu^3Y_qcm4?0e?Wf6$4to(U$vbwU8Qc!TP>;2UC zZ{NOs^XAQ?M~_~=UcGSP!VPkl4(|VcRa{imbHk?AR@RB{1l!u$+HTyq(b3Uy$|Nnp01qB}-AMamTzGcgnU*F%~ z-<}`O%*-q+E6dj&924{B&CSh!{`}DnUuR-w7Svt)@8|Q=)Ai+TtG2AN$y;~FYP;O2 zwZ)N zRgaHxWfkY%+7h`XhYiKg{p#!M^`@_0x9;7eM~_~;L@tc;JA;tL+Dl97(yoEq8O;*+~nSxt6UGj1LMjN<7r^FaBc9wqtup4E-srW3WL<5~OXj*6oJUUR#nUJuuD^cWy0~2>FCQFiUg=z2 z;a*p_Z}Q~J`)YsR$oaO+clM`GpQ5I`ZxP^Y-(2(a)9>%^&(AbozHnjU$45setNE^4 zyLM`DJEMz-zyJ2_+xUrJg!en-K>$^LeN z^LC|ZwYu$bP>9%=)Vh3my035V?%lr^yZ1jkH&^@M)I3|;y}HrcjvP4>5fKryyKJrh ze7l`HcYb=Y;Z@b2A0HnaY<_=#|NX7m*L!++>g(%EOG{7AGBPpY;pg}F@_JNYk$!$& zXECZQpU=C$ zPj_+NrHM|;buTY1jozMj_1ZN)et!GnXFV@2r=OLwulsX$w)yYx@Bg2-|NrIf?eEvt zMsIqxd{y-!{)m#tgp_sfj& z$i$g5Evvq~c=akvUw^&#biJ*)x5Iv!-QJ!*f8M-zuUlL z=yF6{T>Si8>*)eQQc_lRe=25fU%zl+;j1ev{pMQ5?ytLh|9NG`ddb3 zrmS_DkGHqC-0T;3cbBK6s3<-;E?=*sqci2W8iNKqzno3+vz~jRC< zzSl+5g>!Fhv8?~Mr>*VS!^7?W|NYMI-0c$GR~)0u%&TSXpa7om5wv_9r3H)Wqb>nKNyCvbzc& zADc8uNN{z(ynWrPD=V*Fy{a9yX2MrLhCi#6RYirAd4I2!krDvy2U%z67Mftlq zA5MRMe!jahzG3d%yR%HQS(qC8`}yaepT6Vp7nScF{@&i*y}hO;CN_nST4M57zPq~m z`uF?w=O2F*(}_^nzxG08N=iyx+`PGS?@m(nR*9H<{PEOj)8cj(Jv}a8|K{G_-#d;AIQ8%E@9wTH8Ch9dTic_N|88tdzH)fol({4yVF3XXIy+Cs*Z)0w!hVO# zjnjhJqf3&+i!#mlGMW~!L)pz!1A z^mqvsI zUin;?e}5krm(a#{dn!L)xpKvCo=s$6pknTO#((RdJb99Se%{SnxBTW<1kQQ-{_gJb z$H%ywI+iVa)+wyMV*bN3XM6- z{`q0mm$dWq)~;Ju_v_2cO`D94=1f#OH_z6aW2Rwp+mXYT8Ch9nZ*N^afByaT`1+;y z7S0p-Kf`aXRqe;4;#;?FJ>D-rJ*S&NURYMvmt&?`?yMQ@9}aP^&CAQ1Yh5l@XJE3I zL3`G`dH2rDH0D%1-Y1(~Sf1&#Ptd=1zMAhWmFsIwQj?P(KYA1t9{&Bfe7#1_VlQ<8 zjuk=MOFEkPXBnk(eSLIwb@-y`OZIJEWY5W<;vl=J6p(#XeE3@}S^g(Vc-$t3EtnR9q6iKF(-odgrg^1_7gl0}L0vZ{5BvEiE0) zZ0Ef{mVxW*=bZd}{b!#JonEy$?d+m=rUAjh!hNDT|6Id`g(tteyZidJYep}RWm~%0 ze60=oqwx6eudkOcTsU#^Wa1_FjTu)oa)SIiuU@$D;m60v6DA0}nq=C2G-_YX&KIE( zI@8v!)ty+fA-I7*>*D$1$jF%=*y3O5oV8w`oSb}gzfpQ_t}ln+-(W?c&l4!(TtTG`W6Q)Be5-?;JP#6)Ffx1Nk`tXbdY zTno+$Xqc`aKk0v)b$(b%ib|=$_5Jn#m$Cng-CZVYk|Ch@^`c2!+}!Es=iOcG&R;k$ z=IQ63OpOb6?TTWQWv)0r*SZ|kQMi7;{{LU=`Y+taozI>-x3Bnlp9NRZ&KM_;%ga+j zW#1~rJ$gQGR@L8MTOAZKY=hSZAAc{@v+3rRZ8diL7cElK)y+LQN%hsMSN`*C3JVKQ zosm+9;BNvnLyc(fX;3SOR_ZGKiy^Iz`!Bt6f$MJbogZx9h2&X1e!`@3lf} zi=Urs5y*O#*tv4$%B`7~(+)H+-rk;ne`7Mc;*w39J}sYLC$v}W>))m4dKgc*TwfP^ zvR?eN%Y%;D^tWi}ivaZr!?d>*vp(UH|{!)L&Hh_v>|e=_-F;-?y*V@2^Y$ z*}t>$bDFPj@7%d_XU>d-M*pjmTULj!*ZNV${VMVF!De>tqQ$3Dv$9@2cn}a9Tl?~o z>P(Z0A0HB5x$Z7|>!Rak`^wign@vIc)vH%}dV0ORz3J!X{JdebK5nm&+|>Q``o6)3 z{`Yow3)kNJ@#*R5mO}gUMLiYL($dw{zc+2#wEy2P@4DzpdC_8?-*%?mQfHliB&=d# zVR8%&4XvvBwJG&ON;^OeUB+P6t<=#(;Ka%h|Nx6-cai$>|`I}Z*vzkTz@#UwR5 z+gtX7sfkI4U3vYB3yPoadzkhp-nY(L@qThg|D*4oX0Im)I659&>OFnS)~%A?YO};l zx7<59S$*a1eMhcr*^rf$)z;Q_FGIh#wR%VGN-Vb$Nl<- zWorKO)+}5I8jbkz@iD)i!lSYu-#MoX7Os1CYO1!SrRB3{&pyql3F&+y{qf3!Iqm%N zV#RH`FW=tY-hckN|2&(WUt_YDT3A@{NE$6!v`9%u$48B;?DaKWOUs*=E`3Vo{`0>6 zzj8}OWo2e|_UV*v2B)^Rw#{j0AAPKlpS|V5v$M0WuwK2nIolI8Z`-u|Bj4ZLynOZQ*R!+DLw-J4yJ}U|^>wi=6DmJH(@ZEXah5I2 zzqe=SvSn^fS0i<%&6^i@eO>Hw-`Qf4l9kWT$+EMz_v_A?IkWP~3Bl6R(q-qO`MsXM ze}6u0Grz3Wm-F`jPgF;Dc6O$pn{)Bv#f8s|l7)8K|8jZi7ZEYz1-s;|xoPPK7Ko0Yr>kbK{!_2W^u{;#jE!{g%q9p<<1i2KysnwyhT^ZD7?!|nY2^7ii@ zK2&@Z;ie#<6SZZ<#*G)RU*8_Q+;7u8eg-FwmzS2Fz8+tH_W9@Zv$IsMM{bsul3Kwz z|N7eK>-%b}f985(&ma_Nv?!M7Ia0%2}SQoqd+Ct}c`T9SBbKR;c0X*I zUhJ}k`kU0m$Gb<36~8#$i_xg422dv<$!yRcJDZEdTv_2~d- z=fhJpga7?}KL5PkZ=Wwcm)16}U;n;CP&saIm1)6)18MFvGFN6_U*|j1Xz9j{g_oE4 z&aeNsGe?@eVt#k`>04W~D{bb@o;~|T`dZb3KR*gziJaY6`}@e8M6ahB84^b2<=?Na z4mUS3@tJECx^4ck7`^Lv@7k)W9zAqO$;wKqynI8%n$OSAgA&5a%gdGB`waavytMS|*RSv1z4P?++>}*zdt0t?`Z*B}mh9{6 zMEhYCba#JS}>DW@hoDBb+n82s`*J zT)1%Fym?!gi`h}Ia7y#vu+Y%juUEsbe6Ee&mcz-zGbPacrN_c%&iwoPYJYxu zT3T9K{QTTplgvpe-@kFrwXfgjKi}@<%a?PlN>80QAyC*Juf1gb`uTI_{J9z)9~vHB zT~Sf<^=i0~{JHx(i_;G@Fg7+eGBqkFC@9$2#2lRbktyJMUD?}POV_Ubdpm!>rG>>D z>vF$M%g)WSy}i_X`iT=Bva)kQZPlAMwOy+}Kl2R?ytvrCKkx1?)x-N|q%XA5SX)a=${^vu+1chx=WLrY@ynMlN=iyA z?suIy;i0X)`oV(-w{As!j6QMd)GW*5Ws@gg?v*xQvv#e1%#ICpf2)3$N;qu!`T6<% zU8SqDudmy=bEmMnpNv(>i^5xGT9v(KCS^Dn& zM+TMIaWiMd1U5x0dV6`P`OKIwZ=N5=#?6~&o9EBlzJ2?~jfI=Fmn~bCcYmL)mDQ~C z&tJcMnR{o)#pB1_eS6DdMzr4&>T2@vzHumnKMT^SYj>(wi+&DE=`*@G!S8w{G3KZQIHc6CXmy zx@OF%`1hwW%Pzuf_S=^)ch>!_;#6A_Cbrc5{O9NA&GYW;D1RUKFtC5uu3sM>9=4R} z>+9odKdh;zx6Dh|=2HIUWoPGD7K`b}=_E-o?+UD%8_l`&`pn~x-`?Jy|Nh?IL!Ey) z6mQ+S<>uzLYu7FrzU526;H6eyU*Eoc`z|l{&wnYvntyN4&0VFhudR(<$*J@8>sN8T zm={k#;L6V8=gU6cy>X-B`MJ4MrcC+qixr@z0y-`>{NR$Ke^+=YA*I$_8AWZ%Ah zd%R!XKJSjj$-a|>+pSr*?pe{!7`^L<4jnpm>eRJs*G`_y zys{#2RrR^KcKd~#xa$A^E%){1ntpoi%9WY7x2@f}b?e-@bG`0YcpbJf7P5UE6CC{c z*Vos#ZrytID$Cf|STld)q&c*3@KW zW%>B{2nh+v$jTm#pKB?2zT(TDsR?pdA`KBllDWhAtOdgZWdYq@<-yn>cY})z_@d zPjdw>?W_ImJzWnpf3$90-m5Dsuf$k&HFmHw{IIvNu}Mlw0=Yt1IQ8p+-d($@A|h_A z3SG^@)VO`Sx#|BEObU+}8UF3RySse8UG1#{2NX0lSC+rOw<*`g!lL5lrqulV`{tTt zUON7+e1fiv4a0%`Ma9J{pT~ZGd;9xI_4y}~g%uSQ2va}UK&n6t*fE& z!(kj?Ee4xT+S9K5EL97y)lVZG3ER`+fBDx`SU&2tz~9d(QOBXM0+_p{5H0#PT-`R5Zb$c=|t7VyNd47Jre%ziP$=ZJN?e1Q< z@Zr_f)k=KdmV#ZKetw>6i-%c6L~xGT?fduZe}8*>WxBfmyqX^$9Ifkg+d-m*U=a-)~V+N0;k&7DFuOB}GCLiQtVG>NgJ$LS0PQ{fgR~p@X!LIm- zalzKz8ylZKeR^q5*YU@!tgO1RyQcgq`t{}Iwr$(wqi0mDn_9)-B-4ivu4%*`FQ;9?((HMr!HK$aR0u&wzl_|$D1BLe8|+8 zaI8nNt*tF6FOUT^NhV-yy!q$n=kM?AG&V2*4PQK3zhc?4Ws4UlCna6lnC#BZ&Yqs0 z9v&Y4aPeCC&wqXvXBkcS`T4o`)%VXpg$0LV%F|O*A3l7j?msVPTh7e}MrOMmJ!|Cp ze?4rMzqhB-IP;Q9)|teg2Ro%07@RmZZrBj9EoY{K!iO(kL=1Os`w|osq@=7|T3YJt z?EG}QD3_8P!-IH6M#jFrzJ7W8dFJ`^=9qo_@$vD=$?C#R*VaTT%k&1-f!1XFzjWn_ zP1TnbeSLl@DNkmZW~bcy_xk$!{eM24-n7Zc+H?_zh5r<8|D1``T685`^B>ZaZZy$Eu>yg}WcA8XdEk+ZF`ss486;>C-% zZ~uOIdAV+uqk_PZW5?>g-Au2m0~K_u!`6CvdM;d686*L+to`eYi;u6Zjc#mg%*x7Y z=ab#EdGlIEWyCgarDxq zPwnz`KOVNr$L+0}I&IptxpV(MIM{5Sf3IL?jNbI@yu7^J+}!N!*AE^Tq?`~qdPn#s zXeo}v=@~O;?%cWaWT`+xeV|o6-m4N% zFfcUCTakF8K!ltHK7^*Mz1f6+ubd{V?k6H9rnOsqRm!U?E5pLVbYrfsqPTm(xh}=XxuM~!yDPf3Z{ObA+qH@^`1F>tDWf>EFNK`Ko1Z&t3#SUuJ)1x!>Gb*5&JjtPk^a zRGfVI>Xlx^1_gcn{$HQwCJR5D&UJdazPYinv5^s1?edAz=6MlOQSa{ToGftY`RBiX z{@mJ@yL$C%>*8l;X3dgHv_50)AuccbN=86nL*3tBo|DyHzIqiE9W8HB;Bc*aWlhP7 z;^5il`Ek2SR_c6P6Y6}#yYKYVcel2#j^AImNSvF4V@LJ(ys)rqlhys_+11|qvj5y1 zOXIRPH}38(*N@vXLqJGGWXi;e6Q@oM4GLPcbm`Wtt6EMRr8#aw6w@Ml)ZjSXXD~%bS~*x3;!! zslNJZ>!pcmOI2(bE?&Ou?dd7F_rt@(?ZU#sD-~C5&%du%W3Qr;a=cGgTtBX)sA$o> zoBQkQV`AnkU8<_6VqtOP;K7IW|9{Vaact#%VY~VBCr{p7^77Jj{rJ4w+k7wGT_pSK z%FZoYOw7%{pPOr)m6dgRn(ps^zuzxjv`FQi{;q}ob6d@FZeW#zYT-_8{I_+_PW`{A40a%XScXsD=SVlpMMdGqDv=caIr>ut%te(v7A zxIeKC{RcaxPQHBc;>iF9^8+{nysHn%DL@2{`lzJ254 zhKF&7hhJm=acx-H}zyEp9$!dY!Ya=!; z%D=x)-lnLey!`uz4;L<8l(eg<`2Ozh;X{X>Y&q@aIN$30JlorsE?qiz?%bh6i&m|A zbv?d*>SaA`*4kG-9GpBnF*}Qv+LUcB{k8oRcnM|uA)lE>s=B(mmX?yj3r#`Y_UZcZ zcBQYb9B5>Y-lg|S$L>m`;gXe`wHCc|KSaW6Mo=Gsltq^tr`~0JYEkkcz{{%(R7(E* z`7>x+gH<DL6EL`W6v6h&Dfx+bhjPcFo}uC6XBDypokEG+EW>+9>Yv$I#P zTGi9jvu4ejHEY-A=H%SDd-v^|H&gEVGTd@J!dz=x|L@O@jmd(7g68@6YW~;Hx2-Pw z{Vn&^*H>3pfBo_$?d+_rb$@@Ay}hNmZ8pP~)(&n5?AS5q zcD~Y*5*dqv1rsMuT(@rBzI}C(kvA_d_y7Fq)22J9Uqf;i>Zx=Di#&P4RU@m*0i-9v-XaPj<&Y4 z;@ZEb=4X+M%aMu7?u!SV=ozv6QJbC1ZimK|i+YlgxWKP za4X48d4I5(T~AL>R8&-0&Bw#TBjf(QyXUIT7NszDV(!SC!djngCk;lp6vYd^X+P@^77X4M6-b$6krq| zf8V`d?&i&#bFE6ff`fyfy=7-%@tJ3{bJM1xM@KqsCbMK74rV z)~)6K^RHdIHto1tgbrx#eC^t|&FuV#4jp>3k(1$;(~;&&94t+$-qQq}%<}HsSRJna zGq>Yx+WQ+Dn+1df1U@`E%KbB!qajb=;{ucP^YixB{@ylq>eWTA-A5w#Em;Cug!K4W z@9P^I7x(q?%|2^pWK>jC^y}a6_m3YxW@T#ZC_UaI`S{thu+Y%0si(yxOXVF{6&^We zHZ?VID6S4$d+E|8p}i@wv3FO6uFlEHsj01f{OA!E7uTy-uYT0jrKYBqm3@16clZ83 zpS-K9e-}SL*O>uYPNC8wV^y+Z-@drEYDS0jj*5W3J$v@_^ziVtf4*COzccRB)z#t7 zZ9F&kRBnEeaqjHdxfX?sI#_>wd;9vylaQ#Ws$X9+J*#*YGzolMAan8J#TJ3`^74z} zk*{CBKHeugd*)2f+M9c;zvtfGmYSYEed^SY6?@F{@7>v1oc{mc-^a&#gM+jGwO5{- zW7#r6Sl#bL5930g8on}s-7n%=z0aq8&q z?(R5x>hx)6XXlmI8yg${{rjg>bo1J^Z)av2FWP>4Ww1IoclV`Bm)QB`w(Pmiu!>!f z-zy*@A|g2W^14`SXJ==@ZdX^=tJkgxIqfQc|L*y7^~t{cG8PjACQY5HI{6wy2A?3m zmxHVeXc^j|J1{G@BBC{YkzI&I9wbYe0iz&^q}>B|Nb@4zo(P*aL=AS zo|Dx&@4a}x|GyuHqGfB^x&u zUR@Qsx9aPyoyE`BMsL^B)|Qr*UcGvCaZyp-|G(cqK0XePwfijo^K3jlJT6?k`19B6 z^{IP|8A@91!yPRnbgsR+x>{T>W=G=TwoYO7x;KUzxw&uc|NS_8_3GDWXJ@xesQ&&g zu$a+H!Ng?C#*Kxywq&}wxlQ^m?XdKLhTN2AyWj8IykWzR%FoMQSX$lMnqB_&RcL&C z{KftyUGu)aytX!aclrCY%{Nzvub;N(IKwJ7L4L2rOP6jfdmH6+;!rEMYd`n0#fvwW zzP{Gf)TA4|ZO5)%QPI)anVBnBu9TFLva+($i`eks(o%0rOUt~xJo)Uzi;G-q?CLKr zay{Eu%XUD;B9Qr%N%^}w!OQ(r)zs>~-z_&ZG+gAqHuL_zzn`9-o~|E%@6y^evAe&W znyUTw)>du~jtP6#GiY(Re_Y^`m6gTLFK1$A*4NuR^|I!a6)RROSfF4wTUJm|v8tkq z;Tfp#jf{$#XI0vDHZA|&o}bI-*GO->mt8Gbm*m>nNj%lRj9P|>@Q!w z$k|rq+}N;i*|KFzdpTH|9v|;7e|P8Sjg85#UcF-HlUa~=i!o!az()t=zrVllul~Mn z`SRzp&GWCVkH5byH@d#Q-cbGFW5}L@TU%fENE&NtX|?go*S)`ItFNzL`CSr}n;i73 zs;b=kWIR1RB@Gf7rk@5aOg($%3~09#KfYaqKu3ct5>hyxbb5X zE4Q3|T~11hh?LZo>eMa}uyXSF{q61AwQF;8bC35*o2Q+Txbuzg1D}h{1=-nW=NW>A{Eb`QU%7fU zG&FSP%$c2aQeq4O9~V?@*}ApWxSY9xW5*pXGWOIE>T;8}Q)ggI?OywaPviJP=XSmL zeQVytU5Kr#+c!mb!MR5cb48thS*>0aCiRh>UoHgRDgQAeO#CBgX3C;q!KUhkUOKwE zsu4(I2}yzfMMOn4x63myC^(CWiLp$NW@M1yA#3vzfgk~GsA6Eqc=VrfXWotMw;L3F OKq8*5elF{r5}E*QG*o5) literal 0 HcmV?d00001 diff --git a/doc/intro.qbk b/doc/intro.qbk index 8457b2ff..7addd14f 100644 --- a/doc/intro.qbk +++ b/doc/intro.qbk @@ -1,5 +1,7 @@ [def __tr1__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1745.pdf C++ Standard Library Technical Report]] +[def __hash-table__ [@http://en.wikipedia.org/wiki/Hash_table + hash table]] [def __hash-function__ [@http://en.wikipedia.org/wiki/Hash_function hash function]] @@ -7,28 +9,69 @@ For accessing data based on keys, the C++ standard library offers `std::set`, `std::map`, `std::multiset` and `std::multimap`. These are generally -implemented using balanced binary trees so accessing data by key is -consistently of logarithmic complexity. Which is generally okay, but not great. +implemented using balanced binary trees so lookup time has +logarithmic complexity. Which is generally okay, but in many cases a +__hash-table__ can perform better, as accessing data has constant complexity, +on average. The worst case complexity is linear, but that occurs rarely and +with some care, can be avoided. -Also, they require their elements to be ordered, and to supply a 'less than' -comparison object. For some data types this is impractacle, It might be slow -to calculate, or even impossible. +Also, the existing containers require a 'less than' comparison object +to order their elements. For some data types this is impracticle. +It might be slow to calculate, or even impossible. On the other hand, in a hash +table, then elements aren't ordered - but you need an equality function +and a hash function for the key. -So the __tr1__ provides unordered associative containers. These will store data -with no ordering, and typically allow for fast constant time access. Their -worst case complexity is linear, but this is generally rare, and with -care can be avoided. There are four containers to match the existing -associate containers: -[classref boost::unordered_set unordered_set], -[classref boost::unordered_map unordered_map], -[classref boost::unordered_multiset unordered_multiset] and -[classref boost::unordered_multimap unordered_multimap]. +So the __tr1__ provides the unordered associative containers, which are +implemented using hash tables. There are four containers to match the existing +associate containers. In the header <[headerref boost/unordered_set.hpp]>: -The fast lookup speeds are acheived using a __hash-function__. The basic idea -is that a function is called for the key value, which is used to index the -data. If this hash function is carefully chosen the different keys will usually -get different indicies, so there will only be a very small number of keys for -each index, so a key lookup won't have to look at many keys to find the correct -one. + template < + class Key, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_set unordered_set]``; + + template< + class Key, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_multiset unordered_multiset]``; + +and in <[headerref boost/unordered_map.hpp]>: + + template < + class Key, class T, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_map unordered_map]``; + + template< + class Key, class T, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_multimap unordered_multimap]``; + +The containers are used in a similar manner to the normal associative +containers: + + #include <``[headerref boost/unordered_map.hpp]``> + #include + + int main() + { + boost::unordered_map x; + x["one"] = 1; + x["two"] = 2; + x["three"] = 3; + + assert(x["one"] == 1); + assert(x["missing"] == 0); + } + +But there are some major differences, which will be detailed later. [endsect] diff --git a/doc/ref.xml b/doc/ref.xml index eb6653ab..0a8ce52c 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -1,6 +1,4 @@ - - -
+ @@ -75,9 +73,7 @@ The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. - The number of buckets is automatically increased whenever an insert will make the load factor greater than the maximum load factor. It can also change as result of calling rehash. - - When the number of buckets change: iterators are invalidated, the elements can change order, and move to different buckets, but pointers and references to elements remain valid. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. @@ -319,7 +315,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -360,7 +356,7 @@ The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -386,7 +382,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -603,6 +599,11 @@ n < bucket_count() + + The number of elements in bucket + n. + + @@ -692,7 +693,7 @@ float - Changes the container's maximum load factor,using + Changes the container's maximum load factor, using z as a hint. @@ -704,13 +705,13 @@ void Changes the number of buckets so that there at least - n buckets, and so that the load factor is less thanthe maximum load factor. + n buckets, and so that the load factor is less than the maximum load factor. Invalidates iterators, and changes the order of elements - The function has no effect if an exception is throw, unless it is thrown by the container’s hash function or comparison function. + The function has no effect if an exception is thrown, unless it is thrown by the container’s hash function or comparison function. @@ -813,9 +814,7 @@ The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. - The number of buckets is automatically increased whenever an insert will make the load factor greater than the maximum load factor. It can also change as result of calling rehash. - - When the number of buckets change: iterators are invalidated, the elements can change order, and move to different buckets, but pointers and references to elements remain valid. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. @@ -1055,7 +1054,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1096,7 +1095,7 @@ The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1122,7 +1121,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1339,6 +1338,11 @@ n < bucket_count() + + The number of elements in bucket + n. + + @@ -1428,7 +1432,7 @@ float - Changes the container's maximum load factor,using + Changes the container's maximum load factor, using z as a hint. @@ -1440,13 +1444,13 @@ void Changes the number of buckets so that there at least - n buckets, and so that the load factor is less thanthe maximum load factor. + n buckets, and so that the load factor is less than the maximum load factor. Invalidates iterators, and changes the order of elements - The function has no effect if an exception is throw, unless it is thrown by the container’s hash function or comparison function. + The function has no effect if an exception is thrown, unless it is thrown by the container’s hash function or comparison function. @@ -1566,9 +1570,7 @@ The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. - The number of buckets is automatically increased whenever an insert will make the load factor greater than the maximum load factor. It can also change as result of calling rehash. - - When the number of buckets change: iterators are invalidated, the elements can change order, and move to different buckets, but pointers and references to elements remain valid. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. @@ -1813,7 +1815,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1854,7 +1856,7 @@ The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1880,7 +1882,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2092,7 +2094,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2122,6 +2124,11 @@ n < bucket_count() + + The number of elements in bucket + n. + + @@ -2211,7 +2218,7 @@ float - Changes the container's maximum load factor,using + Changes the container's maximum load factor, using z as a hint. @@ -2223,13 +2230,13 @@ void Changes the number of buckets so that there at least - n buckets, and so that the load factor is less thanthe maximum load factor. + n buckets, and so that the load factor is less than the maximum load factor. Invalidates iterators, and changes the order of elements - The function has no effect if an exception is throw, unless it is thrown by the container’s hash function or comparison function. + The function has no effect if an exception is thrown, unless it is thrown by the container’s hash function or comparison function. @@ -2343,9 +2350,7 @@ The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. - The number of buckets is automatically increased whenever an insert will make the load factor greater than the maximum load factor. It can also change as result of calling rehash. - - When the number of buckets change: iterators are invalidated, the elements can change order, and move to different buckets, but pointers and references to elements remain valid. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. @@ -2588,7 +2593,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2629,7 +2634,7 @@ The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2655,7 +2660,7 @@ - Will only rehash if the insert causes the load factor to be greater to or equal to the maximum load factor. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2872,6 +2877,11 @@ n < bucket_count() + + The number of elements in bucket + n. + + @@ -2961,7 +2971,7 @@ float - Changes the container's maximum load factor,using + Changes the container's maximum load factor, using z as a hint. @@ -2973,13 +2983,13 @@ void Changes the number of buckets so that there at least - n buckets, and so that the load factor is less thanthe maximum load factor. + n buckets, and so that the load factor is less than the maximum load factor. Invalidates iterators, and changes the order of elements - The function has no effect if an exception is throw, unless it is thrown by the container’s hash function or comparison function. + The function has no effect if an exception is thrown, unless it is thrown by the container’s hash function or comparison function. @@ -3018,5 +3028,4 @@ -
-
+ diff --git a/doc/unordered.qbk b/doc/unordered.qbk index fb88e703..d0e988c6 100644 --- a/doc/unordered.qbk +++ b/doc/unordered.qbk @@ -18,6 +18,7 @@ and, worst of all, incorrect. Don't take anything in it seriously. [endsect] [include:unordered intro.qbk] +[include:unordered buckets.qbk] [include:unordered comparison.qbk] [include:unordered rationale.qbk] From 66f2cc3e8b60b9bb2d1d756d6749f2f61de5fdc0 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 19 Mar 2006 14:45:05 +0000 Subject: [PATCH 016/175] Some more work on the unordered containers documentation. [SVN r2889] --- doc/comparison.qbk | 88 +++++++++++-------------------------------- doc/hash_equality.qbk | 23 +++++++++++ doc/intro.qbk | 15 +++++++- doc/rationale.qbk | 4 +- doc/unordered.qbk | 1 + 5 files changed, 61 insertions(+), 70 deletions(-) create mode 100644 doc/hash_equality.qbk diff --git a/doc/comparison.qbk b/doc/comparison.qbk index b9cbc562..8684283c 100644 --- a/doc/comparison.qbk +++ b/doc/comparison.qbk @@ -1,70 +1,26 @@ [section:comparison Comparison to Associative Containers] -The unordered associative containers have a very similar interface to the -associative containers. For example: - - #include - - ... - - typedef ``[classref boost::unordered_map]`` map; - map x; - x["one"] = 1; - x["two"] = 2; - x["three"] = 3; - - std::cout<, - typename Pred = std::equal_to, - typename Alloc = std::allocator > - class ``[classref boost::unordered_set unordered_set]``; - -The hash function comes first as you might want to change the hash function -but not the equality predicate, while if you were to change the behaviour -of the equality predicate you would have to change the hash function to match -it. - -For example, if you wanted to use +* The elements in an unordered container are organised into buckets, in an + unpredictable order. There are member functions to.... TODO +* The unordered associative containers don't support the comparison operators. +* Instead of being parameterized by an ordering relation `Compare`, + the unordered associative container are parameterized by a function object + `Hash` and an equivalence realtion `Pred`. The member types and accessor + member functions reflect this. +* Because of this, equivalent keys for unordered container are defined in + terms of `Pred`, while for the associative containers it's defined in terms + of `Compare`. +* Unordered associative containers' iterators can be invalidated by rehashing + or by inserting elements. +* Unordered associative containers' iterators are of at least the forward + iterator category. Associative containers' iterators are bidirectional. +* The unordered associative containers' constructors have extra parameters + for the number of buckets, the hash function and the equality predicate. +* The unordered associative container don't have a `lower_bound` or + `upper_bound` method - they wouldn't make any sense since the elements + aren't ordered. +* TODO: Complexity guarantees. +* TODO: Behaviour when exceptions throw. The unordered containers seem + a lot stronger defined here? [endsect] diff --git a/doc/hash_equality.qbk b/doc/hash_equality.qbk new file mode 100644 index 00000000..f0d9bdf9 --- /dev/null +++ b/doc/hash_equality.qbk @@ -0,0 +1,23 @@ +[section:hash_equality Equality Predicates and Hash Functions] + +[/TODO: A better introduction to hash functions?] + +While the associative containers use an ordering relation to specify how the +elements are stored, the unordered associative containers use an equality +predicate and a hash function. For example [classref boost::unordered_set] +is declared as: + + template, + typename Pred = std::equal_to, + typename Alloc = std::allocator > + class ``[classref boost::unordered_set unordered_set]``; + +The hash function comes first as you might want to change the hash function +but not the equality predicate, while if you were to change the behaviour +of the equality predicate you would have to change the hash function to match +it. + +For example, if you wanted to use + +[endsect] diff --git a/doc/intro.qbk b/doc/intro.qbk index 7addd14f..b9db3ec9 100644 --- a/doc/intro.qbk +++ b/doc/intro.qbk @@ -72,6 +72,19 @@ containers: assert(x["missing"] == 0); } -But there are some major differences, which will be detailed later. +But since the elements aren't ordered, the output of: + + BOOST_FOREACH(map::value_type i, x) { + std::cout< Date: Sun, 19 Mar 2006 22:24:06 +0000 Subject: [PATCH 017/175] Some tests. [SVN r2892] --- test/container/Jamfile.v2 | 17 ++ test/container/compile_tests.hpp | 114 ++++++++++++++ test/container/map_compile.cpp | 45 ++++++ test/container/set_compile.cpp | 39 +++++ test/helpers/check_return_type.hpp | 38 +++++ test/objects/minimal.hpp | 245 +++++++++++++++++++++++++++++ 6 files changed, 498 insertions(+) create mode 100644 test/container/Jamfile.v2 create mode 100644 test/container/compile_tests.hpp create mode 100644 test/container/map_compile.cpp create mode 100644 test/container/set_compile.cpp create mode 100644 test/helpers/check_return_type.hpp create mode 100644 test/objects/minimal.hpp diff --git a/test/container/Jamfile.v2 b/test/container/Jamfile.v2 new file mode 100644 index 00000000..a0dcd090 --- /dev/null +++ b/test/container/Jamfile.v2 @@ -0,0 +1,17 @@ + +# Copyright Daniel James 2005. Use, modification, and distribution are +# 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) + +import testing ; + +project unordered-test/container + : requirements + intel-linux:"-strict_ansi -cxxlib-icc" + ; + +test-suite container-tests + : + [ run set_compile.cpp ] + [ run map_compile.cpp ] + ; diff --git a/test/container/compile_tests.hpp b/test/container/compile_tests.hpp new file mode 100644 index 00000000..1016f870 --- /dev/null +++ b/test/container/compile_tests.hpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../helpers/check_return_type.hpp" + +typedef long double comparison_type; + +template void sink(T const&) {} + +template +void container_test(X& r, T& value) +{ + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename X::difference_type difference_type; + typedef typename X::size_type size_type; + + typedef typename boost::iterator_value::type iterator_value_type; + typedef typename boost::iterator_value::type const_iterator_value_type; + typedef typename boost::iterator_difference::type iterator_difference_type; + typedef typename boost::iterator_difference::type const_iterator_difference_type; + + typedef typename X::value_type value_type; + typedef typename X::reference reference; + typedef typename X::const_reference const_reference; + + // value_type + + BOOST_MPL_ASSERT((boost::is_same)); + boost::function_requires >(); + + // reference_type / const_reference_type + + // TODO: 'lvalue of T' + BOOST_MPL_ASSERT((boost::is_same)); + // TODO: 'const lvalue of T' + BOOST_MPL_ASSERT((boost::is_same)); + + // iterator + + boost::function_requires >(); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_convertible)); + + // const_iterator + + // TODO: Test that it's a constant iterator? + boost::function_requires >(); + BOOST_MPL_ASSERT((boost::is_same)); + + // difference_type + + BOOST_MPL_ASSERT((boost::mpl::bool_< + std::numeric_limits::is_signed>)); + BOOST_MPL_ASSERT((boost::mpl::bool_< + std::numeric_limits::is_integer>)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + // size_type + + BOOST_MPL_ASSERT_NOT((boost::mpl::bool_< + std::numeric_limits::is_signed>)); + BOOST_MPL_ASSERT((boost::mpl::bool_< + std::numeric_limits::is_integer>)); + + // size_type can represent any non-negative value type of difference_type + // I'm not sure about either of these tests... + size_type max_diff((std::numeric_limits::max)()); + difference_type converted_diff(max_diff); + BOOST_TEST((std::numeric_limits::max)() + == converted_diff); + + BOOST_TEST( + static_cast( + (std::numeric_limits::max)()) > + static_cast( + (std::numeric_limits::max)())); + + // I don't test the runtime post-conditions here. + X u; + BOOST_TEST(u.size() == 0); + BOOST_TEST(X().size() == 0); + + X a,b; + + sink(X(a)); + X u2(a); + X u3 = a; + + X* ptr = new X(); + X& a1 = *ptr; + (&a1)->~X(); + + X const a_const; + test::check_return_type::equals(a.begin()); + test::check_return_type::equals(a_const.begin()); + test::check_return_type::equals(a.end()); + test::check_return_type::equals(a_const.end()); + + // No tests for ==, != since they're not required for unordered containers. + + a.swap(b); + test::check_return_type::equals_ref(r = a); + test::check_return_type::equals(a.size()); + test::check_return_type::equals(a.max_size()); + test::check_return_type::convertible(a.empty()); +} diff --git a/test/container/map_compile.cpp b/test/container/map_compile.cpp new file mode 100644 index 00000000..281b9c62 --- /dev/null +++ b/test/container/map_compile.cpp @@ -0,0 +1,45 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +// This test creates the containers with members that meet their minimum +// requirements. Makes sure everything compiles and is defined correctly. + +#include + +#include +#include +#include "../objects/minimal.hpp" +#include "./compile_tests.hpp" + +int main() +{ + typedef std::pair value_type; + value_type value( + test::minimal::assignable::create(), + test::minimal::copy_constructible::create()); + + std::cout<<"Test unordered_map.\n"; + boost::unordered_map< + test::minimal::assignable, + test::minimal::copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > map; + + container_test(map, value); + + std::cout<<"Test unordered_multimap.\n"; + boost::unordered_multimap< + test::minimal::assignable, + test::minimal::copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > multimap; + + container_test(multimap, value); + + return boost::report_errors(); +} diff --git a/test/container/set_compile.cpp b/test/container/set_compile.cpp new file mode 100644 index 00000000..469f6630 --- /dev/null +++ b/test/container/set_compile.cpp @@ -0,0 +1,39 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +// This test creates the containers with members that meet their minimum +// requirements. Makes sure everything compiles and is defined correctly. + +#include + +#include +#include +#include "../objects/minimal.hpp" +#include "./compile_tests.hpp" + +int main() +{ + test::minimal::assignable assignable = test::minimal::assignable::create(); + + std::cout<<"Test unordered_set.\n"; + boost::unordered_set< + test::minimal::assignable, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > set; + + container_test(set, assignable); + + std::cout<<"Test unordered_multiset.\n"; + boost::unordered_multiset< + test::minimal::assignable, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > multiset; + + container_test(multiset, assignable); + + return boost::report_errors(); +} diff --git a/test/helpers/check_return_type.hpp b/test/helpers/check_return_type.hpp new file mode 100644 index 00000000..87ce7e28 --- /dev/null +++ b/test/helpers/check_return_type.hpp @@ -0,0 +1,38 @@ + +// Copyright Daniel James 2005-2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_CHECK_RETURN_TYPE_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_CHECK_RETURN_TYPE_HEADER + +#include +#include +#include + +namespace test +{ + template + struct check_return_type + { + template + static void equals(T2) + { + BOOST_MPL_ASSERT((boost::is_same)); + } + + template + static void equals_ref(T2&) + { + BOOST_MPL_ASSERT((boost::is_same)); + } + + template + static void convertible(T2) + { + BOOST_MPL_ASSERT((boost::is_convertible)); + } + }; +} + +#endif diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp new file mode 100644 index 00000000..5ed9c84d --- /dev/null +++ b/test/objects/minimal.hpp @@ -0,0 +1,245 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER) +#define BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER + +#include + +namespace test +{ +namespace minimal +{ + class copy_constructible; + class assignable; + template class hash; + template class equal_to; + template class pointer; + template class const_pointer; + template class allocator; + + class copy_constructible + { + public: + static copy_constructible create() { return copy_constructible(); } + copy_constructible(copy_constructible const&) {} + ~copy_constructible() {} + private: + copy_constructible& operator=(copy_constructible const&); + copy_constructible() {} + }; + + class assignable + { + public: + static assignable create() { return assignable(); } + assignable(assignable const&) {} + assignable& operator=(assignable const&) { return *this; } + ~assignable() {} + private: + assignable() {} + }; + + template + class hash + { + public: + static hash create() { return hash(); } + // TODO: hash has to be default constructible for the default + // parameters. Maybe use an alternative version for testing + // other member functions. + // + // Or maybe it's required to be default constructible? + // The Container requirements include a default constructor. + hash() {} + hash(hash const&) {} + // TODO: Required to be assignable? + hash& operator=(hash const&) { return *this; } + ~hash() {} + + std::size_t operator()(T const& x) const { return 0; } + }; + + template + class equal_to + { + public: + static equal_to create() { return equal_to(); } + // TODO: equal_to has to be default constructible for the default + // parameters. Maybe use an alternative version for testing + // other member functions. + // + // Or maybe it's required to be default constructible? + // The Container requirements include a default constructor. + equal_to() {} + equal_to(equal_to const&) {} + // TODO: Required to be assignable? + equal_to& operator=(equal_to const&) { return *this; } + ~equal_to() {} + + bool operator()(T const& x, T const& y) const { return true; } + }; + + namespace detail + { + template + class pointer_base + { + protected: + pointer_base() : ptr_(0) {} + explicit pointer_base(T* ptr) : ptr_(ptr) {} + ~pointer_base() {} + Ptr& get() { return *static_cast(this); } + T* ptr_; + public: + typedef void (pointer_base::*bool_type)() const; + void this_type_does_not_support_comparisons() const {} + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + Ptr& operator++() { ++ptr_; return get(); } + Ptr operator++(int) { Ptr tmp(get()); ++ptr_; return tmp; } + Ptr operator+(int s) const { return Ptr(ptr_ + s); } + T& operator[](int s) const { return ptr_[s]; } + + operator bool_type() const { + return ptr_ ? + &pointer_base::this_type_does_not_support_comparisons + : 0; + } + + bool operator!() const { return !ptr_; } + bool operator==(Ptr const& x) const { return ptr_ == x.ptr_; } + bool operator!=(Ptr const& x) const { return ptr_ != x.ptr_; } + bool operator<(Ptr const& x) const { return ptr_ < x.ptr_; } + bool operator>(Ptr const& x) const { return ptr_ > x.ptr_; } + bool operator<=(Ptr const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(Ptr const& x) const { return ptr_ >= x.ptr_; } + }; + } + + template class pointer; + template class const_pointer; + + template + class pointer : public detail::pointer_base, T> + { + friend class allocator; + friend class detail::pointer_base, T>; + friend class const_pointer; + typedef detail::pointer_base, T> base; + + pointer(T* ptr) : base(ptr) {} + + public: + pointer() : base() {} + + bool operator==(pointer const& x) const { return base::operator==(x); } + bool operator!=(pointer const& x) const { return base::operator!=(x); } + bool operator<(pointer const& x) const { return base::operator<(x);} + bool operator>(pointer const& x) const { return base::operator>(x);} + bool operator<=(pointer const& x) const { return base::operator<=(x);} + bool operator>=(pointer const& x) const { return base::operator<=(x);} + + bool operator==(const_pointer const& x) const { return x == *this; } + bool operator!=(const_pointer const& x) const { return x != *this; } + bool operator<(const_pointer const& x) const { return x > *this; } + bool operator>(const_pointer const& x) const { return x < *this; } + bool operator<=(const_pointer const& x) const { return x >= *this; } + bool operator>=(const_pointer const& x) const { return x <= *this; } + }; + + template + class const_pointer : public detail::pointer_base, T const> + { + friend class allocator; + friend class detail::pointer_base, T const>; + typedef detail::pointer_base, T const> base; + + const_pointer(T* ptr) : base(ptr) {} + + public: + const_pointer() : base() {} + const_pointer(pointer const& x) : base(x.ptr_) {} + + bool operator==(const_pointer const& x) const { return base::operator==(x); } + bool operator!=(const_pointer const& x) const { return base::operator!=(x); } + bool operator<(const_pointer const& x) const { return base::operator<(x);} + bool operator>(const_pointer const& x) const { return base::operator>(x);} + bool operator<=(const_pointer const& x) const { return base::operator<=(x);} + bool operator>=(const_pointer const& x) const { return base::operator<=(x);} + + bool operator==(pointer const& x) const { return operator==(const_pointer(x)); } + bool operator!=(pointer const& x) const { return operator!=(const_pointer(x)); } + bool operator<(pointer const& x) const { return operator<(const_pointer(x));} + bool operator>(pointer const& x) const { return operator>(const_pointer(x));} + bool operator<=(pointer const& x) const { return operator<=(const_pointer(x));} + bool operator>=(pointer const& x) const { return operator<=(const_pointer(x));} + }; + + template + class allocator + { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef pointer pointer; + typedef const_pointer const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef allocator other; }; + + allocator() {} + allocator(allocator const&) {} + template allocator(allocator const&) {} + ~allocator() {} + + pointer address(reference r) { return pointer(&r); } + const_pointer address(const_reference r) { return const_pointer(&r); } + + pointer allocate(size_type n) { + return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + pointer allocate(size_type n, const_pointer u) + { + return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + void deallocate(pointer p, size_type n) + { + ::operator delete((void*) p.ptr_); + } + + void construct(pointer p, T const& t) { new((void*)p.ptr_) T(t); } + void destroy(pointer p) { ((T*)p.ptr_)->~T(); } + + size_type max_size() const { return 1000; } + private: + allocator& operator=(allocator const&); + }; + + template + inline bool operator==(allocator const& x, allocator const& y) + { + return true; + } + + template + inline bool operator!=(allocator const& x, allocator const& y) + { + return false; + } + + template + void swap(allocator& x, allocator& y) + { + } +} +} + +#endif From f43b6bb5d8ec3c3bdc16ba62e46a8c9aa0f8e2b6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 20 Mar 2006 20:00:55 +0000 Subject: [PATCH 018/175] On compilers without ADL, qualify the call to swap. [SVN r2895] --- include/boost/unordered/detail/hash_table.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 1e8c6cfc..0e46219c 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -66,8 +66,12 @@ namespace boost { template inline void hash_swap(T& x, T& y) { +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) + std::swap(x,y); +#else using namespace std; swap(x, y); +#endif } inline std::size_t float_to_size_t(float f) From 56e67a9d7d8a22386f4d6b14def29a25f2483007 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 20 Mar 2006 20:01:45 +0000 Subject: [PATCH 019/175] Get the unordered tests running on Visual C++ 6.5 [SVN r2896] --- test/objects/minimal.hpp | 150 +++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 76 deletions(-) diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index 5ed9c84d..d63e85e9 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -82,101 +82,95 @@ namespace minimal bool operator()(T const& x, T const& y) const { return true; } }; - namespace detail - { - template - class pointer_base - { - protected: - pointer_base() : ptr_(0) {} - explicit pointer_base(T* ptr) : ptr_(ptr) {} - ~pointer_base() {} - Ptr& get() { return *static_cast(this); } - T* ptr_; - public: - typedef void (pointer_base::*bool_type)() const; - void this_type_does_not_support_comparisons() const {} - - T& operator*() const { return *ptr_; } - T* operator->() const { return ptr_; } - Ptr& operator++() { ++ptr_; return get(); } - Ptr operator++(int) { Ptr tmp(get()); ++ptr_; return tmp; } - Ptr operator+(int s) const { return Ptr(ptr_ + s); } - T& operator[](int s) const { return ptr_[s]; } - - operator bool_type() const { - return ptr_ ? - &pointer_base::this_type_does_not_support_comparisons - : 0; - } - - bool operator!() const { return !ptr_; } - bool operator==(Ptr const& x) const { return ptr_ == x.ptr_; } - bool operator!=(Ptr const& x) const { return ptr_ != x.ptr_; } - bool operator<(Ptr const& x) const { return ptr_ < x.ptr_; } - bool operator>(Ptr const& x) const { return ptr_ > x.ptr_; } - bool operator<=(Ptr const& x) const { return ptr_ <= x.ptr_; } - bool operator>=(Ptr const& x) const { return ptr_ >= x.ptr_; } - }; - } - template class pointer; template class const_pointer; template - class pointer : public detail::pointer_base, T> + class pointer { friend class allocator; - friend class detail::pointer_base, T>; friend class const_pointer; - typedef detail::pointer_base, T> base; - pointer(T* ptr) : base(ptr) {} + T* ptr_; + pointer(T* ptr) : ptr_(ptr) {} public: - pointer() : base() {} + pointer() : ptr_(0) {} - bool operator==(pointer const& x) const { return base::operator==(x); } - bool operator!=(pointer const& x) const { return base::operator!=(x); } - bool operator<(pointer const& x) const { return base::operator<(x);} - bool operator>(pointer const& x) const { return base::operator>(x);} - bool operator<=(pointer const& x) const { return base::operator<=(x);} - bool operator>=(pointer const& x) const { return base::operator<=(x);} + typedef void (pointer::*bool_type)() const; + void this_type_does_not_support_comparisons() const {} - bool operator==(const_pointer const& x) const { return x == *this; } - bool operator!=(const_pointer const& x) const { return x != *this; } - bool operator<(const_pointer const& x) const { return x > *this; } - bool operator>(const_pointer const& x) const { return x < *this; } - bool operator<=(const_pointer const& x) const { return x >= *this; } - bool operator>=(const_pointer const& x) const { return x <= *this; } + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + pointer& operator++() { ++ptr_; return *this; } + pointer operator++(int) { pointer tmp(*this); ++ptr_; return tmp; } + pointer operator+(int s) const { return pointer(ptr_ + s); } + T& operator[](int s) const { return ptr_[s]; } + bool operator!() const { return !ptr_; } + + operator bool_type() const { + return ptr_ ? + &pointer::this_type_does_not_support_comparisons + : 0; + } + + bool operator==(pointer const& x) const { return ptr_ == x.ptr_; } + bool operator!=(pointer const& x) const { return ptr_ != x.ptr_; } + bool operator<(pointer const& x) const { return ptr_ < x.ptr_; } + bool operator>(pointer const& x) const { return ptr_ > x.ptr_; } + bool operator<=(pointer const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(pointer const& x) const { return ptr_ >= x.ptr_; } + + bool operator==(const_pointer const& x) const { return ptr_ == x.ptr_; } + bool operator!=(const_pointer const& x) const { return ptr_ != x.ptr_; } + bool operator<(const_pointer const& x) const { return ptr_ < x.ptr_; } + bool operator>(const_pointer const& x) const { return ptr_ > x.ptr_; } + bool operator<=(const_pointer const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(const_pointer const& x) const { return ptr_ >= x.ptr_; } }; template - class const_pointer : public detail::pointer_base, T const> + class const_pointer { friend class allocator; - friend class detail::pointer_base, T const>; - typedef detail::pointer_base, T const> base; - const_pointer(T* ptr) : base(ptr) {} + T* ptr_; + const_pointer(T* ptr) : ptr_(ptr) {} public: - const_pointer() : base() {} - const_pointer(pointer const& x) : base(x.ptr_) {} + const_pointer() : ptr_(0) {} + const_pointer(pointer const& x) : ptr_(x.ptr_) {} - bool operator==(const_pointer const& x) const { return base::operator==(x); } - bool operator!=(const_pointer const& x) const { return base::operator!=(x); } - bool operator<(const_pointer const& x) const { return base::operator<(x);} - bool operator>(const_pointer const& x) const { return base::operator>(x);} - bool operator<=(const_pointer const& x) const { return base::operator<=(x);} - bool operator>=(const_pointer const& x) const { return base::operator<=(x);} + typedef void (const_pointer::*bool_type)() const; + void this_type_does_not_support_comparisons() const {} - bool operator==(pointer const& x) const { return operator==(const_pointer(x)); } - bool operator!=(pointer const& x) const { return operator!=(const_pointer(x)); } - bool operator<(pointer const& x) const { return operator<(const_pointer(x));} - bool operator>(pointer const& x) const { return operator>(const_pointer(x));} - bool operator<=(pointer const& x) const { return operator<=(const_pointer(x));} - bool operator>=(pointer const& x) const { return operator<=(const_pointer(x));} + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + const_pointer& operator++() { ++ptr_; return *this; } + const_pointer operator++(int) { const_pointer tmp(*this); ++ptr_; return tmp; } + const_pointer operator+(int s) const { return const_pointer(ptr_ + s); } + T& operator[](int s) const { return ptr_[s]; } + bool operator!() const { return !ptr_; } + + operator bool_type() const { + return ptr_ ? + &const_pointer::this_type_does_not_support_comparisons + : 0; + } + + bool operator==(pointer const& x) const { return ptr_ == x.ptr_; } + bool operator!=(pointer const& x) const { return ptr_ != x.ptr_; } + bool operator<(pointer const& x) const { return ptr_ < x.ptr_; } + bool operator>(pointer const& x) const { return ptr_ > x.ptr_; } + bool operator<=(pointer const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(pointer const& x) const { return ptr_ >= x.ptr_; } + + bool operator==(const_pointer const& x) const { return ptr_ == x.ptr_; } + bool operator!=(const_pointer const& x) const { return ptr_ != x.ptr_; } + bool operator<(const_pointer const& x) const { return ptr_ < x.ptr_; } + bool operator>(const_pointer const& x) const { return ptr_ > x.ptr_; } + bool operator<=(const_pointer const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(const_pointer const& x) const { return ptr_ >= x.ptr_; } }; template @@ -194,8 +188,8 @@ namespace minimal template struct rebind { typedef allocator other; }; allocator() {} - allocator(allocator const&) {} template allocator(allocator const&) {} + allocator(allocator const&) {} ~allocator() {} pointer address(reference r) { return pointer(&r); } @@ -219,8 +213,12 @@ namespace minimal void destroy(pointer p) { ((T*)p.ptr_)->~T(); } size_type max_size() const { return 1000; } - private: - allocator& operator=(allocator const&); + +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) + public: allocator& operator=(allocator const&) { return *this;} +#else + private: allocator& operator=(allocator const&); +#endif }; template From e8faab1268fb0f545c9747362f7e2ce50fdd65f8 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 17 Apr 2006 08:45:32 +0000 Subject: [PATCH 020/175] Update the unordered containers' iterator overloads for the more recent versions of TR1. [SVN r2921] --- include/boost/unordered_map.hpp | 44 +++++++++++++--- include/boost/unordered_set.hpp | 91 +++++---------------------------- 2 files changed, 50 insertions(+), 85 deletions(-) diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 5ea8fc27..e44341b3 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -5,7 +5,7 @@ // boost/unordered_map.hpp // Copyright © 2003-2004 Jeremy B. Maitin-Shepard. -// Copyright © 2005 Daniel James. +// Copyright © 2005-2006 Daniel James. // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy @@ -186,32 +186,47 @@ namespace boost base.insert_unique(obj)); } - iterator insert(const_iterator hint, const value_type& obj) + iterator insert(iterator hint, const value_type& obj) { return iterator(base.insert_unique(get(hint), obj)); } + const_iterator insert(const_iterator hint, const value_type& obj) + { + return const_iterator(base.insert_unique(get(hint), obj)); + } + template void insert(InputIterator first, InputIterator last) { base.insert(first, last); } - iterator erase(const_iterator position) + iterator erase(iterator position) { return iterator(base.erase(get(position))); } + const_iterator erase(const_iterator position) + { + return const_iterator(base.erase(get(position))); + } + size_type erase(const key_type& k) { return base.erase(k); } - iterator erase(const_iterator first, const_iterator last) + iterator erase(iterator first, iterator last) { return iterator(base.erase(get(first), get(last))); } + const_iterator erase(const_iterator first, const_iterator last) + { + return const_iterator(base.erase(get(first), get(last))); + } + void clear() { base.clear(); @@ -473,32 +488,47 @@ namespace boost return iterator(base.insert_equivalent(obj)); } - iterator insert(const_iterator hint, const value_type& obj) + iterator insert(iterator hint, const value_type& obj) { return iterator(base.insert_equivalent(get(hint), obj)); } + const_iterator insert(const_iterator hint, const value_type& obj) + { + return const_iterator(base.insert_equivalent(get(hint), obj)); + } + template void insert(InputIterator first, InputIterator last) { base.insert(first, last); } - iterator erase(const_iterator position) + iterator erase(iterator position) { return iterator(base.erase(get(position))); } + const_iterator erase(const_iterator position) + { + return const_iterator(base.erase(get(position))); + } + size_type erase(const key_type& k) { return base.erase(k); } - iterator erase(const_iterator first, const_iterator last) + iterator erase(iterator first, iterator last) { return iterator(base.erase(get(first), get(last))); } + const_iterator erase(const_iterator first, const_iterator last) + { + return const_iterator(base.erase(get(first), get(last))); + } + void clear() { base.clear(); diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index c1c19e36..ed5e08a4 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -5,13 +5,12 @@ // boost/unordered_set.hpp // Copyright © 2003-2004 Jeremy B. Maitin-Shepard. -// Copyright © 2005 Daniel James. +// Copyright © 2005-2006 Daniel James. // 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) - #ifndef BOOST_UNORDERED_SET_HPP_INCLUDED #define BOOST_UNORDERED_SET_HPP_INCLUDED @@ -130,21 +129,11 @@ namespace boost // iterators - iterator begin() - { - return iterator(base.begin()); - } - const_iterator begin() const { return const_iterator(base.begin()); } - iterator end() - { - return iterator(base.end()); - } - const_iterator end() const { return const_iterator(base.end()); @@ -158,9 +147,9 @@ namespace boost base.insert_unique(obj)); } - iterator insert(const_iterator hint, const value_type& obj) + const_iterator insert(const_iterator hint, const value_type& obj) { - return iterator(base.insert_unique(get(hint), obj)); + return const_iterator(base.insert_unique(get(hint), obj)); } template @@ -169,9 +158,9 @@ namespace boost base.insert(first, last); } - iterator erase(const_iterator position) + const_iterator erase(const_iterator position) { - return iterator(base.erase(get(position))); + return const_iterator(base.erase(get(position))); } size_type erase(const key_type& k) @@ -179,9 +168,9 @@ namespace boost return base.erase(k); } - iterator erase(const_iterator first, const_iterator last) + const_iterator erase(const_iterator first, const_iterator last) { - return iterator(base.erase(get(first), get(last))); + return const_iterator(base.erase(get(first), get(last))); } void clear() @@ -208,11 +197,6 @@ namespace boost // lookup - iterator find(const key_type& k) - { - return iterator(base.find(k)); - } - const_iterator find(const key_type& k) const { return const_iterator(base.find(k)); @@ -223,13 +207,6 @@ namespace boost return base.count(k); } - std::pair - equal_range(const key_type& k) - { - return boost::unordered_detail::pair_cast( - base.equal_range(k)); - } - std::pair equal_range(const key_type& k) const { @@ -259,21 +236,11 @@ namespace boost return base.bucket(k); } - local_iterator begin(size_type n) - { - return local_iterator(base.begin(n)); - } - const_local_iterator begin(size_type n) const { return local_iterator(base.begin(n)); } - local_iterator end(size_type n) - { - return local_iterator(base.end(n)); - } - const_local_iterator end(size_type n) const { return const_local_iterator(base.end(n)); @@ -410,21 +377,11 @@ namespace boost // iterators - iterator begin() - { - return iterator(base.begin()); - } - const_iterator begin() const { return const_iterator(base.begin()); } - iterator end() - { - return iterator(base.end()); - } - const_iterator end() const { return const_iterator(base.end()); @@ -437,9 +394,9 @@ namespace boost return iterator(base.insert_equivalent(obj)); } - iterator insert(const_iterator hint, const value_type& obj) + const_iterator insert(const_iterator hint, const value_type& obj) { - return iterator(base.insert_equivalent(get(hint), obj)); + return const_iterator(base.insert_equivalent(get(hint), obj)); } template @@ -448,9 +405,9 @@ namespace boost base.insert(first, last); } - iterator erase(const_iterator position) + const_iterator erase(const_iterator position) { - return iterator(base.erase(get(position))); + return const_iterator(base.erase(get(position))); } size_type erase(const key_type& k) @@ -458,9 +415,9 @@ namespace boost return base.erase(k); } - iterator erase(const_iterator first, const_iterator last) + const_iterator erase(const_iterator first, const_iterator last) { - return iterator(base.erase(get(first), get(last))); + return const_iterator(base.erase(get(first), get(last))); } void clear() @@ -487,11 +444,6 @@ namespace boost // lookup - iterator find(const key_type& k) - { - return iterator(base.find(k)); - } - const_iterator find(const key_type& k) const { return const_iterator(base.find(k)); @@ -502,13 +454,6 @@ namespace boost return base.count(k); } - std::pair - equal_range(const key_type& k) - { - return boost::unordered_detail::pair_cast( - base.equal_range(k)); - } - std::pair equal_range(const key_type& k) const { @@ -538,21 +483,11 @@ namespace boost return base.bucket(k); } - local_iterator begin(size_type n) - { - return local_iterator(base.begin(n)); - } - const_local_iterator begin(size_type n) const { return const_local_iterator(base.begin(n)); } - local_iterator end(size_type n) - { - return local_iterator(base.end(n)); - } - const_local_iterator end(size_type n) const { return const_local_iterator(base.end(n)); From c243c3d5c11f520549cb3d40d8d8c364d2f47a31 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 17 Apr 2006 10:54:02 +0000 Subject: [PATCH 021/175] Add some notes on the active issues relevant to the unordered associative containers. [SVN r2922] --- doc/rationale.qbk | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/doc/rationale.qbk b/doc/rationale.qbk index 9740c192..fe92c256 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -27,4 +27,38 @@ range. This leaves the more expensive methods, such as Knuth's Multiplicative Method which don't tend to work as well as taking the modulous of a prime, have little efficiency advantage and don't work well for (TODO: what are they called?). +[h2 Active Issues] + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#258 + 258. Missing allocator requirement]] + +Need to look into this one. + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#431 + 431. Swapping containers with unequal allocators]] + +In a fit of probably unwise enthusiasm, I implemented all the three versions +with a macro (BOOST_UNORDERED_SWAP_METHOD) to pick which one is used. As +suggested by Howard Hinnant, I set option 3 as the default. I'll probably remove +the alternative implementations before review. + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#518 + 518. Are insert and erase stable for unordered_multiset and unordered_multimap?]] + +In this implementation, erase is stable but insert is not. As long as a rehash +can change the order of the elements, insert can't be. + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#528 + 528. TR1: issue 6.19 vs 6.3.4.3/2 (and 6.3.4.5/2)]] + +In the current implementation, for unordered_set and +unordered_multiset, iterator and const_iterator have the same type and +local_iterator and const_local_iterator also have the same type. This makes it +impossible to implement the header exactly as described in the synopsis, as +some member functions are overloaded by the same type. +According to the proposed resolution, +[:If they are the same type, those signatures that become otherwise indistinguishable collapse into a single signature.] +So I'm following that - although this means that the header and documentation +are currently inconsistent. This will be fixed before review submission. + [endsect] From 1cbb7f6e578035cf4b274624c7c40e2e23d9f766 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 17 Apr 2006 11:06:08 +0000 Subject: [PATCH 022/175] Re-introduce the non const overloads of begin and end for unordered_set and unordered_multiset. I thought the proposed resolution for issue 528 allowed them to be removed, but re-reading it, it only allows signatures that are 'otherwise indistinguishable', and they are not. [SVN r2923] --- include/boost/unordered_set.hpp | 43 ++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index ed5e08a4..0db03dba 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -129,11 +129,21 @@ namespace boost // iterators + iterator begin() + { + return iterator(base.begin()); + } + const_iterator begin() const { return const_iterator(base.begin()); } + iterator end() + { + return iterator(base.end()); + } + const_iterator end() const { return const_iterator(base.end()); @@ -236,16 +246,27 @@ namespace boost return base.bucket(k); } - const_local_iterator begin(size_type n) const + local_iterator begin(size_type n) { return local_iterator(base.begin(n)); } + const_local_iterator begin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + const_local_iterator end(size_type n) const { return const_local_iterator(base.end(n)); } + // hash policy float load_factor() const @@ -377,11 +398,21 @@ namespace boost // iterators + iterator begin() + { + return iterator(base.begin()); + } + const_iterator begin() const { return const_iterator(base.begin()); } + iterator end() + { + return iterator(base.end()); + } + const_iterator end() const { return const_iterator(base.end()); @@ -483,11 +514,21 @@ namespace boost return base.bucket(k); } + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + const_local_iterator begin(size_type n) const { return const_local_iterator(base.begin(n)); } + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + const_local_iterator end(size_type n) const { return const_local_iterator(base.end(n)); From ed6df529e4bb38f8789b43b7d9f9f5f8ffc4e716 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 17 Apr 2006 11:11:49 +0000 Subject: [PATCH 023/175] Remove non-ascii copyright characters and the old filenames from the start of the unordered container headers. [SVN r2924] --- include/boost/unordered/detail/hash_table.hpp | 8 ++------ include/boost/unordered_map.hpp | 10 ++-------- include/boost/unordered_set.hpp | 9 ++------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 0e46219c..8996cc1d 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -1,10 +1,6 @@ -// JTC1/SC22/WG21 N1456 Hash table implementation -// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html -// boost/detail/hash_table.hpp - -// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. -// Copyright © 2005 Daniel James +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005 Daniel James // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index e44341b3..2d26fc63 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -1,17 +1,11 @@ -// JTC1/SC22/WG21 N1456 Hash table implementation -// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html - -// boost/unordered_map.hpp - -// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. -// Copyright © 2005-2006 Daniel James. +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005-2006 Daniel James. // 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) - #ifndef BOOST_UNORDERED_MAP_HPP_INCLUDED #define BOOST_UNORDERED_MAP_HPP_INCLUDED diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index 0db03dba..75d16031 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -1,11 +1,6 @@ -// JTC1/SC22/WG21 N1456 Hash table implementation -// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html - -// boost/unordered_set.hpp - -// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. -// Copyright © 2005-2006 Daniel James. +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005-2006 Daniel James. // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy From 0fcb2d8041856bd77176b1296aca832d086d69d1 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 30 Apr 2006 14:11:47 +0000 Subject: [PATCH 024/175] Add cbegin() and cend() from the new draft standard to the unordered containers. The standard doesn't add them for local iterators, but I'm guessing that's an oversight. So for now, I've hidden them behind a preprocessor condition. Which I'll probably remove before review. [SVN r2944] --- include/boost/unordered_map.hpp | 44 +++++++++++++++++++++++++++++++++ include/boost/unordered_set.hpp | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 2d26fc63..009e7f9d 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -172,6 +172,16 @@ namespace boost return const_iterator(base.end()); } + const_iterator cbegin() const + { + return const_iterator(base.begin()); + } + + const_iterator cend() const + { + return const_iterator(base.end()); + } + // modifiers std::pair insert(const value_type& obj) @@ -321,6 +331,18 @@ namespace boost return const_local_iterator(base.end(n)); } +#if defined(BOOST_UNORDERED_LOCAL_CBEGIN) + const_local_iterator cbegin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + const_local_iterator cend(size_type n) const + { + return const_local_iterator(base.end(n)); + } +#endif + // hash policy float load_factor() const @@ -475,6 +497,16 @@ namespace boost return const_iterator(base.end()); } + const_iterator cbegin() const + { + return const_iterator(base.begin()); + } + + const_iterator cend() const + { + return const_iterator(base.end()); + } + // modifiers iterator insert(const value_type& obj) @@ -618,6 +650,18 @@ namespace boost return const_local_iterator(base.end(n)); } +#if defined(BOOST_UNORDERED_LOCAL_CBEGIN) + const_local_iterator cbegin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + const_local_iterator cend(size_type n) const + { + return const_local_iterator(base.end(n)); + } +#endif + // hash policy float load_factor() const diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index 75d16031..ee5a9b6c 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -144,6 +144,16 @@ namespace boost return const_iterator(base.end()); } + const_iterator cbegin() const + { + return const_iterator(base.begin()); + } + + const_iterator cend() const + { + return const_iterator(base.end()); + } + // modifiers std::pair insert(const value_type& obj) @@ -261,6 +271,17 @@ namespace boost return const_local_iterator(base.end(n)); } +#if defined(BOOST_UNORDERED_LOCAL_CBEGIN) + const_local_iterator cbegin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + const_local_iterator cend(size_type n) const + { + return const_local_iterator(base.end(n)); + } +#endif // hash policy @@ -413,6 +434,16 @@ namespace boost return const_iterator(base.end()); } + const_iterator cbegin() const + { + return const_iterator(base.begin()); + } + + const_iterator cend() const + { + return const_iterator(base.end()); + } + // modifiers iterator insert(const value_type& obj) @@ -529,6 +560,18 @@ namespace boost return const_local_iterator(base.end(n)); } +#if defined(BOOST_UNORDERED_LOCAL_CBEGIN) + const_local_iterator cbegin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + const_local_iterator cend(size_type n) const + { + return const_local_iterator(base.end(n)); + } +#endif + // hash policy float load_factor() const From 5450c28bc55583080b03aaf1d5a2ac101f070c67 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 30 Apr 2006 14:23:45 +0000 Subject: [PATCH 025/175] Add reference documentation for cbegin() and cend(). [SVN r2945] --- doc/ref.xml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/doc/ref.xml b/doc/ref.xml index 0a8ce52c..0c72c0da 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -286,6 +286,16 @@ An iterator which refers to the past-the-end value for the container. + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + @@ -1027,6 +1037,16 @@ An iterator which refers to the past-the-end value for the container. + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + @@ -1786,6 +1806,16 @@ An iterator which refers to the past-the-end value for the container. + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + @@ -2566,6 +2596,16 @@ An iterator which refers to the past-the-end value for the container. + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + From ef55c1278bdbf1c76ff405fba0def9e49de3b300 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 30 Apr 2006 14:24:56 +0000 Subject: [PATCH 026/175] Update the comments on issue 528 for unordered containers. [SVN r2946] --- doc/rationale.qbk | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/rationale.qbk b/doc/rationale.qbk index fe92c256..47fd99b5 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -51,14 +51,16 @@ can change the order of the elements, insert can't be. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#528 528. TR1: issue 6.19 vs 6.3.4.3/2 (and 6.3.4.5/2)]] -In the current implementation, for unordered_set and -unordered_multiset, iterator and const_iterator have the same type and -local_iterator and const_local_iterator also have the same type. This makes it -impossible to implement the header exactly as described in the synopsis, as +In the current implementation, for `unordered_set` and +`unordered_multiset`, `iterator` and `const_iterator` have the same type and +`local_iterator` and `const_local_iterator` also have the same type. This makes +it impossible to implement the header exactly as described in the synopsis, as some member functions are overloaded by the same type. -According to the proposed resolution, -[:If they are the same type, those signatures that become otherwise indistinguishable collapse into a single signature.] -So I'm following that - although this means that the header and documentation -are currently inconsistent. This will be fixed before review submission. + +The proposed resolution is to add a new subsection to 17.4.4: +[:An implementation shall not supply an overloaded function signature specified in any library clause if such a signature would be inherently ambiguous during overload resolution due to two library types referring to the same type.] +So I don't supply the `iterator` overloads - although this means that the +header and documentation are currently inconsistent. +This will be fixed before review submission. [endsect] From b0821928700d998189f53cf4998167918aa064f9 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 30 Apr 2006 14:25:33 +0000 Subject: [PATCH 027/175] Fix some mistakes in the rehash documentation. [SVN r2947] --- doc/buckets.qbk | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/buckets.qbk b/doc/buckets.qbk index 7fe0f95f..1e0b1315 100644 --- a/doc/buckets.qbk +++ b/doc/buckets.qbk @@ -89,6 +89,9 @@ std::unordered_set and std::unordered_map when insert is called with enough elements to exceed the maximum, but the maximum isn't exceeded because the elements are already in the container) +(TODO: Ah, I forgot about local iterators - rehashing must invalidate ranges +made up of local iterators, right?). + This all sounds quite gloomy, but it's not that bad. Most implementations will probably respect the maximum load factor hint. This implementation certainly does. @@ -134,7 +137,9 @@ If you want to stop the table from ever rehashing due to an insert, you can set the maximum load factor to infinity (or perhaps a load factor that it'll never reach - say `x.max_size()`. As you can only give a 'hint' for the maximum load factor, this isn't guaranteed to work. But again, it'll work in this -implementation. +implementation. (TODO: If an unordered container with infinite load factor +is copied, bad things could happen. So maybe this advice should be removed. Or +maybe the implementation should cope with that). If you do this and want to make the container rehash, `rehash` will still work. But be careful that you only ever call it with a sufficient number of buckets From 13f2fbaf00274dcd74aed814ede3f7357b225dbe Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 30 Apr 2006 15:00:11 +0000 Subject: [PATCH 028/175] Update unordered container links to the new draft standard. [SVN r2948] --- doc/buckets.qbk | 6 +++--- doc/intro.qbk | 10 +++++++--- doc/rationale.qbk | 6 +++--- doc/ref.xml | 16 ++++++++-------- include/boost/unordered_map.hpp | 8 ++++---- include/boost/unordered_set.hpp | 8 ++++---- 6 files changed, 29 insertions(+), 25 deletions(-) diff --git a/doc/buckets.qbk b/doc/buckets.qbk index 1e0b1315..9e3cec3f 100644 --- a/doc/buckets.qbk +++ b/doc/buckets.qbk @@ -73,9 +73,9 @@ initially set to 1.0. `max_load_factor` tells the container to change the maximum load factor, using your supplied hint as a suggestion. -TR1 doesn't actually require the container to pay much attention to this -value. The only time the load factor is required to be less than the maximum -is following a call to `rehash`. +The draft standard doesn't actually require the container to pay much attention +to this value. The only time the load factor is required to be less than the +maximum is following a call to `rehash`. It is not specified anywhere how other member functions affect the bucket count. But most implementations will invalidate the iterators whenever they change diff --git a/doc/intro.qbk b/doc/intro.qbk index b9db3ec9..25f1f392 100644 --- a/doc/intro.qbk +++ b/doc/intro.qbk @@ -1,5 +1,8 @@ -[def __tr1__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1745.pdf +[def __tr1__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf C++ Standard Library Technical Report]] +[def __draft__ + [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf + Working Draft of the C++ Standard]] [def __hash-table__ [@http://en.wikipedia.org/wiki/Hash_table hash table]] [def __hash-function__ [@http://en.wikipedia.org/wiki/Hash_function @@ -21,8 +24,9 @@ It might be slow to calculate, or even impossible. On the other hand, in a hash table, then elements aren't ordered - but you need an equality function and a hash function for the key. -So the __tr1__ provides the unordered associative containers, which are -implemented using hash tables. There are four containers to match the existing +So the __tr1__ introduced the unordered associative containers, which are +implemented using hash tables, and they have now been added to the __draft__. +There are four containers to match the existing associate containers. In the header <[headerref boost/unordered_set.hpp]>: template < diff --git a/doc/rationale.qbk b/doc/rationale.qbk index 47fd99b5..2d6d8a3c 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -4,9 +4,9 @@ [section:rationale Implementation Rationale] -From the start the intent of this library was to implement the unordred -containers in TR1, so the interface was fixed. But there are still some -implementation desicions to make. The priorities for the library are +From the start the intent of this library was to implement the unordered +containers in the draft standard, so the interface was fixed. But there are +still some implementation desicions to make. The priorities for the library are conformance to the standard and portability. [h2 Number of Buckets] diff --git a/doc/ref.xml b/doc/ref.xml index 0c72c0da..99f869f6 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -27,8 +27,8 @@ An unordered associative container that stores unique values. - For the normative reference see section 6.3 of - TR1 [n1836]. + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2009]. @@ -778,8 +778,8 @@ An unordered associative container that stores values. The same key can be stored multiple times. - For the normative reference see section 6.3 of - TR1 [n1836]. + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2009]. @@ -1537,8 +1537,8 @@ An unordered associative container that associates unique keys with another value. - For the normative reference see section 6.3 of - TR1 [n1836]. + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2009]. @@ -2327,8 +2327,8 @@ An unordered associative container that associates keys with another value. The same key can be stored multiple times. - For the normative reference see section 6.3 of - TR1 [n1836]. + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2009]. diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 009e7f9d..55d1a6ff 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -24,8 +24,8 @@ namespace boost { //! An unordered associative container that associates unique keys with another value. - /*! For full details see section 6.3.4.4 of the Technical report. - * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1836.pdf + /*! For full details see chapter 23 of the draft C++ standard. + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf */ template Date: Sun, 7 May 2006 10:06:47 +0000 Subject: [PATCH 029/175] Fix a bug in allocator_constructor. [SVN r2949] --- include/boost/unordered/detail/allocator.hpp | 21 +++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/include/boost/unordered/detail/allocator.hpp b/include/boost/unordered/detail/allocator.hpp index aac39b3a..8ff1c1f6 100644 --- a/include/boost/unordered/detail/allocator.hpp +++ b/include/boost/unordered/detail/allocator.hpp @@ -119,9 +119,10 @@ namespace boost { Allocator& alloc_; pointer ptr_; + bool constructed_; allocator_constructor(Allocator& a) - : alloc_(a), ptr_() + : alloc_(a), ptr_(), constructed_(false) { #if BOOST_WORKAROUND(BOOST_MSVC, < 1300) unordered_detail::reset(ptr_); @@ -129,23 +130,25 @@ namespace boost { } ~allocator_constructor() { - if (ptr_) alloc_.deallocate(ptr_, 1); + if(ptr_) { + if(constructed_) alloc_.destroy(ptr_); + alloc_.deallocate(ptr_, 1); + } } template - pointer construct(V const& v) { - BOOST_ASSERT(!ptr_); - pointer p = alloc_.allocate(1); - ptr_ = p; - alloc_.construct(p, v); - reset(ptr_); - return p; + void construct(V const& v) { + BOOST_ASSERT(!ptr_ && !constructed_); + ptr_ = alloc_.allocate(1); + alloc_.construct(ptr_, v); + constructed_ = true; } // no throw pointer release() { pointer p = ptr_; + constructed_ = false; reset(ptr_); return p; } From 9bd73e4ba7aed3d15c483a2626d2f384425cf460 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 7 May 2006 13:24:17 +0000 Subject: [PATCH 030/175] New version of the unordered associative containers, with a more efficient data structure for unordered_multimap/unordered_multiset that uses an extra pointer per node but makes most operations more efficient when equivalent nodes are present. Now uses macros instead of template metaprogramming to implement the differences between containers with equivalent and unique keys. Removed the erase_iterator stuff which complicated matters more than it helped. Now just using pointers with some helper functions. [SVN r2950] --- include/boost/unordered/detail/hash_table.hpp | 1879 +-------------- .../unordered/detail/hash_table_impl.hpp | 2112 +++++++++++++++++ include/boost/unordered_map.hpp | 22 +- include/boost/unordered_set.hpp | 18 +- 4 files changed, 2182 insertions(+), 1849 deletions(-) create mode 100644 include/boost/unordered/detail/hash_table_impl.hpp diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 8996cc1d..aabe0586 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -1,6 +1,6 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. -// Copyright (C) 2005 Daniel James +// Copyright (C) 2005-2006 Daniel James // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy @@ -13,6 +13,8 @@ # pragma once #endif +#define BOOST_UNORDERED_PARANOID + #include #include @@ -52,6 +54,12 @@ #define BOOST_HASH_BORLAND_BOOL(x) x #endif +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) +#define BOOST_HASH_MSVC_RESET_PTR(x) unordered_detail::reset(node_pointer_) +#else +#define BOOST_HASH_MSVC_RESET_PTR(x) +#endif + namespace boost { namespace unordered_detail { template struct type_wrapper {}; @@ -112,1824 +120,19 @@ namespace boost { { return std::pair(Dst1(x.first), Dst2(x.second)); } + } +} - // Hash Table Data - // - // Responsible for managing the hash buckets. Has no knowledge of hash - // functions or uniqueness. +#define BOOST_UNORDERED_HASH_EQUIVALENT 1 +#include +#undef BOOST_UNORDERED_HASH_EQUIVALENT - template - class hash_table_data - { - public: - class node; - class bucket; - - typedef std::size_t size_type; - - typedef Alloc value_allocator; - - typedef BOOST_DEDUCED_TYPENAME - boost::unordered_detail::rebind_wrap::type - node_allocator; - typedef BOOST_DEDUCED_TYPENAME - boost::unordered_detail::rebind_wrap::type - bucket_allocator; - - typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; - typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type node_ptr; - typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type bucket_ptr; - typedef BOOST_DEDUCED_TYPENAME allocator_reference::type reference; - -#if defined(BOOST_UNORDERED_PARANOID) - // If the allocator has the expected pointer types I take some liberties. - BOOST_STATIC_CONSTANT(bool, is_pointer_allocator = - (boost::mpl::and_< - boost::is_same, - boost::is_same - >::value)); - - typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c< - is_pointer_allocator, bucket_ptr, node_ptr>::type link_ptr; -#else - typedef bucket_ptr link_ptr; -#endif - - // Hash Bucket - // - // all no throw (memory management is performed by hash_table_data). - - class bucket - { - bucket& operator=(bucket const&); - public: - link_ptr next_; - - bucket() : next_() - { -#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) - unordered_detail::reset(next_); -#endif - } - - bucket(bucket const& x) : next_(x.next_) - { - // Only copy construct when allocating. - BOOST_ASSERT(!x.next_); - } - - bool empty() const - { - return !this->next_; - } - }; - - // Hash Node - // - // all no throw - - class node : public bucket - { - public: - node(value_type const& v) : bucket(), value_(v) {} - - value_type value_; - }; - -#if !defined(BOOST_UNORDERED_PARANOID) - class node_constructor - { - node_allocator& node_alloc_; - bucket_allocator& bucket_alloc_; - value_allocator value_alloc_; - - node_ptr ptr_; - bool value_allocated_; - bool bucket_allocated_; - - public: - - node_constructor(node_allocator& n, bucket_allocator& b) - : node_alloc_(n), bucket_alloc_(b), value_alloc_(n), - ptr_(), value_allocated_(false), bucket_allocated_(false) - { -#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) - unordered_detail::reset(ptr_); -#endif - } - - ~node_constructor() - { - if (ptr_) { - if (value_allocated_) - value_alloc_.destroy( - value_alloc_.address(ptr_->value_)); - if (bucket_allocated_) - bucket_alloc_.destroy( - bucket_alloc_.address(*ptr_)); - - node_alloc_.deallocate(ptr_, 1); - } - } - - template - void construct(V const& v) - { - BOOST_ASSERT(!ptr_); - value_allocated_ = bucket_allocated_ = false; - - ptr_ = node_alloc_.allocate(1); - - bucket_alloc_.construct(bucket_alloc_.address( - *ptr_), bucket()); - bucket_allocated_ = true; - - value_alloc_.construct(value_alloc_.address( - ptr_->value_), v); - value_allocated_ = true; - } - - // no throw - link_ptr release() - { - node_ptr p = ptr_; - unordered_detail::reset(ptr_); - return bucket_alloc_.address(*p); - } - - private: - node_constructor(node_constructor const&); - node_constructor& operator=(node_constructor const&); - }; -#else - class node_constructor - : public allocator_constructor - { - public: - node_constructor(node_allocator& n, bucket_allocator&) - : allocator_constructor(n); - }; -#endif - - class local_iterator_base - { - public: - link_ptr node_pointer_; - - local_iterator_base() - : node_pointer_() - { -#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) - unordered_detail::reset(node_pointer_); -#endif - } - - explicit local_iterator_base(link_ptr n) - : node_pointer_(n) {} - - bool not_finished() const - { - return node_pointer_ ? true : false; - } - - bool operator==(local_iterator_base const& x) const - { - return node_pointer_ == x.node_pointer_; - } - - bool operator!=(local_iterator_base const& x) const - { - return node_pointer_ != x.node_pointer_; - } - - reference operator*() const - { - BOOST_ASSERT(node_pointer_); - return static_cast(*node_pointer_).value_; - } - - void increment() - { - BOOST_ASSERT(node_pointer_); - node_pointer_ = node_pointer_->next_; - } - }; - - // Erase Iterator - // - // This is an internal 'iterator' (not an STL iterator) which is - // used to erase or move a node. - // - // All no throw. - - class erase_iterator - { - link_ptr* prev_ptr; - - public: - - explicit erase_iterator(bucket_ptr b) - : prev_ptr(&b->next_) {} - - void next() - { - prev_ptr = &(*prev_ptr)->next_; - } - - bool not_finished() const - { - return *prev_ptr ? true : false; - } - - value_type& operator*() const - { - return static_cast(**prev_ptr).value_; - } - - link_ptr& get_node() const - { - return *prev_ptr; - } - - bool operator!=(local_iterator_base const& y) - { - return *prev_ptr != y.node_pointer_; - } - }; - - class iterator_base - { - public: - bucket_ptr bucket_; - local_iterator_base local_; - - iterator_base() - : bucket_(), local_() {} - - iterator_base(bucket_ptr b) - : bucket_(b), local_(b->next_) {} - - iterator_base(bucket_ptr b, link_ptr n) - : bucket_(b), local_(n) {} - - iterator_base(bucket_ptr b, local_iterator_base const& it) - : bucket_(b), local_(it) {} - - local_iterator_base local() const - { - return local_iterator_base(local_); - } - - bool operator==(iterator_base const& x) const - { - return local_ == x.local_; - } - - bool operator!=(iterator_base const& x) const - { - return local_ != x.local_; - } - - reference operator*() const - { - return *local_; - } - - void increment() - { - BOOST_ASSERT(bucket_); - local_.increment(); - - while (!local_.node_pointer_) { - ++bucket_; - local_ = local_iterator_base(bucket_->next_); - } - } - }; - - // Member Variables - - node_allocator node_alloc_; - bucket_allocator bucket_alloc_; - - bucket_ptr buckets_; - size_type bucket_count_; - bucket_ptr cached_begin_bucket_; - size_type size_; - - // Constructor - - hash_table_data(size_type n, node_allocator const& a) - : node_alloc_(a), bucket_alloc_(a), - buckets_(), bucket_count_(next_prime(n)), - cached_begin_bucket_(), size_(0) - { - // The array constructor will clean up in the event of an - // exception. - allocator_array_constructor - constructor(bucket_alloc_); - constructor.construct(bucket(), bucket_count_ + 1); - - cached_begin_bucket_ = constructor.get() + bucket_count_; - - // Only release the buckets once everything is successfully - // done. - buckets_ = constructor.release(); - } - - // no throw - ~hash_table_data() - { - if(buckets_) { - if(buckets_[bucket_count_].next_) remove_end_marker(); - - for(size_type i = 0; i < bucket_count_; ++i) - delete_nodes(erase_iterator(buckets_ + i)); - - for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2) - bucket_alloc_.destroy(buckets_ + i2); - - bucket_alloc_.deallocate(buckets_, bucket_count_ + 1); - } - } - - void add_end_marker() - { - BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && - !buckets_[bucket_count_].next_); - -#if !defined(BOOST_UNORDERED_PARANOID) - buckets_[bucket_count_].next_ = buckets_ + bucket_count_; -#else - if(is_pointer_allocator) { - buckets_[bucket_count_].next_ = buckets_ + bucket_count_; - } - else { - // This seems very wasteful, but I can't think of a better - // way to create an end node and work with all allocators. - // Although, I might change it to do something different - // when typename node_allocator::pointer == node*. - buckets_[bucket_count_].next_ = node_alloc_.allocate(1); - } -#endif - } - - void move_end_marker(hash_table_data& src) - { - BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && - !buckets_[bucket_count_].next_); - BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(src.buckets_) && - BOOST_HASH_BORLAND_BOOL(src.buckets_[src.bucket_count_].next_)); - -#if !defined(BOOST_UNORDERED_PARANOID) - buckets_[bucket_count_].next_ = buckets_ + bucket_count_; -#else - if(is_pointer_allocator) { - buckets_[bucket_count_].next_ = buckets_ + bucket_count_; - } - else { - buckets_[bucket_count_].next_ - = src.buckets_[src.bucket_count_].next_; - } -#endif - - unordered_detail::reset(src.buckets_[src.bucket_count_].next_); - } - - void remove_end_marker() - { - BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && - BOOST_HASH_BORLAND_BOOL(buckets_[bucket_count_].next_)); - -#if defined(BOOST_UNORDERED_PARANOID) - if(!is_pointer_allocator) - node_alloc_.deallocate(static_cast( - buckets_[bucket_count_].next_), 1); -#endif - - unordered_detail::reset(buckets_[bucket_count_].next_); - } - - private: - - hash_table_data(hash_table_data const&); - hash_table_data& operator=(hash_table_data const&); - - public: - - // no throw - void swap(hash_table_data& other) - { - std::swap(buckets_, other.buckets_); - std::swap(bucket_count_, other.bucket_count_); - std::swap(cached_begin_bucket_, other.cached_begin_bucket_); - std::swap(size_, other.size_); - } - - // Return the bucket index for a hashed value. - // - // no throw - size_type index_from_hash(size_type hashed) const - { - return hashed % bucket_count_; - } - - // Begin & End - // - // no throw - - iterator_base begin() const - { - return size_ - ? iterator_base(cached_begin_bucket_) - : end(); - } - - iterator_base end() const - { - return iterator_base(buckets_ + bucket_count_); - } - - local_iterator_base begin(size_type n) const - { - return local_iterator_base(buckets_[n].next_); - } - - local_iterator_base end(size_type) const - { - return local_iterator_base(); - } - - local_iterator_base begin(bucket_ptr b) const - { - return local_iterator_base(b->next_); - } - - // Bucket Size - - // no throw - size_type bucket_size(size_type n) const - { - std::size_t count = 0; - local_iterator_base it1 = begin(n); - while(it1.not_finished()) { - ++count; - it1.increment(); - } - return count; - } - - // Erase iterator - // - // Find the pointer to a node, for use when erasing. - // - // no throw - - erase_iterator get_for_erase(iterator_base r) const - { - erase_iterator it(r.bucket_); - local_iterator_base pos(r.local()); - while(it != pos) it.next(); - return it; - } - - // Link/Unlink/Move Node - // - // For adding nodes to buckets, removing them and moving them to a - // new bucket. - // - // no throw - - void link_node(link_ptr node, local_iterator_base pos) - { - node->next_ = pos.node_pointer_->next_; - pos.node_pointer_->next_ = node; - ++size_; - } - - void link_node(link_ptr node, bucket_ptr base) - { - node->next_ = base->next_; - base->next_ = node; - ++size_; - if(base < cached_begin_bucket_) cached_begin_bucket_ = base; - } - - void unlink_node(erase_iterator it) - { - it.get_node() = it.get_node()->next_; - --size_; - } - - void move_node(hash_table_data& src, erase_iterator it, - bucket_ptr dst) - { - link_ptr n = it.get_node(); - src.unlink_node(it); - link_node(n, dst); - } - - // throws, strong exception-safety: - link_ptr construct_node(value_type const& v) - { - node_constructor a(node_alloc_, bucket_alloc_); - a.construct(v); - return a.release(); - } - - // Create Node - // - // Create a node and add it to the buckets in the given position. - // - // strong exception safety. - - iterator_base create_node(value_type const& v, bucket_ptr base) - { - // throws, strong exception-safety: - link_ptr node = construct_node(v); - - // Rest is no throw - link_node(node, base); - return iterator_base(base, node); - } - - iterator_base create_node(value_type const& v, iterator_base position) - { - // throws, strong exception-safety: - link_ptr node = construct_node(v); - - // Rest is no throw - link_node(node, position.local()); - return iterator_base(position.bucket_, node); - } - - iterator_base create_node(value_type const& v, - bucket_ptr base, local_iterator_base position) - { - // throws, strong exception-safety: - link_ptr node = construct_node(v); - - // Rest is no throw - if(position.not_finished()) - link_node(node, position); - else - link_node(node, base); - - return iterator_base(base, node); - } - - // Delete Node - // - // Remove a node, or a range of nodes, from a bucket, and destory - // them. - // - // no throw - - void delete_node(erase_iterator it) - { - node_ptr n = node_alloc_.address( - static_cast(*it.get_node())); - unlink_node(it); - - node_alloc_.destroy(n); - node_alloc_.deallocate(n, 1); - } - - void delete_nodes(erase_iterator begin, local_iterator_base end) - { - while(begin != end) { - BOOST_ASSERT(begin.not_finished()); - delete_node(begin); - } - } - - void delete_nodes(erase_iterator begin) - { - while(begin.not_finished()) delete_node(begin); - } - - // Clear - // - // Remove all the nodes. - // - // no throw - // - // TODO: If delete_nodes did throw (it shouldn't but just for - // argument's sake), could this leave cached_begin_bucket_ pointing - // at an empty bucket? - - void clear() - { - bucket_ptr end = buckets_ + bucket_count_; - while(cached_begin_bucket_ != end) { - delete_nodes(erase_iterator(cached_begin_bucket_)); - ++cached_begin_bucket_; - } - BOOST_ASSERT(!size_); - } - - // Erase - // - // Return type of erase(const_iterator): - // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1753.html#130 - // - // no throw - - iterator_base erase(iterator_base r) - { - BOOST_ASSERT(r != end()); - iterator_base next = r; - next.increment(); - delete_node(get_for_erase(r)); - // r has been invalidated but its bucket is still valid - recompute_begin_bucket(r.bucket_, next.bucket_); - return next; - } - - iterator_base erase(iterator_base r1, iterator_base r2) - { - if(r1 != r2) - { - BOOST_ASSERT(r1 != end()); - - if (r1.bucket_ == r2.bucket_) { - delete_nodes(get_for_erase(r1), r2.local()); - - // No need to call recompute_begin_bucket because - // the nodes are only deleted from one bucket, which - // still contains r2 after the erase. - BOOST_ASSERT(!r1.bucket_->empty()); - } - else { - BOOST_ASSERT(r1.bucket_ < r2.bucket_); - - delete_nodes(get_for_erase(r1)); - - for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i) - delete_nodes(erase_iterator(i)); - - delete_nodes(erase_iterator(r2.bucket_), r2.local()); - - // r1 has been invalidated but its bucket is still - // valid. - recompute_begin_bucket(r1.bucket_, r2.bucket_); - } - } - - return r2; - } - - // recompute_begin_bucket - // - // After an erase cached_begin_bucket_ might be left pointing to - // an empty bucket, so this is called to update it - // - // no throw - - void recompute_begin_bucket(bucket_ptr ptr) - { - BOOST_ASSERT(!(ptr < cached_begin_bucket_)); - - if(ptr == cached_begin_bucket_) - { - if (size_ != 0) { - while (cached_begin_bucket_->empty()) - ++cached_begin_bucket_; - } else { - cached_begin_bucket_ = buckets_ + bucket_count_; - } - } - } - - // This is called when a range has been erased - // - // no throw - - void recompute_begin_bucket(bucket_ptr i, bucket_ptr j) - { - BOOST_ASSERT(!(i < cached_begin_bucket_) && !(j < i)); - BOOST_ASSERT(j == buckets_ + bucket_count_ || !j->empty()); - - if(i == cached_begin_bucket_ && i->empty()) - cached_begin_bucket_ = j; - } - }; - -#if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) - template <> - class hash_table_data - { - public: - typedef int size_type; - typedef int iterator_base; - }; -#endif - - template - class hash_table - : public hash_table_data - { - typedef hash_table_data data; - - typedef typename data::node_constructor node_constructor; - typedef typename data::link_ptr link_ptr; - - public: - - typedef BOOST_DEDUCED_TYPENAME data::value_allocator value_allocator; - typedef BOOST_DEDUCED_TYPENAME data::node_allocator node_allocator; - typedef BOOST_DEDUCED_TYPENAME data::bucket_ptr bucket_ptr; - typedef BOOST_DEDUCED_TYPENAME data::erase_iterator erase_iterator; - - // Type definitions - - typedef KeyType key_type; - typedef Hash hasher; - typedef Pred key_equal; - typedef ValueType value_type; - typedef std::size_t size_type; - - // iterators - - typedef BOOST_DEDUCED_TYPENAME data::local_iterator_base local_iterator_base; - typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; - - private: - - // From the compressed functions docs: - // - // "Finally, a word of caution for Visual C++ 6 users: if either - // argument is an empty type, then assigning to that member will - // produce memory corruption, unless the empty type has a "do - // nothing" assignment operator defined. This is due to a bug in - // the way VC6 generates implicit assignment operators." - // - // Nice. - // - // So use std::pair for Visual C++. - - class functions - { -#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) - boost::compressed_pair functions_; -#else - std::pair functions_; -#endif - - public: - - functions(hasher const& h, key_equal const& k) - : functions_(h, k) {} - - hasher const& hash_function() const - { -#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) - return functions_.first(); -#else - return functions_.first; -#endif - } - - key_equal const& key_eq() const - { -#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) - return functions_.second(); -#else - return functions_.second; -#endif - } - }; - - // Both hasher and key_equal's copy/assign can throw so double - // buffering is used to copy them. func_ points to the currently - // active function objects. - - typedef functions hash_table::*functions_ptr; - - functions func1_; - functions func2_; - functions_ptr func_; - - float mlf_; - size_type max_load_; - - public: - - // Constructors - - hash_table(size_type n, - hasher const& hf, key_equal const& eq, - value_allocator const& a) - : data(n, a), // throws, cleans itself up - func1_(hf, eq), // throws " " - func2_(hf, eq), // throws " " - func_(&hash_table::func1_), // no throw - mlf_(1.0f) // no throw - { - this->add_end_marker(); - calculate_max_load(); // no throw - } - - // Construct from iterators - - // initial_size - // - // A helper function for the copy constructor to calculate how many - // nodes will be created if the iterator's support it. Might get it - // totally wrong for containers with unique keys. - // - // no throw - - template - std::size_t initial_size(I i, I j, size_type x, - boost::random_access_traversal_tag) - { - // max load factor isn't set yet, but when it is, it'll be 1.0. - size_type n = j - i + 1; - return n > x ? n : x; - }; - - template - std::size_t initial_size(I, I, size_type n, - boost::incrementable_traversal_tag) - { - return n; - }; - - template - std::size_t initial_size(I i, I j, size_type x) - { - BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type - iterator_traversal_tag; - return initial_size(i, j, x, iterator_traversal_tag); - }; - - template - hash_table(I i, I j, size_type n, - hasher const& hf, key_equal const& eq, - value_allocator const& a) - : data(initial_size(i, j, n), a), // throws, cleans itself up - func1_(hf, eq), // throws " " - func2_(hf, eq), // throws " " - func_(&hash_table::func1_), // no throw - mlf_(1.0f) // no throw - { - this->add_end_marker(); - calculate_max_load(); // no throw - - // This can throw, but hash_table_data's destructor will clean - // up. - insert(i, j); - } - - // Copy Construct - - hash_table(hash_table const& x) - : data(x.min_buckets_for_size(x.size()), x.node_alloc_), // throws - func1_(x.current_functions()), // throws - func2_(x.current_functions()), // throws - func_(&hash_table::func1_), // no throw - mlf_(x.mlf_) // no throw - { - this->add_end_marker(); - calculate_max_load(); // no throw - - // This can throw, but hash_table_data's destructor will clean - // up. - copy_buckets(x, *this, current_functions()); - } - - // Assign - // - // basic exception safety, if copy_functions of reserver throws - // the container is left in a sane, empty state. If copy_buckets - // throws the container is left with whatever was successfully - // copied. - - hash_table& operator=(hash_table const& x) - { - if(this != &x) - { - // TODO: I could rewrite this to use the existing nodes. - this->clear(); // no throw - func_ = copy_functions(x); // throws, strong - mlf_ = x.mlf_; // no throw - calculate_max_load(); // no throw - reserve(x.size()); // throws - copy_buckets(x, *this, current_functions()); // throws - } - - return *this; - } - - // Swap - // - // Swap's behaviour when allocators aren't equal is in dispute, see - // this paper for full details: - // - // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1599.html - // - // It lists 3 possible behaviours: - // - // 1 - If the allocators aren't equal then throw an exception. - // 2 - Reallocate the elements in the containers with the - // appropriate allocators - messing up exception safety in - // the process. - // 3 - Swap the allocators, hoping that the allocators have a - // no-throw swap. - // - // The paper recommends #3. - // - // I've implemented all three, but actived #3 by default, to change - // it '#define BOOST_UNORDERED_SWAP_METHOD n' where n is the option. - // - // ---------------------------------------------------------------- - // - // Strong exception safety (might change unused function objects) - // - // Can throw if hash or predicate object's copy constructor throws. - // If allocators are unequal: - // Method 1: always throws. - // Method 2: can throw if copying throws - // (memory allocation/hash function object) - // Method 3: Can throw if allocator's swap throws - // (TODO: This one is broken right now, double buffering?) - - void swap(hash_table& x) - { - // This only effects the function objects that aren't in use - // so it is strongly exception safe, via. double buffering. - functions_ptr new_func_this = copy_functions(x); // throws - functions_ptr new_func_that = x.copy_functions(*this); // throws - - if(this->node_alloc_ == x.node_alloc_) { - this->data::swap(x); // no throw - } - else { -#if BOOST_UNORDERED_SWAP_METHOD == 1 - throw std::runtime_error( - "Swapping containers with different allocators.");; -#elif BOOST_UNORDERED_SWAP_METHOD == 2 - // Create new buckets in separate hash_table_data objects - // which will clean up if any of this throws. - data new_this(x.min_buckets_for_size(x.size_), - this->node_alloc_); // throws - copy_buckets(x, new_this, this->*new_func_this); // throws - - data new_that(min_buckets_for_size(this->size_), - x.node_alloc_); // throws - x.copy_buckets(*this, new_that, x.*new_func_that); // throws - - // Start updating the data here, no throw from now on. - new_this.move_end_marker(*this); // no throw - new_that.move_end_marker(x); // no throw - this->data::swap(new_this); // no throw - x.data::swap(new_that); // no throw -#elif BOOST_UNORDERED_SWAP_METHOD == 3 - hash_swap(this->node_alloc_, - x.node_alloc_); // no throw, or is it? - hash_swap(this->bucket_alloc_, - x.bucket_alloc_); // no throw, or is it? - this->data::swap(x); // no throw -#else -#error "Invalid swap method" -#endif - } - - // We've made it, the rest is no throw. - std::swap(mlf_, x.mlf_); - - func_ = new_func_this; - x.func_ = new_func_that; - - calculate_max_load(); - x.calculate_max_load(); - } - - private: - - functions const& current_functions() const - { - return this->*func_; - } - - // This copies the given function objects into the currently unused - // function objects and returns a pointer, that func_ can later be - // set to, to commit the change. - // - // Strong exception safety (since only usued function objects are - // changed). - functions_ptr copy_functions(hash_table const& x) - { - // no throw: - functions_ptr ptr = func_ == &hash_table::func1_ - ? &hash_table::func2_ : &hash_table::func1_; - // throws, functions not in use, so strong - this->*ptr = x.current_functions(); - return ptr; - } - - public: - - // accessors - - // TODO: This creates an unnecessary copy. - // no throw - value_allocator get_allocator() const - { - return this->node_alloc_; - } - - // no throw - hasher const& hash_function() const - { - return current_functions().hash_function(); - } - - // no throw - key_equal const& key_eq() const - { - return current_functions().key_eq(); - } - - // no throw - size_type size() const - { - return this->size_; - } - - // no throw - bool empty() const - { - return this->size_ == 0; - } - - // no throw - size_type max_size() const - { - // size < mlf_ * count - return float_to_size_t(ceil( - max_bucket_count() * mlf_)) - 1; - } - - // strong safety - size_type bucket(key_type const& k) const - { - // hash_function can throw: - return this->index_from_hash(hash_function()(k)); - } - - // strong safety - bucket_ptr get_bucket(key_type const& k) const - { - return this->buckets_ + bucket(k); - } - - // no throw - size_type bucket_count() const - { - return this->bucket_count_; - } - - // no throw - size_type max_bucket_count() const - { - // -1 to account for the end marker. - return prev_prime(this->bucket_alloc_.max_size() - 1); - } - - private: - - // no throw - size_type min_buckets_for_size(size_type n) const - { - BOOST_ASSERT(mlf_ != 0); - - using namespace std; - - // From 6.3.1/13: - // size < mlf_ * count - // => count > size / mlf_ - // - // Or from rehash post-condition: - // count > size / mlf_ - return static_cast(floor(n / mlf_)) + 1; - } - - // no throw - void calculate_max_load() - { - using namespace std; - - // From 6.3.1/13: - // Only resize when size >= mlf_ * count - max_load_ = float_to_size_t(ceil(mlf_ * this->bucket_count_)); - } - - // basic exception safety - bool reserve(size_type n) - { - bool need_to_reserve = n >= max_load_; - // throws - basic: - if (need_to_reserve) rehash_impl(min_buckets_for_size(n)); - BOOST_ASSERT(n < max_load_); - return need_to_reserve; - } - - public: - - // no throw - float max_load_factor() const - { - return mlf_; - } - - // no throw - // - // TODO: the argument is a hint. So don't use it if it's - // unreasonably small. - void max_load_factor(float z) - { - BOOST_ASSERT(z > 0); - mlf_ = z; - calculate_max_load(); - } - - // no throw - float load_factor() const - { - BOOST_ASSERT(this->bucket_count_ != 0); - return static_cast(this->size_) - / static_cast(this->bucket_count_); - } - - private: - - // key extractors - - // no throw - static key_type const& extract_key(value_type const& v) - { - return extract(v, (type_wrapper*)0); - } - - static key_type const& extract(value_type const& v, - type_wrapper*) - { - return v; - } - - static key_type const& extract(value_type const& v, - void*) - { - return v.first; - } - - public: - - // if hash function throws, basic exception safety - // strong otherwise. - void rehash(size_type n) - { - using namespace std; - - // no throw: - size_type min_size = min_buckets_for_size(size()); - // basic/strong: - rehash_impl(min_size > n ? min_size : n); - - BOOST_ASSERT(bucket_count() > size() / max_load_factor() - && bucket_count() >= n); - } - - private: - - // if hash function throws, basic exception safety - // strong otherwise - void rehash_impl(size_type n) - { - n = next_prime(n); // no throw - - if (n == bucket_count()) // no throw - return; - - data new_buckets(n, this->node_alloc_); // throws, seperate - move_buckets(*this, new_buckets); // basic/no throw - new_buckets.swap(*this); // no throw - calculate_max_load(); // no throw - } - - // move_buckets & copy_buckets - // - // Note: Because equivalent elements are already - // adjacent to each other in the existing buckets, this - // simple rehashing technique is sufficient to ensure - // that they remain adjacent to each other in the new - // buckets (but in reverse order). - // - // if the hash function throws, basic excpetion safety - // no throw otherwise - - void move_buckets(data& src, data& dst) - { - BOOST_ASSERT(dst.size_ == 0); - BOOST_ASSERT(src.node_alloc_ == dst.node_alloc_); - - bucket_ptr end = src.buckets_ + src.bucket_count_; // no throw - - for(; src.cached_begin_bucket_ != end; // no throw - ++src.cached_begin_bucket_) { - erase_iterator it(src.cached_begin_bucket_); // no throw - while(it.not_finished()) { - // This next line throws iff the hash function throws. - bucket_ptr dst_bucket = dst.buckets_ + - dst.index_from_hash( - hash_function()(extract_key(*it))); - - dst.move_node(src, it, dst_bucket); // no throw - } - } - - // Move the end marker from the source to destination. - // Now destination is valid, source is not. - dst.move_end_marker(src); - } - - // basic excpetion safety, will leave dst partially filled. - - static void copy_buckets(data const& src, data& dst, functions const& f) - { - BOOST_ASSERT(dst.size_ == 0); - // no throw: - bucket_ptr end = src.buckets_ + src.bucket_count_; - hasher const& hf = f.hash_function(); - - // no throw: - for(bucket_ptr i = src.cached_begin_bucket_; i != end; ++i) { - // no throw: - for(local_iterator_base it = src.begin(i); - it.not_finished(); it.increment()) { - // hash function can throw. - bucket_ptr dst_bucket = dst.buckets_ + - dst.index_from_hash(hf(extract_key(*it))); - // throws, strong - dst.create_node(*it, dst_bucket); - } - } - } - - public: - - // Insert functions - // - // basic exception safety, if hash function throws - // strong otherwise. - - // if hash function throws, basic exception safety - // strong otherwise - value_type& operator[](key_type const& k) - { - BOOST_STATIC_ASSERT(!EquivalentKeys); - BOOST_STATIC_ASSERT(( - !boost::is_same::value)); - typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type; - - bucket_ptr bucket = get_bucket(k); - local_iterator_base node = find_iterator(bucket, k); - - if (node.not_finished()) - return *node; - else - { - // Effects only in this block: - - // Create the node before rehashing in case it throws. - // throws, no side effects: - node_constructor a(this->node_alloc_, this->bucket_alloc_); - a.construct(value_type(k, mapped_type())); - - if (reserve(size() + 1)) // basic/strong - bucket = get_bucket(k); // throws, strong - - link_ptr node = a.release(); - this->link_node(node, bucket); - - return *local_iterator_base(node); - } - } - - // Insert for containers with equivalent keys - - private: - - // Insert node without checking if a resize is necessary. - - // strong exception safety. - iterator_base unchecked_insert_equivalent(value_type const& v) - { - key_type const& k = extract_key(v); - bucket_ptr bucket = get_bucket(k); - local_iterator_base position = find_iterator(bucket, k); - - // No effects until here. - return this->create_node(v, bucket, position); // throws, strong - } - - // strong exception safety - iterator_base unchecked_insert_equivalent(iterator_base const& it, - value_type const& v) - { - // Condition throws, no side effects. - if(it != this->end() && equal(extract_key(v), *it)) { - return this->create_node(v, it); // throws, strong - } - else { - return unchecked_insert_equivalent(v); // throws, strong - } - } - - public: - - // if hash function throws, basic exception safety - // strong otherwise - iterator_base insert_equivalent(value_type const& v) - { - key_type const& k = extract_key(v); - size_type hash_value = hash_function()(k); - bucket_ptr bucket = this->buckets_ - + this->index_from_hash(hash_value); - local_iterator_base position = find_iterator(bucket, k); - - // Create the node before rehashing in case it throws. - // throws, no side effects: - node_constructor a(this->node_alloc_, this->bucket_alloc_); - a.construct(v); - - // strong/no throw: - if(reserve(size() + 1)) // basic/strong - bucket = this->buckets_ + this->index_from_hash(hash_value); - - // No throw from here. - - link_ptr node = a.release(); - - // I'm relying on local_iterator_base not being invalidated by - // the rehash here. - if(position.not_finished()) - this->link_node(node, position); - else - this->link_node(node, bucket); - - return iterator_base(bucket, node); - } - - // if hash function throws, basic exception safety - // strong otherwise - iterator_base insert_equivalent(iterator_base const& it, value_type const& v) - { - if (it != this->end() && equal(extract_key(v), *it)) { // throws, no side effects - // Create the node before rehashing in case it throws. - // throws, no side effects: - node_constructor a(this->node_alloc_, this->bucket_alloc_); - a.construct(v); - - // The hash function can throw in get_bucket, but that's okay - // because then only basic exception safety is required. - bucket_ptr base = reserve(size() + 1) ? - get_bucket(extract_key(v)) : it.bucket_; - - link_ptr node = a.release(); - this->link_node(node, it.local()); - - return iterator_base(base, node); - } - else { - return insert_equivalent(v); // basic/strong - } - } - - public: - - // Insert for containers with unique keys - - // if hash function throws, basic exception safety - // strong otherwise - std::pair insert_unique(value_type const& v) - { - // Throws, but no side effects in this initial code - key_type const& k = extract_key(v); - size_type hash_value = hash_function()(k); - bucket_ptr bucket = this->buckets_ - + this->index_from_hash(hash_value); - local_iterator_base pos = find_iterator(bucket, k); - - if (pos.not_finished()) { // no throw - // Found existing key, return it. - return std::pair( - iterator_base(bucket, pos), false); // no throw - - } else { - // Doesn't already exist, add to bucket. - // Data is only changed in this block. - - // Create the node before rehashing in case it throws. - // throws, no side effects: - node_constructor a(this->node_alloc_, this->bucket_alloc_); - a.construct(v); - - // If we resize, then need to recalculate bucket. - if(reserve(size() + 1)) // throws, basic/strong - bucket = this->buckets_ + this->index_from_hash(hash_value); - - link_ptr node = a.release(); - this->link_node(node, bucket); - - return std::pair( - iterator_base(bucket, node), true); // throws, strong - } - } - - // if hash function throws, basic exception safety - // strong otherwise - iterator_base insert_unique(iterator_base const& it, value_type const& v) - { - // If we are given an iterator pointer to the given key, - // then just return it. - if(it != this->end() && equal(extract_key(v), *it)) // throws, strong - return it; - else - return insert_unique(v).first; // basic, if hash - } - - private: - - // if hash function throws, basic exception safety - // strong otherwise - void insert(value_type const& v) - { - if(EquivalentKeys) - insert_equivalent(v); - else - insert_unique(v); - } - - // if hash function throws, basic exception safety - // strong otherwise - void unchecked_insert(value_type const& v) - { - if(EquivalentKeys) - unchecked_insert_equivalent(v); - else - insert_unique(v); - } - - // Insert from iterators - - private: - - // if hash function throws, or inserting > 1 element, basic exception safety - // strong otherwise - template - void insert_for_range(I i, I j, - boost::random_access_traversal_tag) - { - if(EquivalentKeys) { - std::size_t distance = j - i; - if(distance == 1) { - insert(*i); - } - else { - reserve(size() + distance); // basic/strong - for (; i != j; ++i) unchecked_insert(*i); // strong - } - } - else { - for (; i != j; ++i) insert_unique(*i); // basic/strong - } - } - - // if hash function throws, or inserting > 1 element, basic exception safety - // strong otherwise - template - void insert_for_range(I i, I j, - boost::incrementable_traversal_tag) - { - for (; i != j; ++i) insert(*i); // basic/strong - } - - public: - - // if hash function throws, or inserting > 1 element, basic exception safety - // strong otherwise - template - void insert(InputIterator i, InputIterator j) - { - BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type - iterator_traversal_tag; - insert_for_range(i, j, iterator_traversal_tag); - } - - public: - - // erase - - // no throw - iterator_base erase(iterator_base const& r) - { - return this->data::erase(r); - } - - // strong exception safety - size_type erase(key_type const& k) - { - // No side effects in initial section - bucket_ptr bucket = get_bucket(k); - size_type count = 0; - erase_iterator it(find_for_erase(bucket, k)); - - // Rest is no throw, side effects only after this point. - if (it.not_finished()) { - if (EquivalentKeys) { - do { - ++count; - this->delete_node(it); - } while(it.not_finished() && equal(k, *it)); - } - else { - count = 1; - this->delete_node(it); - } - - this->recompute_begin_bucket(bucket); - } - - return count; - } - - // no throw - iterator_base erase(iterator_base const& r1, iterator_base const& r2) - { - return this->data::erase(r1, r2); - } - - // count - // - // strong exception safety, no side effects - size_type count(key_type const& k) const - { - local_iterator_base it = find_iterator(k); // throws, strong - size_type count = 0; - - if(it.not_finished()) { - if(EquivalentKeys) { - do { - ++count; - it.increment(); - } while (it.not_finished() && equal(k, *it)); // throws, strong - } - else { - count = 1; - } - } - - return count; - } - - // find - // - // strong exception safety, no side effects - iterator_base find(key_type const& k) const - { - bucket_ptr bucket = get_bucket(k); - local_iterator_base it = find_iterator(bucket, k); - - if (it.not_finished()) - return iterator_base(bucket, it); - else - return this->end(); - } - - // equal_range - // - // strong exception safety, no side effects - std::pair equal_range(key_type const& k) const - { - bucket_ptr bucket = get_bucket(k); - local_iterator_base it = find_iterator(bucket, k); - if (it.not_finished()) { - local_iterator_base last = it; - - if(EquivalentKeys) { - local_iterator_base next = last; - next.increment(); - - while(next.not_finished() && equal(k, *next)) { - last = next; - next.increment(); - } - } - - iterator_base first(iterator_base(bucket, it)); - iterator_base second(iterator_base(bucket, last)); - second.increment(); - return std::pair(first, second); - } - else { - return std::pair( - this->end(), this->end()); - } - } - - private: - - // strong exception safety, no side effects - bool equal(key_type const& k, value_type const& v) const - { - return key_eq()(k, extract_key(v)); - } - - // strong exception safety, no side effects - local_iterator_base find_iterator(key_type const& k) const - { - return find_iterator(get_bucket(k), k); - } - - // strong exception safety, no side effects - local_iterator_base find_iterator(bucket_ptr bucket, - key_type const& k) const - { - local_iterator_base it = this->begin(bucket); - while (it.not_finished() && !equal(k, *it)) - it.increment(); - - return it; - } - - // strong exception safety, no side effects - erase_iterator find_for_erase(bucket_ptr bucket, key_type const& k) - const - { - erase_iterator it(bucket); - while(it.not_finished() && !equal(k, *it)) - it.next(); - - return it; - } - }; - - // Iterators - - template class hash_iterator; - template class hash_const_iterator; - template class hash_local_iterator; - template class hash_const_local_iterator; - class iterator_access; - - // Local Iterators - // - // all no throw - - template - class hash_local_iterator - : public boost::iterator < - std::forward_iterator_tag, - BOOST_DEDUCED_TYPENAME allocator_value_type::type, - std::ptrdiff_t, - BOOST_DEDUCED_TYPENAME allocator_pointer::type, - BOOST_DEDUCED_TYPENAME allocator_reference::type > - { - public: - typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; - - private: - typedef BOOST_DEDUCED_TYPENAME hash_table_data::local_iterator_base base; - typedef hash_const_local_iterator const_local_iterator; - - friend class hash_const_local_iterator; - base base_; - - public: - hash_local_iterator() : base_() {} - explicit hash_local_iterator(base x) : base_(x) {} - BOOST_DEDUCED_TYPENAME allocator_reference::type operator*() const - { return *base_; } - value_type* operator->() const { return &*base_; } - hash_local_iterator& operator++() { base_.increment(); return *this; } - hash_local_iterator operator++(int) { hash_local_iterator tmp(base_); base_.increment(); return tmp; } - bool operator==(hash_local_iterator x) const { return base_ == x.base_; } - bool operator==(const_local_iterator x) const { return base_ == x.base_; } - bool operator!=(hash_local_iterator x) const { return base_ != x.base_; } - bool operator!=(const_local_iterator x) const { return base_ != x.base_; } - }; - - template - class hash_const_local_iterator - : public boost::iterator < - std::forward_iterator_tag, - BOOST_DEDUCED_TYPENAME allocator_value_type::type, - std::ptrdiff_t, - BOOST_DEDUCED_TYPENAME allocator_const_pointer::type, - BOOST_DEDUCED_TYPENAME allocator_const_reference::type > - { - public: - typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; - - private: - typedef BOOST_DEDUCED_TYPENAME hash_table_data::local_iterator_base base; - typedef hash_local_iterator local_iterator; - friend class hash_local_iterator; - base base_; - - public: - hash_const_local_iterator() : base_() {} - explicit hash_const_local_iterator(base x) : base_(x) {} - hash_const_local_iterator(local_iterator x) : base_(x.base_) {} - BOOST_DEDUCED_TYPENAME allocator_const_reference::type - operator*() const { return *base_; } - value_type const* operator->() const { return &*base_; } - hash_const_local_iterator& operator++() { base_.increment(); return *this; } - hash_const_local_iterator operator++(int) { hash_const_local_iterator tmp(base_); base_.increment(); return tmp; } - bool operator==(local_iterator x) const { return base_ == x.base_; } - bool operator==(hash_const_local_iterator x) const { return base_ == x.base_; } - bool operator!=(local_iterator x) const { return base_ != x.base_; } - bool operator!=(hash_const_local_iterator x) const { return base_ != x.base_; } - }; - - // iterators - // - // all no throw - - - template - class hash_iterator - : public boost::iterator < - std::forward_iterator_tag, - BOOST_DEDUCED_TYPENAME allocator_value_type::type, - std::ptrdiff_t, - BOOST_DEDUCED_TYPENAME allocator_pointer::type, - BOOST_DEDUCED_TYPENAME allocator_reference::type > - { - public: - typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; - - private: - typedef BOOST_DEDUCED_TYPENAME hash_table_data::iterator_base base; - typedef hash_const_iterator const_iterator; - friend class hash_const_iterator; - base base_; - - public: - - hash_iterator() : base_() {} - explicit hash_iterator(base const& x) : base_(x) {} - BOOST_DEDUCED_TYPENAME allocator_reference::type - operator*() const { return *base_; } - value_type* operator->() const { return &*base_; } - hash_iterator& operator++() { base_.increment(); return *this; } - hash_iterator operator++(int) { hash_iterator tmp(base_); base_.increment(); return tmp; } - bool operator==(hash_iterator const& x) const { return base_ == x.base_; } - bool operator==(const_iterator const& x) const { return base_ == x.base_; } - bool operator!=(hash_iterator const& x) const { return base_ != x.base_; } - bool operator!=(const_iterator const& x) const { return base_ != x.base_; } - }; - - template - class hash_const_iterator - : public boost::iterator < - std::forward_iterator_tag, - BOOST_DEDUCED_TYPENAME allocator_value_type::type, - std::ptrdiff_t, - BOOST_DEDUCED_TYPENAME allocator_const_pointer::type, - BOOST_DEDUCED_TYPENAME allocator_const_reference::type > - { - public: - typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; - - private: - typedef BOOST_DEDUCED_TYPENAME hash_table_data::iterator_base base; - typedef hash_iterator iterator; - friend class hash_iterator; - friend class iterator_access; - base base_; - - public: - - hash_const_iterator() : base_() {} - explicit hash_const_iterator(base const& x) : base_(x) {} - hash_const_iterator(iterator const& x) : base_(x.base_) {} - BOOST_DEDUCED_TYPENAME allocator_const_reference::type - operator*() const { return *base_; } - value_type const* operator->() const { return &*base_; } - hash_const_iterator& operator++() { base_.increment(); return *this; } - hash_const_iterator operator++(int) { hash_const_iterator tmp(base_); base_.increment(); return tmp; } - bool operator==(iterator const& x) const { return base_ == x.base_; } - bool operator==(hash_const_iterator const& x) const { return base_ == x.base_; } - bool operator!=(iterator const& x) const { return base_ != x.base_; } - bool operator!=(hash_const_iterator const& x) const { return base_ != x.base_; } - }; +#define BOOST_UNORDERED_HASH_EQUIVALENT 0 +#include +#undef BOOST_UNORDERED_HASH_EQUIVALENT +namespace boost { + namespace unordered_detail { class iterator_access { public: @@ -1940,24 +143,46 @@ namespace boost { }; template - class hash_types + class Hash, class Pred, class Alloc> + class hash_types_unique_keys { public: typedef BOOST_DEDUCED_TYPENAME boost::unordered_detail::rebind_wrap::type value_allocator; - typedef hash_table hash_table; - typedef hash_table_data data; + typedef hash_table_unique_keys hash_table; + typedef hash_table_data_unique_keys data; typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; - typedef hash_const_local_iterator const_local_iterator; - typedef hash_local_iterator local_iterator; - typedef hash_const_iterator const_iterator; - typedef hash_iterator iterator; + typedef hash_const_local_iterator_unique_keys const_local_iterator; + typedef hash_local_iterator_unique_keys local_iterator; + typedef hash_const_iterator_unique_keys const_iterator; + typedef hash_iterator_unique_keys iterator; + + typedef BOOST_DEDUCED_TYPENAME data::size_type size_type; + typedef std::ptrdiff_t difference_type; + }; + + template + class hash_types_equivalent_keys + { + public: + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + value_allocator; + + typedef hash_table_equivalent_keys hash_table; + typedef hash_table_data_equivalent_keys data; + typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; + + typedef hash_const_local_iterator_equivalent_keys const_local_iterator; + typedef hash_local_iterator_equivalent_keys local_iterator; + typedef hash_const_iterator_equivalent_keys const_iterator; + typedef hash_iterator_equivalent_keys iterator; typedef BOOST_DEDUCED_TYPENAME data::size_type size_type; typedef std::ptrdiff_t difference_type; @@ -1966,6 +191,6 @@ namespace boost { } // namespace boost #undef BOOST_HASH_BORLAND_BOOL +#undef BOOST_HASH_MSVC_RESET_PTR #endif // BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED - diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp new file mode 100644 index 00000000..62c3c22f --- /dev/null +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -0,0 +1,2112 @@ + +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005-2006 Daniel James + +// 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) + +#if BOOST_UNORDERED_HASH_EQUIVALENT +#define HASH_TABLE hash_table_equivalent_keys +#define HASH_TABLE_DATA hash_table_data_equivalent_keys +#define HASH_ITERATOR hash_iterator_equivalent_keys +#define HASH_CONST_ITERATOR hash_const_iterator_equivalent_keys +#define HASH_LOCAL_ITERATOR hash_local_iterator_equivalent_keys +#define HASH_CONST_LOCAL_ITERATOR hash_const_local_iterator_equivalent_keys +#else +#define HASH_TABLE hash_table_unique_keys +#define HASH_TABLE_DATA hash_table_data_unique_keys +#define HASH_ITERATOR hash_iterator_unique_keys +#define HASH_CONST_ITERATOR hash_const_iterator_unique_keys +#define HASH_LOCAL_ITERATOR hash_local_iterator_unique_keys +#define HASH_CONST_LOCAL_ITERATOR hash_const_local_iterator_unique_keys +#endif + +namespace boost { + namespace unordered_detail { + + // + // Hash Table Data + // + // Responsible for managing the hash buckets. + + template + class HASH_TABLE_DATA + { + public: + class node; + class node_base; + class bucket; + typedef std::size_t size_type; + + typedef Alloc value_allocator; + + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + node_allocator; + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + node_base_allocator; + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + bucket_allocator; + + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type node_ptr; + typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type bucket_ptr; + typedef BOOST_DEDUCED_TYPENAME allocator_reference::type reference; + +#if defined(BOOST_UNORDERED_PARANOID) + // If the allocator has the expected pointer types I take some liberties. + typedef typename boost::mpl::and_< + boost::is_same, + boost::is_same + >::type is_pointer_allocator; + + typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_< + is_pointer_allocator, bucket_ptr, node_ptr>::type link_ptr; +#else + typedef bucket_ptr link_ptr; +#endif + + // Hash Bucket + // + // all no throw (memory management is performed by HASH_TABLE_DATA). + + class bucket + { + bucket& operator=(bucket const&); + public: + link_ptr next_; + + bucket() : next_() + { + BOOST_HASH_MSVC_RESET_PTR(next_); + } + + bucket(bucket const& x) : next_(x.next_) + { + // Only copy construct when allocating. + BOOST_ASSERT(!x.next_); + } + + bool empty() const + { + return !this->next_; + } + }; + + // Hash Node + // + // all no throw + + class node_base : public bucket + { +#if BOOST_UNORDERED_HASH_EQUIVALENT + public: + node_base() : group_next_() + { + BOOST_HASH_MSVC_RESET_PTR(group_next_); + } + + link_ptr group_next_; +#endif + }; + + class node : public node_base + { + public: + node(value_type const& v) : node_base(), value_(v) {} + + value_type value_; + }; + +#if !defined(BOOST_UNORDERED_PARANOID) + class node_constructor + { + node_allocator& node_alloc_; + bucket_allocator& bucket_alloc_; + node_base_allocator node_base_alloc_; + value_allocator value_alloc_; + + node_ptr ptr_; + bool value_allocated_; + bool node_base_allocated_; + + public: + + node_constructor(node_allocator& n, bucket_allocator& b) + : node_alloc_(n), bucket_alloc_(b), node_base_alloc_(n), value_alloc_(n), + ptr_(), value_allocated_(false), node_base_allocated_(false) + { + BOOST_HASH_MSVC_RESET_PTR(ptr_); + } + + ~node_constructor() + { + if (ptr_) { + if (value_allocated_) + value_alloc_.destroy( + value_alloc_.address(ptr_->value_)); + if (node_base_allocated_) + node_base_alloc_.destroy( + node_base_alloc_.address(*ptr_)); + + node_alloc_.deallocate(ptr_, 1); + } + } + + template + void construct(V const& v) + { + BOOST_ASSERT(!ptr_); + value_allocated_ = node_base_allocated_ = false; + + ptr_ = node_alloc_.allocate(1); + + node_base_alloc_.construct(node_base_alloc_.address( + *ptr_), node_base()); + node_base_allocated_ = true; + + value_alloc_.construct(value_alloc_.address( + ptr_->value_), v); + value_allocated_ = true; + } + + // no throw + link_ptr release() + { + node_ptr p = ptr_; + unordered_detail::reset(ptr_); + return bucket_alloc_.address(*p); + } + + private: + node_constructor(node_constructor const&); + node_constructor& operator=(node_constructor const&); + }; +#else + class node_constructor + : public allocator_constructor + { + public: + node_constructor(node_allocator& n, bucket_allocator&) + : allocator_constructor(n) {} + }; +#endif + +#if BOOST_UNORDERED_HASH_EQUIVALENT + static link_ptr& next_in_group(link_ptr p) { + return static_cast(*p).group_next_; + } + + // pre: Must be pointing to the first node in a group. + static link_ptr last_in_group(link_ptr p) { + BOOST_ASSERT(p && p != next_in_group(p)->next_); + return next_in_group(p); + } + + // pre: Must be pointing to the first node in a group. + static link_ptr& next_group(link_ptr p) { + BOOST_ASSERT(p && p != next_in_group(p)->next_); + return next_in_group(p)->next_; + } +#else + static link_ptr last_in_group(link_ptr p) { + return p; + } + + static link_ptr& next_group(link_ptr p) { + BOOST_ASSERT(p); + return p->next_; + } +#endif + + // pre: Must be pointing to a node + static node& get_node(link_ptr p) { + BOOST_ASSERT(p); + return static_cast(*p); + } + + // pre: Must be pointing to a node + static reference get_value(link_ptr p) { + BOOST_ASSERT(p); + return static_cast(*p).value_; + } + + class local_iterator_base + { + public: + link_ptr node_pointer_; + + local_iterator_base() + : node_pointer_() + { + BOOST_HASH_MSVC_RESET_PTR(node_pointer_); + } + + explicit local_iterator_base(link_ptr n) + : node_pointer_(n) {} + + bool not_finished() const + { + return node_pointer_ ? true : false; + } + + bool operator==(local_iterator_base const& x) const + { + return node_pointer_ == x.node_pointer_; + } + + bool operator!=(local_iterator_base const& x) const + { + return node_pointer_ != x.node_pointer_; + } + + reference operator*() const + { + return get_value(node_pointer_); + } + + void increment() + { + BOOST_ASSERT(node_pointer_); + node_pointer_ = node_pointer_->next_; + } + + // pre: Must be pointing to first element in group. + void last_in_group() + { + node_pointer_ = HASH_TABLE_DATA::last_in_group(node_pointer_); + } + + // pre: Must be pointing to first element in group. + void next_group() + { + node_pointer_ = HASH_TABLE_DATA::next_group(node_pointer_); + } + }; + + class iterator_base + { + public: + bucket_ptr bucket_; + local_iterator_base local_; + + iterator_base() + : bucket_(), local_() {} + + iterator_base(bucket_ptr b) + : bucket_(b), local_(b->next_) {} + + iterator_base(bucket_ptr b, link_ptr n) + : bucket_(b), local_(n) {} + + iterator_base(bucket_ptr b, local_iterator_base const& it) + : bucket_(b), local_(it) {} + + local_iterator_base local() const + { + return local_iterator_base(local_); + } + + bool operator==(iterator_base const& x) const + { + return local_ == x.local_; + } + + bool operator!=(iterator_base const& x) const + { + return local_ != x.local_; + } + + reference operator*() const + { + return *local_; + } + + void increment() + { + BOOST_ASSERT(bucket_); + local_.increment(); + + while (!local_.node_pointer_) { + ++bucket_; + local_ = local_iterator_base(bucket_->next_); + } + } + }; + + // Member Variables + + node_allocator node_alloc_; + bucket_allocator bucket_alloc_; + + bucket_ptr buckets_; + size_type bucket_count_; + bucket_ptr cached_begin_bucket_; + size_type size_; + + // Constructor + + HASH_TABLE_DATA(size_type n, node_allocator const& a) + : node_alloc_(a), bucket_alloc_(a), + buckets_(), bucket_count_(next_prime(n)), + cached_begin_bucket_(), size_(0) + { + // The array constructor will clean up in the event of an + // exception. + allocator_array_constructor + constructor(bucket_alloc_); + constructor.construct(bucket(), bucket_count_ + 1); + + cached_begin_bucket_ = constructor.get() + bucket_count_; + + // Only release the buckets once everything is successfully + // done. + buckets_ = constructor.release(); + } + + // no throw + ~HASH_TABLE_DATA() + { + if(buckets_) { + if(buckets_[bucket_count_].next_) remove_end_marker(); + + for(size_type i = 0; i < bucket_count_; ++i) + delete_bucket_contents(buckets_ + i); + + for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2) + bucket_alloc_.destroy(buckets_ + i2); + + bucket_alloc_.deallocate(buckets_, bucket_count_ + 1); + } + } + + struct normal_end_marker_impl + { + static void add(HASH_TABLE_DATA* data) { + data->buckets_[data->bucket_count_].next_ + = data->buckets_ + data->bucket_count_; + } + + static void remove(HASH_TABLE_DATA* data) { + reset(data->buckets_[data->bucket_count_].next_); + } + + static void move(HASH_TABLE_DATA* src, HASH_TABLE_DATA* dst) { + add(dst); + remove(src); + } + }; + + struct paranoid_end_marker_impl + { + static void add(HASH_TABLE_DATA* data) { + // This seems very wasteful, but I can't think of a better + // way to create an end node and work with all allocators. + data->buckets_[data->bucket_count_].next_ + = data->node_alloc_.allocate(1); + } + + static void remove(HASH_TABLE_DATA* data) { + data->node_alloc_.deallocate( + data->buckets_[data->bucket_count_].next_, 1); + } + + static void move(HASH_TABLE_DATA* src, HASH_TABLE_DATA* dst) { + dst->buckets_[dst->bucket_count_].next_ + = src->buckets_[src->bucket_count_].next_; + reset(src->buckets_[src->bucket_count_].next_); + } + }; + +#if !defined(BOOST_UNORDERED_PARANOID) + typedef normal_end_marker_impl end_marker_impl; +#else + typedef typename mpl::if_::type end_marker_impl; +#endif + + void add_end_marker() + { + BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && + !buckets_[bucket_count_].next_); + end_marker_impl::add(this); + } + + void move_end_marker(HASH_TABLE_DATA& src) + { + BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && + !buckets_[bucket_count_].next_); + BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(src.buckets_) && + BOOST_HASH_BORLAND_BOOL(src.buckets_[src.bucket_count_].next_)); + end_marker_impl::move(&src, this); + BOOST_ASSERT(!!buckets_[bucket_count_].next_); + BOOST_ASSERT(!src.buckets_[src.bucket_count_].next_); + } + + void remove_end_marker() + { + BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && + BOOST_HASH_BORLAND_BOOL(buckets_[bucket_count_].next_)); + end_marker_impl::remove(this); + unordered_detail::reset(buckets_[bucket_count_].next_); + } + + private: + + HASH_TABLE_DATA(HASH_TABLE_DATA const&); + HASH_TABLE_DATA& operator=(HASH_TABLE_DATA const&); + + public: + + // no throw + void swap(HASH_TABLE_DATA& other) + { + std::swap(buckets_, other.buckets_); + std::swap(bucket_count_, other.bucket_count_); + std::swap(cached_begin_bucket_, other.cached_begin_bucket_); + std::swap(size_, other.size_); + } + + // Return the bucket index for a hashed value. + // + // no throw + size_type index_from_hash(size_type hashed) const + { + return hashed % bucket_count_; + } + + // Begin & End + // + // no throw + + iterator_base begin() const + { + return size_ + ? iterator_base(cached_begin_bucket_) + : end(); + } + + iterator_base end() const + { + return iterator_base(buckets_ + bucket_count_); + } + + local_iterator_base begin(size_type n) const + { + return local_iterator_base(buckets_[n].next_); + } + + local_iterator_base end(size_type) const + { + return local_iterator_base(); + } + + local_iterator_base begin(bucket_ptr b) const + { + return local_iterator_base(b->next_); + } + + // Bucket Size + + // no throw + size_type bucket_size(size_type n) const + { + std::size_t count = 0; + local_iterator_base it1 = begin(n); + while(it1.not_finished()) { + ++count; + it1.increment(); + } + return count; + } + +#if BOOST_UNORDERED_HASH_EQUIVALENT + std::size_t group_count(local_iterator_base pos) const + { + link_ptr it = pos.node_pointer_; + link_ptr first = it; + size_type count = 0; + do { + ++count; + it = next_in_group(it); + } while (it != first); // throws, strong + return count; + } +#else + std::size_t group_count(local_iterator_base) const + { + return 1; + } +#endif + + // get_for_erase + // + // Find the pointer to a node, for use when erasing. + // + // no throw + +#if BOOST_UNORDERED_HASH_EQUIVALENT + link_ptr* get_for_erase(iterator_base r) const + { + link_ptr pos = r.local().node_pointer_; + + link_ptr* it = &next_in_group(pos)->next_; + if(*it == pos) return it; + + it = &r.bucket_->next_; + while(*it != pos) it = &(*it)->next_; + return it; + } +#else + link_ptr* get_for_erase(iterator_base r) const + { + link_ptr pos = r.local().node_pointer_; + link_ptr* it = &r.bucket_->next_; + while(*it != pos) it = &(*it)->next_; + return it; + } +#endif + + // Link/Unlink/Move Node + // + // For adding nodes to buckets, removing them and moving them to a + // new bucket. + // + // no throw + +#if BOOST_UNORDERED_HASH_EQUIVALENT + void link_node(link_ptr n, local_iterator_base pos) + { + node& node_ref = get_node(n); + node& pos_ref = get_node(pos.node_pointer_); + node_ref.next_ = pos_ref.group_next_->next_; + node_ref.group_next_ = pos_ref.group_next_; + pos_ref.group_next_->next_ = n; + pos_ref.group_next_ = n; + ++size_; + } + + void link_node(link_ptr n, bucket_ptr base) + { + node& node_ref = get_node(n); + node_ref.next_ = base->next_; + node_ref.group_next_ = n; + base->next_ = n; + ++size_; + if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + } + + void link_group(link_ptr n, bucket_ptr base) + { + node& node_ref = get_node(n); + node& last_ref = get_node(node_ref.group_next_); + last_ref.next_ = base->next_; + base->next_ = n; + + // TODO: Use group_count... + // or take count as a parameter - we've probably already counted + // this when we unlinked it. + link_ptr it = n; + do { + ++size_; + it = next_in_group(it); + } while(it != n); + + if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + } +#else + void link_node(link_ptr n, bucket_ptr base) + { + n->next_ = base->next_; + base->next_ = n; + ++size_; + if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + } + + void link_group(link_ptr n, bucket_ptr base) + { + link_node(n, base); + } +#endif + +#if BOOST_UNORDERED_HASH_EQUIVALENT + // TODO: Improve this: + void unlink_node(link_ptr* pos) + { + node& to_delete = get_node(*pos); + link_ptr next = to_delete.next_; + + if(to_delete.group_next_ == *pos) { + // The deleted node is the sole node in the group, so + // no need to unlink it from a goup. + } + else if(next && next_in_group(next) == *pos) + { + next_in_group(next) = to_delete.group_next_; + } + else { + link_ptr it = to_delete.group_next_; + while(next_in_group(it) != *pos) { + it = next_in_group(it); + } + next_in_group(it) = to_delete.group_next_; + } + *pos = (*pos)->next_; + --size_; + } + + void unlink_group(link_ptr* pos) + { + size_ -= group_count(local_iterator_base(*pos)); + link_ptr last = last_in_group(*pos); + *pos = last->next_; + } +#else + void unlink_node(link_ptr* pos) + { + *pos = (*pos)->next_; + --size_; + } + + void unlink_group(link_ptr* pos) + { + *pos = (*pos)->next_; + --size_; + } +#endif + + void move_group(HASH_TABLE_DATA& src, bucket_ptr src_bucket, bucket_ptr dst_bucket) + { + link_ptr n = src_bucket->next_; + src.unlink_group(&src_bucket->next_); + link_group(n, dst_bucket); + } + + // throws, strong exception-safety: + link_ptr construct_node(value_type const& v) + { + node_constructor a(node_alloc_, bucket_alloc_); + a.construct(v); + return a.release(); + } + + // Create Node + // + // Create a node and add it to the buckets in the given position. + // + // strong exception safety. + + iterator_base create_node(value_type const& v, bucket_ptr base) + { + // throws, strong exception-safety: + link_ptr n = construct_node(v); + + // Rest is no throw + link_node(n, base); + return iterator_base(base, n); + } + +#if BOOST_UNORDERED_HASH_EQUIVALENT + iterator_base create_node(value_type const& v, iterator_base position) + { + // throws, strong exception-safety: + link_ptr n = construct_node(v); + + // Rest is no throw + link_node(n, position.local()); + return iterator_base(position.bucket_, n); + } + + iterator_base create_node(value_type const& v, + bucket_ptr base, local_iterator_base position) + { + // throws, strong exception-safety: + link_ptr n = construct_node(v); + + // Rest is no throw + if(position.not_finished()) + link_node(n, position); + else + link_node(n, base); + + return iterator_base(base, n); + } +#endif + +#if BOOST_UNORDERED_HASH_EQUIVALENT + void copy_group(local_iterator_base it, bucket_ptr dst) + { + local_iterator_base end = it; + end.next_group(); + iterator_base pos = create_node(*it, dst); + for(it.increment(); it != end; it.increment()) + create_node(*it, pos); + } +#else + void copy_group(local_iterator_base it, bucket_ptr dst) + { + create_node(*it, dst); + } +#endif + + // Delete Node + // + // Remove a node, or a range of nodes, from a bucket, and destory + // them. + // + // no throw + + void delete_node(link_ptr* pos) + { + // TODO: alloc_cast + node_ptr n = node_alloc_.address(static_cast(**pos)); + unlink_node(pos); + + node_alloc_.destroy(n); + node_alloc_.deallocate(n, 1); + } + + // TODO: Rename this: + void delete_bucket_contents(link_ptr* pos) + { + link_ptr ptr = *pos; + unordered_detail::reset(*pos); + + while(ptr) { + node_ptr n = node_alloc_.address( + static_cast(*ptr)); + ptr = n->next_; + + node_alloc_.destroy(n); + node_alloc_.deallocate(n, 1); + --size_; + } + } + + void delete_bucket_contents(bucket_ptr ptr) + { + delete_bucket_contents(&ptr->next_); + } + +#if BOOST_UNORDERED_HASH_EQUIVALENT + link_ptr split_group(link_ptr split) + { + link_ptr it = split; + if(next_in_group(it)->next_ != it) + return link_ptr(); + + do { + it = next_in_group(it); + } while(next_in_group(it)->next_ == it); + + link_ptr tmp = next_in_group(it); + next_in_group(it) = next_in_group(split); + next_in_group(split) = tmp; + + return it; + } + + void split_group(link_ptr split1, link_ptr split2) + { + link_ptr it1 = split_group(split1); + link_ptr it2 = split_group(split2); + + if(it1 && it1 == it2) { + link_ptr tmp = next_in_group(it1); + next_in_group(it1) = next_in_group(it2); + next_in_group(it2) = tmp; + } + } +#else + void split_group(link_ptr) + { + } + + void split_group(link_ptr, link_ptr) + { + } +#endif + + void delete_nodes(iterator_base pos) + { + link_ptr* it = get_for_erase(pos); + split_group(*it); + delete_bucket_contents(it); + } + + void delete_nodes(iterator_base begin, local_iterator_base end) + { + if(end.not_finished()) { + link_ptr* it = get_for_erase(begin); + + link_ptr ptr = *it; + split_group(*it, end.node_pointer_); + *it = end.node_pointer_; + + while(ptr != end.node_pointer_) { + node_ptr n = node_alloc_.address(static_cast(*ptr)); + ptr = n->next_; + + node_alloc_.destroy(n); + node_alloc_.deallocate(n, 1); + --size_; + } + } + else { + delete_nodes(begin); + } + } + + void delete_nodes(bucket_ptr base, local_iterator_base end) + { + BOOST_ASSERT(end.not_finished()); + split_group(end.node_pointer_); + + link_ptr ptr(base->next_); + base->next_ = end.node_pointer_; + + while(ptr != end.node_pointer_) { + node_ptr n = node_alloc_.address(static_cast(*ptr)); + ptr = n->next_; + + node_alloc_.destroy(n); + node_alloc_.deallocate(n, 1); + --size_; + } + } + +#if BOOST_UNORDERED_HASH_EQUIVALENT + std::size_t delete_group(link_ptr* pos) + { + std::size_t count = 0; + link_ptr first = *pos; + link_ptr end = next_in_group(first)->next_; + unlink_group(pos); + while(first != end) { + node_ptr n = node_alloc_.address(static_cast(*first)); + first = first->next_; + node_alloc_.destroy(n); + node_alloc_.deallocate(n, 1); + ++count; + } + return count; + } +#else + std::size_t delete_group(link_ptr* pos) + { + delete_node(pos); + return 1; + } +#endif + + // Clear + // + // Remove all the nodes. + // + // no throw + // + // TODO: If delete_nodes did throw (it shouldn't but just for + // argument's sake), could this leave cached_begin_bucket_ pointing + // at an empty bucket? + + void clear() + { + bucket_ptr end = buckets_ + bucket_count_; + while(cached_begin_bucket_ != end) { + delete_bucket_contents(cached_begin_bucket_); + ++cached_begin_bucket_; + } + BOOST_ASSERT(!size_); + } + + // Erase + // + // Return type of erase(const_iterator): + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1753.html#130 + // + // no throw + + iterator_base erase(iterator_base r) + { + BOOST_ASSERT(r != end()); + iterator_base next = r; + next.increment(); + delete_node(get_for_erase(r)); + // r has been invalidated but its bucket is still valid + recompute_begin_bucket(r.bucket_, next.bucket_); + return next; + } + + iterator_base erase(iterator_base r1, iterator_base r2) + { + if(r1 != r2) + { + BOOST_ASSERT(r1 != end()); + + if (r1.bucket_ == r2.bucket_) { + delete_nodes(r1, r2.local()); + + // No need to call recompute_begin_bucket because + // the nodes are only deleted from one bucket, which + // still contains r2 after the erase. + BOOST_ASSERT(!r1.bucket_->empty()); + } + else { + BOOST_ASSERT(r1.bucket_ < r2.bucket_); + + delete_nodes(r1); + + for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i) + delete_bucket_contents(i); + + if(r2 != end()) delete_nodes(r2.bucket_, r2.local()); + + // r1 has been invalidated but its bucket is still + // valid. + recompute_begin_bucket(r1.bucket_, r2.bucket_); + } + } + + return r2; + } + + // recompute_begin_bucket + // + // After an erase cached_begin_bucket_ might be left pointing to + // an empty bucket, so this is called to update it + // + // no throw + + void recompute_begin_bucket(bucket_ptr ptr) + { + BOOST_ASSERT(!(ptr < cached_begin_bucket_)); + + if(ptr == cached_begin_bucket_) + { + if (size_ != 0) { + while (cached_begin_bucket_->empty()) + ++cached_begin_bucket_; + } else { + cached_begin_bucket_ = buckets_ + bucket_count_; + } + } + } + + // This is called when a range has been erased + // + // no throw + + void recompute_begin_bucket(bucket_ptr i, bucket_ptr j) + { + BOOST_ASSERT(!(i < cached_begin_bucket_) && !(j < i)); + BOOST_ASSERT(j == buckets_ + bucket_count_ || !j->empty()); + + if(i == cached_begin_bucket_ && i->empty()) + cached_begin_bucket_ = j; + } + }; + +#if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) + template <> + class HASH_TABLE_DATA + { + public: + typedef int size_type; + typedef int iterator_base; + }; +#endif + + // + // Hash Table + // + + template + class HASH_TABLE + : public HASH_TABLE_DATA + { + typedef HASH_TABLE_DATA data; + + typedef typename data::node_constructor node_constructor; + typedef typename data::link_ptr link_ptr; + + public: + + typedef BOOST_DEDUCED_TYPENAME data::value_allocator value_allocator; + typedef BOOST_DEDUCED_TYPENAME data::node_allocator node_allocator; + typedef BOOST_DEDUCED_TYPENAME data::bucket_ptr bucket_ptr; + + // Type definitions + + typedef KeyType key_type; + typedef Hash hasher; + typedef Pred key_equal; + typedef ValueType value_type; + typedef std::size_t size_type; + + // iterators + + typedef BOOST_DEDUCED_TYPENAME data::local_iterator_base local_iterator_base; + typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; + + private: + + // From the compressed functions docs: + // + // "Finally, a word of caution for Visual C++ 6 users: if either + // argument is an empty type, then assigning to that member will + // produce memory corruption, unless the empty type has a "do + // nothing" assignment operator defined. This is due to a bug in + // the way VC6 generates implicit assignment operators." + // + // Nice. + // + // So use std::pair for Visual C++. + + class functions + { +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) + boost::compressed_pair functions_; +#else + std::pair functions_; +#endif + + public: + + functions(hasher const& h, key_equal const& k) + : functions_(h, k) {} + + hasher const& hash_function() const + { +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) + return functions_.first(); +#else + return functions_.first; +#endif + } + + key_equal const& key_eq() const + { +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) + return functions_.second(); +#else + return functions_.second; +#endif + } + }; + + // Both hasher and key_equal's copy/assign can throw so double + // buffering is used to copy them. func_ points to the currently + // active function objects. + + typedef functions HASH_TABLE::*functions_ptr; + + functions func1_; + functions func2_; + functions_ptr func_; + + float mlf_; + size_type max_load_; + + public: + + // Constructors + + HASH_TABLE(size_type n, + hasher const& hf, key_equal const& eq, + value_allocator const& a) + : data(n, a), // throws, cleans itself up + func1_(hf, eq), // throws " " + func2_(hf, eq), // throws " " + func_(&HASH_TABLE::func1_), // no throw + mlf_(1.0f) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + } + + // Construct from iterators + + // initial_size + // + // A helper function for the copy constructor to calculate how many + // nodes will be created if the iterator's support it. Might get it + // totally wrong for containers with unique keys. + // + // no throw + + template + std::size_t initial_size(I i, I j, size_type n, + boost::forward_traversal_tag) + { + // max load factor isn't set yet, but when it is, it'll be 1.0. + return (std::max)(static_cast(std::distance(i, j)) + 1, n); + }; + + template + std::size_t initial_size(I, I, size_type n, + boost::incrementable_traversal_tag) + { + return n; + }; + + template + std::size_t initial_size(I i, I j, size_type x) + { + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type + iterator_traversal_tag; + return initial_size(i, j, x, iterator_traversal_tag); + }; + + template + HASH_TABLE(I i, I j, size_type n, + hasher const& hf, key_equal const& eq, + value_allocator const& a) + : data(initial_size(i, j, n), a), // throws, cleans itself up + func1_(hf, eq), // throws " " + func2_(hf, eq), // throws " " + func_(&HASH_TABLE::func1_), // no throw + mlf_(1.0f) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + + // This can throw, but HASH_TABLE_DATA's destructor will clean up. + insert(i, j); + } + + // Copy Construct + + HASH_TABLE(HASH_TABLE const& x) + : data(x.min_buckets_for_size(x.size()), x.node_alloc_), // throws + func1_(x.current_functions()), // throws + func2_(x.current_functions()), // throws + func_(&HASH_TABLE::func1_), // no throw + mlf_(x.mlf_) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + + // This can throw, but HASH_TABLE_DATA's destructor will clean + // up. + copy_buckets(x, *this, current_functions()); + } + + // Assign + // + // basic exception safety, if copy_functions of reserver throws + // the container is left in a sane, empty state. If copy_buckets + // throws the container is left with whatever was successfully + // copied. + + HASH_TABLE& operator=(HASH_TABLE const& x) + { + if(this != &x) + { + // TODO: I could rewrite this to use the existing nodes. + this->clear(); // no throw + func_ = copy_functions(x); // throws, strong + mlf_ = x.mlf_; // no throw + calculate_max_load(); // no throw + reserve(x.size()); // throws + copy_buckets(x, *this, current_functions()); // throws + } + + return *this; + } + + // Swap + // + // Swap's behaviour when allocators aren't equal is in dispute, see + // this paper for full details: + // + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1599.html + // + // It lists 3 possible behaviours: + // + // 1 - If the allocators aren't equal then throw an exception. + // 2 - Reallocate the elements in the containers with the + // appropriate allocators - messing up exception safety in + // the process. + // 3 - Swap the allocators, hoping that the allocators have a + // no-throw swap. + // + // The paper recommends #3. + // + // I've implemented all three, but actived #3 by default, to change + // it '#define BOOST_UNORDERED_SWAP_METHOD n' where n is the option. + // + // ---------------------------------------------------------------- + // + // Strong exception safety (might change unused function objects) + // + // Can throw if hash or predicate object's copy constructor throws. + // If allocators are unequal: + // Method 1: always throws. + // Method 2: can throw if copying throws + // (memory allocation/hash function object) + // Method 3: Can throw if allocator's swap throws + // (TODO: This one is broken right now, double buffering?) + + void swap(HASH_TABLE& x) + { + // This only effects the function objects that aren't in use + // so it is strongly exception safe, via. double buffering. + functions_ptr new_func_this = copy_functions(x); // throws + functions_ptr new_func_that = x.copy_functions(*this); // throws + + if(this->node_alloc_ == x.node_alloc_) { + this->data::swap(x); // no throw + } + else { +#if BOOST_UNORDERED_SWAP_METHOD == 1 + throw std::runtime_error( + "Swapping containers with different allocators.");; +#elif BOOST_UNORDERED_SWAP_METHOD == 2 + // Create new buckets in separate HASH_TABLE_DATA objects + // which will clean up if any of this throws. + data new_this(x.min_buckets_for_size(x.size_), + this->node_alloc_); // throws + copy_buckets(x, new_this, this->*new_func_this); // throws + + data new_that(min_buckets_for_size(this->size_), + x.node_alloc_); // throws + x.copy_buckets(*this, new_that, x.*new_func_that); // throws + + // Start updating the data here, no throw from now on. + new_this.move_end_marker(*this); // no throw + new_that.move_end_marker(x); // no throw + this->data::swap(new_this); // no throw + x.data::swap(new_that); // no throw +#elif BOOST_UNORDERED_SWAP_METHOD == 3 + hash_swap(this->node_alloc_, + x.node_alloc_); // no throw, or is it? + hash_swap(this->bucket_alloc_, + x.bucket_alloc_); // no throw, or is it? + this->data::swap(x); // no throw +#else +#error "Invalid swap method" +#endif + } + + // We've made it, the rest is no throw. + std::swap(mlf_, x.mlf_); + + func_ = new_func_this; + x.func_ = new_func_that; + + calculate_max_load(); + x.calculate_max_load(); + } + + private: + + functions const& current_functions() const + { + return this->*func_; + } + + // This copies the given function objects into the currently unused + // function objects and returns a pointer, that func_ can later be + // set to, to commit the change. + // + // Strong exception safety (since only usued function objects are + // changed). + functions_ptr copy_functions(HASH_TABLE const& x) + { + // no throw: + functions_ptr ptr = func_ == &HASH_TABLE::func1_ + ? &HASH_TABLE::func2_ : &HASH_TABLE::func1_; + // throws, functions not in use, so strong + this->*ptr = x.current_functions(); + return ptr; + } + + public: + + // accessors + + // TODO: This creates an unnecessary copy. + // no throw + value_allocator get_allocator() const + { + return this->node_alloc_; + } + + // no throw + hasher const& hash_function() const + { + return current_functions().hash_function(); + } + + // no throw + key_equal const& key_eq() const + { + return current_functions().key_eq(); + } + + // no throw + size_type size() const + { + return this->size_; + } + + // no throw + bool empty() const + { + return this->size_ == 0; + } + + // no throw + size_type max_size() const + { + // size < mlf_ * count + return float_to_size_t(ceil( + max_bucket_count() * mlf_)) - 1; + } + + // strong safety + size_type bucket(key_type const& k) const + { + // hash_function can throw: + return this->index_from_hash(hash_function()(k)); + } + + // strong safety + bucket_ptr get_bucket(key_type const& k) const + { + return this->buckets_ + bucket(k); + } + + // no throw + size_type bucket_count() const + { + return this->bucket_count_; + } + + // no throw + size_type max_bucket_count() const + { + // -1 to account for the end marker. + return prev_prime(this->bucket_alloc_.max_size() - 1); + } + + private: + + // no throw + size_type min_buckets_for_size(size_type n) const + { + BOOST_ASSERT(mlf_ != 0); + + using namespace std; + + // From 6.3.1/13: + // size < mlf_ * count + // => count > size / mlf_ + // + // Or from rehash post-condition: + // count > size / mlf_ + return static_cast(floor(n / mlf_)) + 1; + } + + // no throw + void calculate_max_load() + { + using namespace std; + + // From 6.3.1/13: + // Only resize when size >= mlf_ * count + max_load_ = float_to_size_t(ceil(mlf_ * this->bucket_count_)); + } + + // basic exception safety + bool reserve(size_type n) + { + bool need_to_reserve = n >= max_load_; + // throws - basic: + if (need_to_reserve) rehash_impl(min_buckets_for_size(n)); + BOOST_ASSERT(n < max_load_); + return need_to_reserve; + } + + public: + + // no throw + float max_load_factor() const + { + return mlf_; + } + + // no throw + // + // TODO: the argument is a hint. So don't use it if it's + // unreasonably small. + void max_load_factor(float z) + { + BOOST_ASSERT(z > 0); + mlf_ = z; + calculate_max_load(); + } + + // no throw + float load_factor() const + { + BOOST_ASSERT(this->bucket_count_ != 0); + return static_cast(this->size_) + / static_cast(this->bucket_count_); + } + + private: + + // key extractors + + // no throw + static key_type const& extract_key(value_type const& v) + { + return extract(v, (type_wrapper*)0); + } + + static key_type const& extract(value_type const& v, + type_wrapper*) + { + return v; + } + + static key_type const& extract(value_type const& v, + void*) + { + return v.first; + } + + public: + + // if hash function throws, basic exception safety + // strong otherwise. + void rehash(size_type n) + { + using namespace std; + + // no throw: + size_type min_size = min_buckets_for_size(size()); + // basic/strong: + rehash_impl(min_size > n ? min_size : n); + + BOOST_ASSERT(bucket_count() > size() / max_load_factor() + && bucket_count() >= n); + } + + private: + + // if hash function throws, basic exception safety + // strong otherwise + void rehash_impl(size_type n) + { + n = next_prime(n); // no throw + + if (n == bucket_count()) // no throw + return; + + data new_buckets(n, this->node_alloc_); // throws, seperate + move_buckets(*this, new_buckets); // basic/no throw + new_buckets.swap(*this); // no throw + calculate_max_load(); // no throw + } + + // move_buckets & copy_buckets + // + // Note: Because equivalent elements are already + // adjacent to each other in the existing buckets, this + // simple rehashing technique is sufficient to ensure + // that they remain adjacent to each other in the new + // buckets (but in reverse order). + // + // if the hash function throws, basic excpetion safety + // no throw otherwise + + void move_buckets(data& src, data& dst) + { + BOOST_ASSERT(dst.size_ == 0); + BOOST_ASSERT(src.node_alloc_ == dst.node_alloc_); + + bucket_ptr end = src.buckets_ + src.bucket_count_; + + for(; src.cached_begin_bucket_ != end; + ++src.cached_begin_bucket_) { + bucket_ptr src_bucket = src.cached_begin_bucket_; + while(src_bucket->next_) { + // This next line throws iff the hash function throws. + bucket_ptr dst_bucket = dst.buckets_ + + dst.index_from_hash( + hash_function()(extract_key( + get_value(src_bucket->next_)))); + + dst.move_group(src, src_bucket, dst_bucket); + } + } + + // Move the end marker from the source to destination. + // Now destination is valid, source is not. + dst.move_end_marker(src); + } + + // basic excpetion safety, will leave dst partially filled. + + static void copy_buckets(data const& src, data& dst, functions const& f) + { + BOOST_ASSERT(dst.size_ == 0); + // no throw: + bucket_ptr end = src.buckets_ + src.bucket_count_; + hasher const& hf = f.hash_function(); + + // no throw: + for(bucket_ptr i = src.cached_begin_bucket_; i != end; ++i) { + // no throw: + for(local_iterator_base it = src.begin(i); + it.not_finished(); it.next_group()) { + // hash function can throw. + bucket_ptr dst_bucket = dst.buckets_ + + dst.index_from_hash(hf(extract_key(*it))); + // throws, strong + // dst.create_node(*it, dst_bucket); + dst.copy_group(it, dst_bucket); + } + } + } + + public: + + // Insert functions + // + // basic exception safety, if hash function throws + // strong otherwise. + +#if BOOST_UNORDERED_HASH_EQUIVALENT + + private: + + // Insert node without checking if a resize is necessary. + // (equivalent key containers) + + // strong exception safety. + iterator_base unchecked_insert(value_type const& v) + { + key_type const& k = extract_key(v); + bucket_ptr bucket = get_bucket(k); + local_iterator_base position = find_iterator(bucket, k); + + // No effects until here, this is strong. + return this->create_node(v, bucket, position); + } + + // strong exception safety + iterator_base unchecked_insert(iterator_base const& it, + value_type const& v) + { + // The condition can throw, but no side effects. + if(it != this->end() && equal(extract_key(v), *it)) { + // Strong exception safety: + return this->create_node(v, it); + } + else { + // Strong exception safety: + return unchecked_insert(v); + } + } + + public: + + // Insert (equivalent key containers) + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert(value_type const& v) + { + key_type const& k = extract_key(v); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->buckets_ + + this->index_from_hash(hash_value); + local_iterator_base position = find_iterator(bucket, k); + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->node_alloc_, this->bucket_alloc_); + a.construct(v); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(reserve(size() + 1)) + bucket = this->buckets_ + this->index_from_hash(hash_value); + + // Nothing after the point can throw. + + link_ptr n = a.release(); + + // I'm relying on local_iterator_base not being invalidated by + // the rehash here. + if(position.not_finished()) + this->link_node(n, position); + else + this->link_node(n, bucket); + + return iterator_base(bucket, n); + } + + // Insert (equivalent key containers) + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert(iterator_base const& it, value_type const& v) + { + // equal can throw, but with no effects + if (it == this->end() || !equal(extract_key(v), *it)) { + // Use the standard insert if the iterator doesn't point + // to a matching key. + return insert(v); + } + else { + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->node_alloc_, this->bucket_alloc_); + a.construct(v); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + bucket_ptr base = reserve(size() + 1) ? + get_bucket(extract_key(v)) : it.bucket_; + + // Nothing after this point can throw + + link_ptr n = a.release(); + this->link_node(n, it.local()); + + return iterator_base(base, n); + } + } + + // Insert from iterator range (equivalent key containers) + + private: + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise + template + void insert_for_range(I i, I j, forward_traversal_tag) + { + std::size_t distance = std::distance(i, j); + if(distance == 1) { + insert(*i); + } + else { + // Only require basic exception safety here + reserve(size() + distance); + for (; i != j; ++i) unchecked_insert(*i); + } + } + + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise + template + void insert_for_range(I i, I j, + boost::incrementable_traversal_tag) + { + // If only inserting 1 element, get the required + // safety since insert is only called once. + for (; i != j; ++i) insert(*i); + } + + public: + + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise + template + void insert(InputIterator i, InputIterator j) + { + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type + iterator_traversal_tag; + insert_for_range(i, j, iterator_traversal_tag); + } +#else + // if hash function throws, basic exception safety + // strong otherwise + value_type& operator[](key_type const& k) + { + BOOST_STATIC_ASSERT(( + !boost::is_same::value)); + typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type; + + bucket_ptr bucket = get_bucket(k); + local_iterator_base pos = find_iterator(bucket, k); + + if (pos.not_finished()) + return *pos; + else + { + // Side effects only in this block. + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->node_alloc_, this->bucket_alloc_); + a.construct(value_type(k, mapped_type())); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if (reserve(size() + 1)) { + // get_bucket can only throw if the hash function throws (in + // which case basic exception safety is okay). + bucket = get_bucket(k); + } + + // Nothing after this point can throw. + + link_ptr n = a.release(); + this->link_node(n, bucket); + + return *local_iterator_base(n); + } + } + + // Insert (unique keys) + + // if hash function throws, basic exception safety + // strong otherwise + std::pair insert(value_type const& v) + { + // No side effects in this initial code + key_type const& k = extract_key(v); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->buckets_ + + this->index_from_hash(hash_value); + local_iterator_base pos = find_iterator(bucket, k); + + if (pos.not_finished()) { + // Found an existing key, return it (no throw). + return std::pair( + iterator_base(bucket, pos), false); + + } else { + // Doesn't already exist, add to bucket. + // Side effects only in this block. + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->node_alloc_, this->bucket_alloc_); + a.construct(v); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(reserve(size() + 1)) + bucket = this->buckets_ + this->index_from_hash(hash_value); + + // Nothing after this point can throw. + + link_ptr n = a.release(); + this->link_node(n, bucket); + + return std::pair( + iterator_base(bucket, n), true); + } + } + + // Insert (unique keys) + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert(iterator_base const& it, value_type const& v) + { + if(it != this->end() && equal(extract_key(v), *it)) + return it; + else + return insert(v).first; + } + + // Insert from iterators (unique keys) + + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise + template + void insert(InputIterator i, InputIterator j) + { + // If only inserting 1 element, get the required + // safety since insert is only called once. + for (; i != j; ++i) insert(*i); + } +#endif + + public: + + // erase + + // no throw + iterator_base erase(iterator_base const& r) + { + return this->data::erase(r); + } + + // strong exception safety + size_type erase(key_type const& k) + { + // No side effects in initial section + bucket_ptr bucket = get_bucket(k); + link_ptr* it = find_for_erase(bucket, k); + + // The rest is no throw. + if (*it) { + size_type count = delete_group(it); + this->recompute_begin_bucket(bucket); + return count; + } + else { + return 0; + } + } + + // no throw + iterator_base erase(iterator_base const& r1, iterator_base const& r2) + { + return this->data::erase(r1, r2); + } + + // count + // + // strong exception safety, no side effects + size_type count(key_type const& k) const + { + local_iterator_base it = find_iterator(k); // throws, strong + return it.not_finished() ? group_count(it) : 0; + } + + // find + // + // strong exception safety, no side effects + iterator_base find(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + local_iterator_base it = find_iterator(bucket, k); + + if (it.not_finished()) + return iterator_base(bucket, it); + else + return this->end(); + } + + // equal_range + // + // strong exception safety, no side effects + std::pair equal_range(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + local_iterator_base it = find_iterator(bucket, k); + if (it.not_finished()) { + local_iterator_base last = it; + last.last_in_group(); + + iterator_base first(iterator_base(bucket, it)); + iterator_base second(iterator_base(bucket, last)); + second.increment(); + return std::pair(first, second); + } + else { + return std::pair( + this->end(), this->end()); + } + } + + private: + + // strong exception safety, no side effects + bool equal(key_type const& k, value_type const& v) const + { + return key_eq()(k, extract_key(v)); + } + + // strong exception safety, no side effects + local_iterator_base find_iterator(key_type const& k) const + { + return find_iterator(get_bucket(k), k); + } + + // strong exception safety, no side effects + local_iterator_base find_iterator(bucket_ptr bucket, + key_type const& k) const + { + local_iterator_base it = this->begin(bucket); + while (it.not_finished() && !equal(k, *it)) + it.next_group(); + + return it; + } + + // strong exception safety, no side effects + link_ptr* find_for_erase(bucket_ptr bucket, key_type const& k) const + { + link_ptr* it = &bucket->next_; + while(*it && !equal(k, this->get_value(*it))) + it = &this->next_group(*it); + + return it; + } + }; + + // Iterators + + template class HASH_ITERATOR; + template class HASH_CONST_ITERATOR; + template class HASH_LOCAL_ITERATOR; + template class HASH_CONST_LOCAL_ITERATOR; + class iterator_access; + + // Local Iterators + // + // all no throw + + template + class HASH_LOCAL_ITERATOR + : public boost::iterator < + std::forward_iterator_tag, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, + std::ptrdiff_t, + BOOST_DEDUCED_TYPENAME allocator_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_reference::type > + { + public: + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + + private: + typedef BOOST_DEDUCED_TYPENAME HASH_TABLE_DATA::local_iterator_base base; + typedef HASH_CONST_LOCAL_ITERATOR const_local_iterator; + + friend class HASH_CONST_LOCAL_ITERATOR; + base base_; + + public: + HASH_LOCAL_ITERATOR() : base_() {} + explicit HASH_LOCAL_ITERATOR(base x) : base_(x) {} + BOOST_DEDUCED_TYPENAME allocator_reference::type operator*() const + { return *base_; } + value_type* operator->() const { return &*base_; } + HASH_LOCAL_ITERATOR& operator++() { base_.increment(); return *this; } + HASH_LOCAL_ITERATOR operator++(int) { HASH_LOCAL_ITERATOR tmp(base_); base_.increment(); return tmp; } + bool operator==(HASH_LOCAL_ITERATOR x) const { return base_ == x.base_; } + bool operator==(const_local_iterator x) const { return base_ == x.base_; } + bool operator!=(HASH_LOCAL_ITERATOR x) const { return base_ != x.base_; } + bool operator!=(const_local_iterator x) const { return base_ != x.base_; } + }; + + template + class HASH_CONST_LOCAL_ITERATOR + : public boost::iterator < + std::forward_iterator_tag, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, + std::ptrdiff_t, + BOOST_DEDUCED_TYPENAME allocator_const_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_const_reference::type > + { + public: + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + + private: + typedef BOOST_DEDUCED_TYPENAME HASH_TABLE_DATA::local_iterator_base base; + typedef HASH_LOCAL_ITERATOR local_iterator; + friend class HASH_LOCAL_ITERATOR; + base base_; + + public: + HASH_CONST_LOCAL_ITERATOR() : base_() {} + explicit HASH_CONST_LOCAL_ITERATOR(base x) : base_(x) {} + HASH_CONST_LOCAL_ITERATOR(local_iterator x) : base_(x.base_) {} + BOOST_DEDUCED_TYPENAME allocator_const_reference::type + operator*() const { return *base_; } + value_type const* operator->() const { return &*base_; } + HASH_CONST_LOCAL_ITERATOR& operator++() { base_.increment(); return *this; } + HASH_CONST_LOCAL_ITERATOR operator++(int) { HASH_CONST_LOCAL_ITERATOR tmp(base_); base_.increment(); return tmp; } + bool operator==(local_iterator x) const { return base_ == x.base_; } + bool operator==(HASH_CONST_LOCAL_ITERATOR x) const { return base_ == x.base_; } + bool operator!=(local_iterator x) const { return base_ != x.base_; } + bool operator!=(HASH_CONST_LOCAL_ITERATOR x) const { return base_ != x.base_; } + }; + + // iterators + // + // all no throw + + + template + class HASH_ITERATOR + : public boost::iterator < + std::forward_iterator_tag, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, + std::ptrdiff_t, + BOOST_DEDUCED_TYPENAME allocator_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_reference::type > + { + public: + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + + private: + typedef BOOST_DEDUCED_TYPENAME HASH_TABLE_DATA::iterator_base base; + typedef HASH_CONST_ITERATOR const_iterator; + friend class HASH_CONST_ITERATOR; + base base_; + + public: + + HASH_ITERATOR() : base_() {} + explicit HASH_ITERATOR(base const& x) : base_(x) {} + BOOST_DEDUCED_TYPENAME allocator_reference::type + operator*() const { return *base_; } + value_type* operator->() const { return &*base_; } + HASH_ITERATOR& operator++() { base_.increment(); return *this; } + HASH_ITERATOR operator++(int) { HASH_ITERATOR tmp(base_); base_.increment(); return tmp; } + bool operator==(HASH_ITERATOR const& x) const { return base_ == x.base_; } + bool operator==(const_iterator const& x) const { return base_ == x.base_; } + bool operator!=(HASH_ITERATOR const& x) const { return base_ != x.base_; } + bool operator!=(const_iterator const& x) const { return base_ != x.base_; } + }; + + template + class HASH_CONST_ITERATOR + : public boost::iterator < + std::forward_iterator_tag, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, + std::ptrdiff_t, + BOOST_DEDUCED_TYPENAME allocator_const_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_const_reference::type > + { + public: + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + + private: + typedef BOOST_DEDUCED_TYPENAME HASH_TABLE_DATA::iterator_base base; + typedef HASH_ITERATOR iterator; + friend class HASH_ITERATOR; + friend class iterator_access; + base base_; + + public: + + HASH_CONST_ITERATOR() : base_() {} + explicit HASH_CONST_ITERATOR(base const& x) : base_(x) {} + HASH_CONST_ITERATOR(iterator const& x) : base_(x.base_) {} + BOOST_DEDUCED_TYPENAME allocator_const_reference::type + operator*() const { return *base_; } + value_type const* operator->() const { return &*base_; } + HASH_CONST_ITERATOR& operator++() { base_.increment(); return *this; } + HASH_CONST_ITERATOR operator++(int) { HASH_CONST_ITERATOR tmp(base_); base_.increment(); return tmp; } + bool operator==(iterator const& x) const { return base_ == x.base_; } + bool operator==(HASH_CONST_ITERATOR const& x) const { return base_ == x.base_; } + bool operator!=(iterator const& x) const { return base_ != x.base_; } + bool operator!=(HASH_CONST_ITERATOR const& x) const { return base_ != x.base_; } + }; + } +} + +#undef HASH_TABLE +#undef HASH_TABLE_DATA +#undef HASH_ITERATOR +#undef HASH_CONST_ITERATOR +#undef HASH_LOCAL_ITERATOR +#undef HASH_CONST_LOCAL_ITERATOR diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 55d1a6ff..9f40c291 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -36,9 +36,8 @@ namespace boost class unordered_map { // Named for the benefit of Doxygen. - typedef boost::unordered_detail::hash_types< - std::pair, Key, Hash, - Pred, Alloc, false + typedef boost::unordered_detail::hash_types_unique_keys< + std::pair, Key, Hash, Pred, Alloc > implementation_defined; typename implementation_defined::hash_table base; @@ -187,17 +186,17 @@ namespace boost std::pair insert(const value_type& obj) { return boost::unordered_detail::pair_cast( - base.insert_unique(obj)); + base.insert(obj)); } iterator insert(iterator hint, const value_type& obj) { - return iterator(base.insert_unique(get(hint), obj)); + return iterator(base.insert(get(hint), obj)); } const_iterator insert(const_iterator hint, const value_type& obj) { - return const_iterator(base.insert_unique(get(hint), obj)); + return const_iterator(base.insert(get(hint), obj)); } template @@ -386,9 +385,8 @@ namespace boost class unordered_multimap { // Named for the benefit of Doxygen. - typedef boost::unordered_detail::hash_types< - std::pair, Key, Hash, - Pred, Alloc, true + typedef boost::unordered_detail::hash_types_equivalent_keys< + std::pair, Key, Hash, Pred, Alloc > implementation_defined; typename implementation_defined::hash_table base; @@ -511,17 +509,17 @@ namespace boost iterator insert(const value_type& obj) { - return iterator(base.insert_equivalent(obj)); + return iterator(base.insert(obj)); } iterator insert(iterator hint, const value_type& obj) { - return iterator(base.insert_equivalent(get(hint), obj)); + return iterator(base.insert(get(hint), obj)); } const_iterator insert(const_iterator hint, const value_type& obj) { - return const_iterator(base.insert_equivalent(get(hint), obj)); + return const_iterator(base.insert(get(hint), obj)); } template diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index c42a5e64..8785bf08 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -35,9 +35,8 @@ namespace boost class unordered_set { // Named for the benefit of Doxygen. - typedef boost::unordered_detail::hash_types< - Value, Value, Hash, - Pred, Alloc, false + typedef boost::unordered_detail::hash_types_unique_keys< + Value, Value, Hash, Pred, Alloc > implementation_defined; typename implementation_defined::hash_table base; @@ -159,12 +158,12 @@ namespace boost std::pair insert(const value_type& obj) { return boost::unordered_detail::pair_cast( - base.insert_unique(obj)); + base.insert(obj)); } const_iterator insert(const_iterator hint, const value_type& obj) { - return const_iterator(base.insert_unique(get(hint), obj)); + return const_iterator(base.insert(get(hint), obj)); } template @@ -325,9 +324,8 @@ namespace boost class unordered_multiset { // Named for the benefit of Doxygen. - typedef boost::unordered_detail::hash_types< - Value, Value, Hash, - Pred, Alloc, true + typedef boost::unordered_detail::hash_types_equivalent_keys< + Value, Value, Hash, Pred, Alloc > implementation_defined; typename implementation_defined::hash_table base; @@ -448,12 +446,12 @@ namespace boost iterator insert(const value_type& obj) { - return iterator(base.insert_equivalent(obj)); + return iterator(base.insert(obj)); } const_iterator insert(const_iterator hint, const value_type& obj) { - return const_iterator(base.insert_equivalent(get(hint), obj)); + return const_iterator(base.insert(get(hint), obj)); } template From e33f1b21d649f96532ba0bd253a48c8d8db1b594 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 17 May 2006 17:09:47 +0000 Subject: [PATCH 031/175] Update the container compile tests to include cbegin/cend. And add the copyright notice. [SVN r2952] --- test/container/compile_tests.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/container/compile_tests.hpp b/test/container/compile_tests.hpp index 1016f870..95479b6c 100644 --- a/test/container/compile_tests.hpp +++ b/test/container/compile_tests.hpp @@ -1,3 +1,8 @@ + +// Copyright Daniel James 2005-2006. Use, modification, and distribution are +// 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) + #include #include #include @@ -101,8 +106,12 @@ void container_test(X& r, T& value) X const a_const; test::check_return_type::equals(a.begin()); test::check_return_type::equals(a_const.begin()); + test::check_return_type::equals(a.cbegin()); + test::check_return_type::equals(a_const.cbegin()); test::check_return_type::equals(a.end()); test::check_return_type::equals(a_const.end()); + test::check_return_type::equals(a.cend()); + test::check_return_type::equals(a_const.cend()); // No tests for ==, != since they're not required for unordered containers. From 1301d774e019c47ac9d0df19b5237266d22732a6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 17 May 2006 17:15:26 +0000 Subject: [PATCH 032/175] Add a small note about testing allocators. [SVN r2953] --- test/objects/minimal.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index d63e85e9..81648e0c 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -173,6 +173,9 @@ namespace minimal bool operator>=(const_pointer const& x) const { return ptr_ >= x.ptr_; } }; + // TODO: Issue 560 suggests that an allocator doesn't have to have + // a default constructor. + // http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#560 template class allocator { From 1be8ab0d30f14d797e65e89142c96c63f2e85c0b Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 17 May 2006 17:19:16 +0000 Subject: [PATCH 033/175] Add some tests for the unordered associative containers. [SVN r2954] --- test/Jamfile.v2 | 7 + test/container/Jamfile.v2 | 1 + test/helpers/equivalent.hpp | 75 ++++++ test/helpers/fwd.hpp | 20 ++ test/helpers/generators.hpp | 88 +++++++ test/helpers/helpers.hpp | 30 +++ test/helpers/invariants.hpp | 82 +++++++ test/helpers/metafunctions.hpp | 103 ++++++++ test/helpers/random_values.hpp | 29 +++ test/helpers/tracker.hpp | 146 +++++++++++ test/objects/fwd.hpp | 18 ++ test/objects/test.hpp | 202 +++++++++++++++ test/unordered/Jamfile.v2 | 23 ++ test/unordered/assign_tests.cpp | 91 +++++++ test/unordered/compile_tests.cpp | 297 +++++++++++++++++++++++ test/unordered/constructor_tests.cpp | 202 +++++++++++++++ test/unordered/copy_tests.cpp | 103 ++++++++ test/unordered/equivalent_keys_tests.cpp | 85 +++++++ test/unordered/erase_tests.cpp | 140 +++++++++++ test/unordered/find_tests.cpp | 90 +++++++ test/unordered/insert_tests.cpp | 188 ++++++++++++++ 21 files changed, 2020 insertions(+) create mode 100644 test/Jamfile.v2 create mode 100644 test/helpers/equivalent.hpp create mode 100644 test/helpers/fwd.hpp create mode 100644 test/helpers/generators.hpp create mode 100644 test/helpers/helpers.hpp create mode 100644 test/helpers/invariants.hpp create mode 100644 test/helpers/metafunctions.hpp create mode 100644 test/helpers/random_values.hpp create mode 100644 test/helpers/tracker.hpp create mode 100644 test/objects/fwd.hpp create mode 100644 test/objects/test.hpp create mode 100644 test/unordered/Jamfile.v2 create mode 100644 test/unordered/assign_tests.cpp create mode 100644 test/unordered/compile_tests.cpp create mode 100644 test/unordered/constructor_tests.cpp create mode 100644 test/unordered/copy_tests.cpp create mode 100644 test/unordered/equivalent_keys_tests.cpp create mode 100644 test/unordered/erase_tests.cpp create mode 100644 test/unordered/find_tests.cpp create mode 100644 test/unordered/insert_tests.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 new file mode 100644 index 00000000..122f58d6 --- /dev/null +++ b/test/Jamfile.v2 @@ -0,0 +1,7 @@ + +# Copyright Daniel James 2006. Use, modification, and distribution are +# 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) + +build-project container ; +build-project unordered ; diff --git a/test/container/Jamfile.v2 b/test/container/Jamfile.v2 index a0dcd090..6ec50a34 100644 --- a/test/container/Jamfile.v2 +++ b/test/container/Jamfile.v2 @@ -14,4 +14,5 @@ test-suite container-tests : [ run set_compile.cpp ] [ run map_compile.cpp ] + [ run simple_tests.cpp ] ; diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp new file mode 100644 index 00000000..8d44fafd --- /dev/null +++ b/test/helpers/equivalent.hpp @@ -0,0 +1,75 @@ + +// Copyright Daniel James 2005-2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER) +#define BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER + +#include +#include +#include +#include +#include "./metafunctions.hpp" + +namespace test +{ + template + bool equivalent(T const& x, T const& y) { + return x == y; + } + + template + bool equivalent(boost::hash const&, boost::hash const&) { + return true; + } + + template + bool equivalent(std::equal_to const&, std::equal_to const&) { + return true; + } + + template + class unordered_equivalence_tester + { + typename Container::size_type size_; + typename Container::hasher hasher_; + typename Container::key_equal key_equal_; + float max_load_factor_; + + typedef typename non_const_value_type::type value_type; + std::vector values_; + public: + unordered_equivalence_tester(Container const &x) + : size_(x.size()), + hasher_(x.hash_function()), key_equal_(x.key_eq()), + max_load_factor_(x.max_load_factor()), + values_() + { + // Can't initialise values_ straight from x because of Visual C++ 6 + values_.reserve(x.size()); + std::copy(x.begin(), x.end(), std::back_inserter(values_)); + + std::sort(values_.begin(), values_.end()); + } + + bool operator()(Container const& x) const + { + if(!((size_ == x.size()) && + (test::equivalent(hasher_, x.hash_function())) && + (test::equivalent(key_equal_, x.key_eq())) && + (max_load_factor_ == x.max_load_factor()) && + (values_.size() == x.size()))) return false; + + std::vector copy; + copy.reserve(x.size()); + std::copy(x.begin(), x.end(), std::back_inserter(copy)); + std::sort(copy.begin(), copy.end()); + return(std::equal(values_.begin(), values_.end(), copy.begin())); + } + private: + unordered_equivalence_tester(); + }; +} + +#endif diff --git a/test/helpers/fwd.hpp b/test/helpers/fwd.hpp new file mode 100644 index 00000000..77032bf9 --- /dev/null +++ b/test/helpers/fwd.hpp @@ -0,0 +1,20 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER + +namespace test +{ + int generate(int const*); + char generate(char const*); + std::string generate(std::string*); + float generate(float const*); + template + std::pair generate(std::pair*); +} + +#endif + diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp new file mode 100644 index 00000000..3ed8cf1b --- /dev/null +++ b/test/helpers/generators.hpp @@ -0,0 +1,88 @@ + +// Copyright Daniel James 2005-2006. Use, modification, and distribution are +// 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) + +// A crude wrapper round Boost.Random to make life easier. + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER + +#include +#include + +#include +#include +#include +#include +#include + +#include "./fwd.hpp" + +namespace test +{ + typedef boost::hellekalek1995 integer_generator_type; + typedef boost::lagged_fibonacci607 real_generator_type; + + template + struct generator + { + typedef T value_type; + value_type operator()() + { + return generate((T const*) 0); + } + }; + + inline int generate(int const*) + { + static boost::variate_generator > + vg((integer_generator_type()), boost::uniform_int<>(0, 1000)); + return vg(); + } + + inline char generate(char const*) + { + static boost::variate_generator > + vg((integer_generator_type()), boost::uniform_int(32, 128)); + return vg(); + } + + inline std::string generate(std::string const*) + { + using namespace std; + + static test::generator char_gen; + + std::string result; + + int length = rand() % 10; + for(int i = 0; i < length; ++i) + result += char_gen(); + + //std::generate_n( + // std::back_inserter(result), + // rand() % 10, + // char_gen); + + return result; + } + + float generate(float const*) + { + static boost::variate_generator > + vg((real_generator_type()), boost::uniform_real()); + return vg(); + } + + template std::pair generate( + std::pair const*) + { + static generator g1; + static generator g2; + + return std::pair(g1(), g2()); + } +} + +#endif diff --git a/test/helpers/helpers.hpp b/test/helpers/helpers.hpp new file mode 100644 index 00000000..fa219cda --- /dev/null +++ b/test/helpers/helpers.hpp @@ -0,0 +1,30 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_HEADER + +namespace test +{ + template + inline typename Container::key_type get_key(typename Container::key_type const& x) + { + return x; + } + + template + inline typename Container::key_type get_key(std::pair const& x) + { + return x.first; + } + + template + inline typename Container::key_type get_key(std::pair const& x) + { + return x.first; + } +} + +#endif diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp new file mode 100644 index 00000000..ac37891b --- /dev/null +++ b/test/helpers/invariants.hpp @@ -0,0 +1,82 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +// This header contains metafunctions/functions to get the equivalent +// associative container for an unordered container, and compare the contents. + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER + +#include +#include "./metafunctions.hpp" +#include "./helpers.hpp" + +namespace test +{ + template + void check_equivalent_keys(X const& x1) + { + typename X::key_equal eq = x1.key_eq(); + typedef typename X::key_type key_type; + std::set found_; + + typename X::const_iterator it = x1.begin(), end = x1.end(); + typename X::size_type size = 0; + while(it != end) { + // First test that the current key has not occured before, required + // to test either that keys are unique or that equivalent keys are + // adjacent. (6.3.1/6) + key_type key = get_key(*it); + if(found_.find(key) != found_.end()) + BOOST_ERROR("Elements with equivalent keys aren't adjacent."); + found_.insert(key); + + // Iterate over equivalent keys, counting them. + unsigned int count = 0; + do { + ++it; + ++count; + ++size; + } while(it != end && eq(get_key(*it), key)); + + // If the container has unique keys, test that there's only one. + // Since the previous test makes sure that all equivalent keys are + // adjacent, this is all the equivalent keys - so the test is + // sufficient. (6.3.1/6 again). + if(test::has_unique_keys::value && count != 1) + BOOST_ERROR("Non-unique key."); + + if(x1.count(key) != count) + BOOST_ERROR("Incorrect output of count."); + + // Check that the keys are in the correct bucket and are adjacent in + // the bucket. + typename X::size_type bucket = x1.bucket(key); + typename X::const_local_iterator lit = x1.begin(bucket), lend = x1.end(bucket); + for(; lit != lend && !eq(get_key(*lit), key); ++lit) continue; + if(lit == lend) + BOOST_ERROR("Unable to find element with a local_iterator"); + unsigned int count2 = 0; + for(; lit != lend && eq(get_key(*lit), key); ++lit) ++count2; + if(count != count2) + BOOST_ERROR("Element count doesn't match local_iterator."); + for(; lit != lend; ++lit) { + if(eq(get_key(*lit), key)) { + BOOST_ERROR("Non-adjacent element with equivalent key in bucket."); + break; + } + } + }; + + // Finally, check that size matches up. + if(x1.size() != size) + BOOST_ERROR("x1.size() doesn't match actual size."); + if(static_cast(size) / x1.bucket_count() != x1.load_factor()) + BOOST_ERROR("x1.load_factor() doesn't match actual load_factor."); + } +} + +#endif + diff --git a/test/helpers/metafunctions.hpp b/test/helpers/metafunctions.hpp new file mode 100644 index 00000000..5f8a8ab9 --- /dev/null +++ b/test/helpers/metafunctions.hpp @@ -0,0 +1,103 @@ + +// Copyright Daniel James 2005. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace test +{ + /* + struct unordered_set_type { char x[100]; }; + struct unordered_multiset_type { char x[200]; }; + struct unordered_map_type { char x[300]; }; + struct unordered_multimap_type { char x[400]; }; + + template + unordered_set_type container_type( + boost::unordered_set const*); + template + unordered_multiset_type container_type( + boost::unordered_multiset const*); + template + unordered_map_type container_type( + boost::unordered_map const*); + template + unordered_multimap_type container_type( + boost::unordered_multimap const*); + */ + + template + struct is_set + : public boost::is_same< + typename Container::key_type, + typename Container::value_type> {}; + + template + struct is_map + : public boost::mpl::not_ > {}; + + struct yes_type { char x[100]; }; + struct no_type { char x[200]; }; + + template + yes_type has_unique_key_impl( + boost::unordered_set const*); + template + no_type has_unique_key_impl( + boost::unordered_multiset const*); + template + yes_type has_unique_key_impl( + boost::unordered_map const*); + template + no_type has_unique_key_impl( + boost::unordered_multimap const*); + + template + struct has_unique_keys + { + BOOST_STATIC_CONSTANT(bool, value = + sizeof(has_unique_key_impl((Container const*)0)) + == sizeof(yes_type)); + }; + + template + struct has_equivalent_keys + { + BOOST_STATIC_CONSTANT(bool, value = + sizeof(has_unique_key_impl((Container const*)0)) + == sizeof(no_type)); + }; + + // Non Const Value Type + + template + struct map_non_const_value_type + { + typedef std::pair< + typename Container::key_type, + typename Container::mapped_type> type; + }; + + + template + struct non_const_value_type + : boost::mpl::eval_if, + map_non_const_value_type, + boost::mpl::identity > + { + }; +} + +#endif + diff --git a/test/helpers/random_values.hpp b/test/helpers/random_values.hpp new file mode 100644 index 00000000..91e41902 --- /dev/null +++ b/test/helpers/random_values.hpp @@ -0,0 +1,29 @@ + +// Copyright Daniel James 2005-2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER + +#include +#include +#include "./generators.hpp" +#include "./metafunctions.hpp" + +namespace test +{ + template + struct random_values + : public std::vector::type> + { + random_values(int count) { + typedef typename non_const_value_type::type value_type; + static test::generator gen; + this->reserve(count); + std::generate_n(std::back_inserter(*this), count, gen); + } + }; +} + +#endif diff --git a/test/helpers/tracker.hpp b/test/helpers/tracker.hpp new file mode 100644 index 00000000..d64bd92d --- /dev/null +++ b/test/helpers/tracker.hpp @@ -0,0 +1,146 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +// This header contains metafunctions/functions to get the equivalent +// associative container for an unordered container, and compare the contents. + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_TRACKER_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_TRACKER_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include "../objects/fwd.hpp" +#include "./metafunctions.hpp" +#include "./helpers.hpp" + +namespace test +{ + template + struct equals_to_compare + { + typedef std::less type; + }; + + template <> + struct equals_to_compare + { + typedef test::less type; + }; + + template + void compare_range(X1 const& x1, X2 const& x2) + { + typedef typename non_const_value_type::type value_type; + std::vector values1, values2; + values1.reserve(x1.size()); + values2.reserve(x2.size()); + std::copy(x1.begin(), x1.end(), std::back_inserter(values1)); + std::copy(x2.begin(), x2.end(), std::back_inserter(values2)); + std::sort(values1.begin(), values1.end()); + std::sort(values2.begin(), values2.end()); + BOOST_TEST(values1 == values2); + } + + template + void compare_pairs(X1 const& x1, X2 const& x2, T*) + { + std::vector values1, values2; + values1.reserve(std::distance(x1.first, x1.second)); + values2.reserve(std::distance(x2.first, x2.second)); + std::copy(x1.first, x1.second, std::back_inserter(values1)); + std::copy(x2.first, x2.second, std::back_inserter(values2)); + std::sort(values1.begin(), values1.end()); + std::sort(values2.begin(), values2.end()); + BOOST_TEST(values1 == values2); + } + + template + struct ordered_set + : public boost::mpl::if_< + test::has_unique_keys, + std::set::type>, + std::multiset::type> + > {}; + + template + struct ordered_map + : public boost::mpl::if_< + test::has_unique_keys, + std::map::type>, + std::multimap::type> + > {}; + + template + struct ordered_base + : public boost::mpl::eval_if< + test::is_set, + test::ordered_set, + test::ordered_map > + { + }; + + template + class ordered : public ordered_base::type + { + typedef typename ordered_base::type base; + public: + typedef typename base::key_compare key_compare; + + ordered() + : base() + {} + + explicit ordered(key_compare const& compare) + : base(compare) + {} + + void compare(X const& x) + { + compare_range(x, *this); + } + + void compare_key(X const& x, typename X::value_type const& val) + { + compare_pairs( + x.equal_range(get_key(val)), + this->equal_range(get_key(val)), + (typename non_const_value_type::type*) 0 + ); + } + }; + + template + typename equals_to_compare::type create_compare( + Equals equals) + { + return typename equals_to_compare::type(); + } + + template + ordered create_ordered(X const& container) + { + return ordered(create_compare(container.key_eq())); + } + + template + void check_container(X1 const& container, X2 const& values) + { + ordered tracker = create_ordered(container); + tracker.insert(values.begin(), values.end()); + tracker.compare(container); + } +} + +#endif + diff --git a/test/objects/fwd.hpp b/test/objects/fwd.hpp new file mode 100644 index 00000000..c055de70 --- /dev/null +++ b/test/objects/fwd.hpp @@ -0,0 +1,18 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER + +namespace test +{ + class object; + class hash; + class less; + class equal_to; + template class allocator; +} + +#endif diff --git a/test/objects/test.hpp b/test/objects/test.hpp new file mode 100644 index 00000000..aacedfca --- /dev/null +++ b/test/objects/test.hpp @@ -0,0 +1,202 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) +#define BOOST_UNORDERED_TEST_OBJECTS_HEADER + +#include +#include "../helpers/fwd.hpp" +#include + +namespace test +{ + // Note that the default hash function will work for any equal_to (but not + // very well). + class object; + class hash; + class less; + class equal_to; + template class allocator; + + class object + { + friend class hash; + friend class equal_to; + friend class less; + int tag1_, tag2_; + public: + explicit object(int t1 = 0, int t2 = 0) : tag1_(t1), tag2_(t2) {} + + friend bool operator==(object const& x1, object const& x2) { + return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; + } + + friend bool operator!=(object const& x1, object const& x2) { + return x1.tag1_ != x2.tag1_ || x1.tag2_ != x2.tag2_; + } + + friend bool operator<(object const& x1, object const& x2) { + return x1.tag1_ < x2.tag1_ || + (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); + } + + + friend object generate(object const*) { + int* x; + return object(generate(x), generate(x)); + } + + friend std::ostream& operator<<(std::ostream& out, object const& o) + { + return out<<"("< + class allocator + { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef allocator other; }; + + explicit allocator(int t = 0) {} + template allocator(allocator const& x) {} + allocator(allocator const&) {} + ~allocator() {} + + pointer address(reference r) { return pointer(&r); } + const_pointer address(const_reference r) { return const_pointer(&r); } + + pointer allocate(size_type n) { + return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + pointer allocate(size_type n, const_pointer u) + { + return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + void deallocate(pointer p, size_type n) + { + ::operator delete((void*) p); + } + + void construct(pointer p, T const& t) { new(p) T(t); } + void destroy(pointer p) { p->~T(); } + + size_type max_size() const { return 1000; } + }; + + template + inline bool operator==(allocator const& x, allocator const& y) + { + return true; + } + + template + inline bool operator!=(allocator const& x, allocator const& y) + { + return false; + } +} + +#endif diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 new file mode 100644 index 00000000..007f7759 --- /dev/null +++ b/test/unordered/Jamfile.v2 @@ -0,0 +1,23 @@ + +# Copyright Daniel James 2006. Use, modification, and distribution are +# 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) + +import testing ; + +project unordered-test/unordered + : requirements + intel-linux:"-strict_ansi -cxxlib-icc" + ; + +test-suite unordered-tests + : + [ run equivalent_keys_tests.cpp ] + [ run compile_tests.cpp ] + [ run constructor_tests.cpp ] + [ run copy_tests.cpp ] + [ run assign_tests.cpp ] + [ run insert_tests.cpp ] + [ run erase_tests.cpp ] + [ run find_tests.cpp ] + ; diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp new file mode 100644 index 00000000..91b47371 --- /dev/null +++ b/test/unordered/assign_tests.cpp @@ -0,0 +1,91 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" + +#include + +template +void assign_tests1(T* = 0) +{ + typename T::hasher hf; + typename T::key_equal eq; + + std::cerr<<"assign_tests1.1\n"; + { + T x; + x = x; + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + } + + std::cerr<<"assign_tests1.2\n"; + { + test::random_values v(1000); + T x(v.begin(), v.end()); + + test::ordered tracker = test::create_ordered(x); + tracker.insert(v.begin(), v.end()); + + x = x; + tracker.compare(x); + + T y; + y.max_load_factor(x.max_load_factor() / 20); + y = x; + tracker.compare(y); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + } +} + +template +void assign_tests2(T* = 0) +{ + typename T::hasher hf; + typename T::key_equal eq; + typename T::hasher hf1(1); + typename T::key_equal eq1(1); + typename T::hasher hf2(2); + typename T::key_equal eq2(2); + + std::cerr<<"assign_tests2.1\n"; + { + // TODO: Need to generate duplicates... + test::random_values v(1000); + T x1(v.begin(), v.end(), 0, hf1, eq1); + T x2(0, hf2, eq2); + x2 = x1; + BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); + check_container(x2, v); + } +} + +int main() +{ + assign_tests1((boost::unordered_set*) 0); + assign_tests1((boost::unordered_multiset*) 0); + assign_tests1((boost::unordered_map*) 0); + assign_tests1((boost::unordered_multimap*) 0); + + assign_tests1((boost::unordered_set >*) 0); + assign_tests1((boost::unordered_multiset >*) 0); + assign_tests1((boost::unordered_map >*) 0); + assign_tests1((boost::unordered_multimap >*) 0); + + assign_tests2((boost::unordered_set >*) 0); + assign_tests2((boost::unordered_multiset >*) 0); + assign_tests2((boost::unordered_map >*) 0); + assign_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/compile_tests.cpp b/test/unordered/compile_tests.cpp new file mode 100644 index 00000000..ccc90b0c --- /dev/null +++ b/test/unordered/compile_tests.cpp @@ -0,0 +1,297 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include + +#include +#include +#include +#include "../helpers/check_return_type.hpp" + +#include +#include +#include "../objects/minimal.hpp" + +template void sink(T const&) {} + +template +void unordered_set_test(X& r, Key const& k) +{ + BOOST_MPL_ASSERT((boost::is_same< + typename X::value_type, + typename X::key_type>)); +} + +template +void unordered_map_test(X& r, Key const& k, T const& t) +{ + BOOST_MPL_ASSERT((boost::is_same< + typename X::value_type, + std::pair >)); +} + +template +void unordered_unique_test(X& r, T const& t) +{ + typedef typename X::iterator iterator; + test::check_return_type >::equals(r.insert(t)); +} + +template +void unordered_equivalent_test(X& r, T const& t) +{ + typedef typename X::iterator iterator; + test::check_return_type::equals(r.insert(t)); +} + +template +void unordered_test(X& ref, Key& k, T& t, Hash& hf, Pred& eq) +{ + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename X::local_iterator local_iterator; + typedef typename X::const_local_iterator const_local_iterator; + + typedef typename X::key_type key_type; + typedef typename X::hasher hasher; + typedef typename X::key_equal key_equal; + + BOOST_MPL_ASSERT((boost::is_same)); + boost::function_requires >(); + boost::function_requires >(); + + BOOST_MPL_ASSERT((boost::is_same)); + test::check_return_type::equals(hf(k)); + + BOOST_MPL_ASSERT((boost::is_same)); + test::check_return_type::convertible(eq(k, k)); + // TODO: Pred is an equivalence relation. Doesn't really matter for these + // tests. + + boost::function_requires >(); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::BOOST_ITERATOR_CATEGORY::type, + typename boost::BOOST_ITERATOR_CATEGORY::type >)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_difference::type, + typename boost::iterator_difference::type >)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_pointer::type, + typename boost::iterator_pointer::type >)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_reference::type, + typename boost::iterator_reference::type >)); + + boost::function_requires >(); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::BOOST_ITERATOR_CATEGORY::type, + typename boost::BOOST_ITERATOR_CATEGORY::type >)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_difference::type, + typename boost::iterator_difference::type >)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_pointer::type, + typename boost::iterator_pointer::type >)); + BOOST_MPL_ASSERT((boost::is_same< + typename boost::iterator_reference::type, + typename boost::iterator_reference::type >)); + + X(10, hf, eq); + X a(10, hf, eq); + X(10, hf); + X a2(10, hf); + X(10); + X a3(10); + X(); + X a4(); + + typename X::value_type* i = 0; + typename X::value_type* j = 0; + + X(i, j, 10, hf, eq); + X a5(i, j, 10, hf, eq); + X(i, j, 10, hf); + X a6(i, j, 10, hf); + X(i, j, 10); + X a7(i, j, 10); + X(i, j); + X a8(i, j); + + X const b; + sink(X(b)); + X a9(b); + a = b; + + test::check_return_type::equals(b.hash_function()); + test::check_return_type::equals(b.key_eq()); + + iterator q = a.begin(); + const_iterator r = a.begin(); + test::check_return_type::equals(a.insert(q, t)); + test::check_return_type::equals(a.insert(r, t)); + + // TODO: void return? + a.insert(i, j); + test::check_return_type::equals(a.erase(k)); + + BOOST_TEST(a.empty()); + if(a.empty()) { + a.insert(t); + q = a.begin(); + test::check_return_type::equals(a.erase(q)); + } + + BOOST_TEST(a.empty()); + if(a.empty()) { + a.insert(t); + r = a.begin(); + test::check_return_type::equals(a.erase(r)); + } + + iterator q1 = a.begin(), q2 = a.end(); + test::check_return_type::equals(a.erase(q1, q2)); + + const_iterator r1 = a.begin(), r2 = a.end(); + test::check_return_type::equals(a.erase(r1, r2)); + + // TODO: void return? + a.clear(); + + test::check_return_type::equals(a.find(k)); + test::check_return_type::equals(b.find(k)); + test::check_return_type::equals(b.count(k)); + test::check_return_type >::equals( + a.equal_range(k)); + test::check_return_type >::equals( + b.equal_range(k)); + test::check_return_type::equals(b.bucket_count()); + test::check_return_type::equals(b.max_bucket_count()); + test::check_return_type::equals(b.bucket(k)); + test::check_return_type::equals(b.bucket_size(0)); + + test::check_return_type::equals(a.begin(0)); + test::check_return_type::equals(b.begin(0)); + test::check_return_type::equals(a.end(0)); + test::check_return_type::equals(b.end(0)); + + test::check_return_type::equals(b.load_factor()); + test::check_return_type::equals(b.max_load_factor()); + a.max_load_factor((float) 2.0); + a.rehash(100); +} + +void test1() +{ + boost::hash hash; + std::equal_to equal_to; + int value = 0; + std::pair map_value(0, 0); + + std::cout<<"Test unordered_set.\n"; + + boost::unordered_set set; + + unordered_unique_test(set, value); + unordered_set_test(set, value); + unordered_test(set, value, value, hash, equal_to); + + std::cout<<"Test unordered_multiset.\n"; + + boost::unordered_multiset multiset; + + unordered_equivalent_test(multiset, value); + unordered_set_test(multiset, value); + unordered_test(multiset, value, value, hash, equal_to); + + std::cout<<"Test unordered_map.\n"; + + boost::unordered_map map; + + unordered_unique_test(map, map_value); + unordered_map_test(map, value, value); + unordered_test(map, value, map_value, hash, equal_to); + + std::cout<<"Test unordered_multimap.\n"; + + boost::unordered_multimap multimap; + + unordered_equivalent_test(multimap, map_value); + unordered_map_test(multimap, value, value); + unordered_test(multimap, value, map_value, hash, equal_to); +} + +void test2() +{ + test::minimal::assignable assignable + = test::minimal::assignable::create(); + test::minimal::copy_constructible copy_constructible + = test::minimal::copy_constructible::create(); + test::minimal::hash hash + = test::minimal::hash::create(); + test::minimal::equal_to equal_to + = test::minimal::equal_to::create(); + + typedef std::pair map_value_type; + map_value_type map_value(assignable, copy_constructible); + + std::cout<<"Test unordered_set.\n"; + + boost::unordered_set< + test::minimal::assignable, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > set; + + unordered_unique_test(set, assignable); + unordered_set_test(set, assignable); + unordered_test(set, assignable, assignable, hash, equal_to); + + std::cout<<"Test unordered_multiset.\n"; + + boost::unordered_multiset< + test::minimal::assignable, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > multiset; + + unordered_equivalent_test(multiset, assignable); + unordered_set_test(multiset, assignable); + unordered_test(multiset, assignable, assignable, hash, equal_to); + + std::cout<<"Test unordered_map.\n"; + + boost::unordered_map< + test::minimal::assignable, + test::minimal::copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > map; + + unordered_unique_test(map, map_value); + unordered_map_test(map, assignable, copy_constructible); + unordered_test(map, assignable, map_value, hash, equal_to); + + std::cout<<"Test unordered_multimap.\n"; + + boost::unordered_multimap< + test::minimal::assignable, + test::minimal::copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > multimap; + + unordered_equivalent_test(multimap, map_value); + unordered_map_test(multimap, assignable, copy_constructible); + unordered_test(multimap, assignable, map_value, hash, equal_to); +} + +int main() { + test1(); + test2(); + + return boost::report_errors(); +} diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp new file mode 100644 index 00000000..5114331c --- /dev/null +++ b/test/unordered/constructor_tests.cpp @@ -0,0 +1,202 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" + +#include + +template +void constructor_tests1(T* = 0) +{ + typename T::hasher hf; + typename T::key_equal eq; + + std::cerr<<"Construct 1\n"; + { + T x(0, hf, eq); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 0); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + } + + std::cerr<<"Construct 2\n"; + { + T x(100, hf); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 100); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + } + + std::cerr<<"Construct 3\n"; + { + T x(2000); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 2000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + } + + std::cerr<<"Construct 4\n"; + { + T x; + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + } + + std::cerr<<"Construct 5\n"; + { + test::random_values v(1000); + T x(v.begin(), v.end(), 10000, hf, eq); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + check_container(x, v); + } + + std::cerr<<"Construct 6\n"; + { + test::random_values v(10); + T x(v.begin(), v.end(), 10000, hf); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + check_container(x, v); + } + + std::cerr<<"Construct 7\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 100); + BOOST_TEST(x.bucket_count() >= 100); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + check_container(x, v); + } + + std::cerr<<"Construct 8\n"; + { + test::random_values v(1); + T x(v.begin(), v.end()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + check_container(x, v); + } +} + +template +void constructor_tests2(T* = 0) +{ + typename T::hasher hf; + typename T::hasher hf1(1); + typename T::hasher hf2(2); + typename T::key_equal eq; + typename T::key_equal eq1(1); + typename T::key_equal eq2(2); + + std::cerr<<"Construct 1\n"; + { + T x(10000, hf1, eq1); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + } + + std::cerr<<"Construct 2\n"; + { + T x(100, hf1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 100); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + } + + std::cerr<<"Construct 3\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 0, hf1, eq1); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + check_container(x, v); + } + + std::cerr<<"Construct 4\n"; + { + test::random_values v(5); + T x(v.begin(), v.end(), 1000, hf1); + BOOST_TEST(x.bucket_count() >= 1000); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + check_container(x, v); + } + + + std::cerr<<"Construct 5\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 0, hf, eq); + T y(x.begin(), x.end(), 0, hf1, eq1); + check_container(x, v); + check_container(y, x); + } + + std::cerr<<"Construct 6\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 0, hf1, eq1); + T y(x.begin(), x.end(), 0, hf, eq); + check_container(x, v); + check_container(y, x); + } + + std::cerr<<"Construct 7\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 0, hf1, eq1); + T y(x.begin(), x.end(), 0, hf2, eq2); + check_container(x, v); + check_container(y, x); + } +} + +int main() +{ + std::cerr<<"Test1 unordered_set\n"; + constructor_tests1((boost::unordered_set*) 0); + std::cerr<<"Test1 unordered_multiset\n"; + constructor_tests1((boost::unordered_multiset*) 0); + std::cerr<<"Test1 unordered_map\n"; + constructor_tests1((boost::unordered_map*) 0); + std::cerr<<"Test1 unordered_multimap\n"; + constructor_tests1((boost::unordered_multimap*) 0); + + std::cerr<<"Test1 unordered_set\n"; + constructor_tests1((boost::unordered_set >*) 0); + std::cerr<<"Test1 unordered_multiset\n"; + constructor_tests1((boost::unordered_multiset >*) 0); + std::cerr<<"Test1 unordered_map\n"; + constructor_tests1((boost::unordered_map >*) 0); + std::cerr<<"Test1 unordered_multimap\n"; + constructor_tests1((boost::unordered_multimap >*) 0); + + std::cerr<<"Test2 unordered_set\n"; + constructor_tests2((boost::unordered_set >*) 0); + std::cerr<<"Test2 unordered_multiset\n"; + constructor_tests2((boost::unordered_multiset >*) 0); + std::cerr<<"Test2 unordered_map\n"; + constructor_tests2((boost::unordered_map >*) 0); + std::cerr<<"Test2 unordered_multimap\n"; + constructor_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp new file mode 100644 index 00000000..1a72bf9f --- /dev/null +++ b/test/unordered/copy_tests.cpp @@ -0,0 +1,103 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" +#include "../helpers/invariants.hpp" + +template +void copy_construct_tests1(T* = 0) +{ + typename T::hasher hf; + typename T::key_equal eq; + + { + T x; + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + test::check_equivalent_keys(y); + } + + { + test::random_values v(1000); + + T x(v.begin(), v.end()); + T y(x); + test::unordered_equivalence_tester equivalent(x); + equivalent(y); + test::check_equivalent_keys(y); + } + + { + // In this test I drop the original containers max load factor, so it + // is much lower than the load factor. The hash table is not allowed + // to rehash, but the destination container should probably allocate + // enough buckets to decrease the load factor appropriately. Although, + // I don't think it has to. + test::random_values v(1000); + T x(v.begin(), v.end()); + x.max_load_factor(x.load_factor() / 4); + T y(x); + test::unordered_equivalence_tester equivalent(x); + equivalent(y); + // I don't think this is guaranteed: + BOOST_TEST(y.load_factor() < y.max_load_factor()); + test::check_equivalent_keys(y); + } +} + +template +void copy_construct_tests2(T* ptr = 0) +{ + copy_construct_tests1(ptr); + + typename T::hasher hf(1); + typename T::key_equal eq(1); + + { + // TODO: I could check how many buckets y has, it should be lower (QOI issue). + T x(10000, hf, eq); + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + test::check_equivalent_keys(y); + } + + { + // TODO: Invariant checks are especially important here. + test::random_values v(1000); + + T x(v.begin(), v.end(), 0, hf, eq); + T y(x); + test::unordered_equivalence_tester equivalent(x); + equivalent(y); + test::check_equivalent_keys(y); + } +} + +int main() +{ + copy_construct_tests1((boost::unordered_set*) 0); + copy_construct_tests1((boost::unordered_multiset*) 0); + copy_construct_tests1((boost::unordered_map*) 0); + copy_construct_tests1((boost::unordered_multimap*) 0); + + copy_construct_tests2((boost::unordered_set >*) 0); + copy_construct_tests2((boost::unordered_multiset >*) 0); + copy_construct_tests2((boost::unordered_map >*) 0); + copy_construct_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/equivalent_keys_tests.cpp b/test/unordered/equivalent_keys_tests.cpp new file mode 100644 index 00000000..d6c6a792 --- /dev/null +++ b/test/unordered/equivalent_keys_tests.cpp @@ -0,0 +1,85 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include +#include +#include +#include "../helpers/tracker.hpp" +#include "../helpers/invariants.hpp" + +#include + +template +void test_equal_insertion(Iterator begin, Iterator end) +{ + typedef test::ordered tracker; + + Container x1; + tracker x2 = test::create_ordered(x1); + + for(Iterator it = begin; it != end; ++it) { + x1.insert(*it); + x2.insert(*it); + x2.compare_key(x1, *it); + } + + x2.compare(x1); + test::check_equivalent_keys(x1); +} + +void set_tests() +{ + int values[][5] = { + {1}, + {54, 23}, + {-13, 65}, + {77, 77}, + {986, 25, 986} + }; + + test_equal_insertion >(values[0], values[0] + 1); + test_equal_insertion >(values[1], values[1] + 2); + test_equal_insertion >(values[2], values[2] + 2); + test_equal_insertion >(values[3], values[3] + 2); + test_equal_insertion >(values[4], values[4] + 3); + + test_equal_insertion >(values[0], values[0] + 1); + test_equal_insertion >(values[1], values[1] + 2); + test_equal_insertion >(values[2], values[2] + 2); + test_equal_insertion >(values[3], values[3] + 2); + test_equal_insertion >(values[4], values[4] + 3); +} + +void map_tests() +{ + typedef std::deque > values_type; + values_type v[5]; + v[0].push_back(std::pair(1,1)); + v[1].push_back(std::pair(28,34)); + v[1].push_back(std::pair(16,58)); + v[1].push_back(std::pair(-124, 62)); + v[2].push_back(std::pair(432,12)); + v[2].push_back(std::pair(9,13)); + v[2].push_back(std::pair(432,24)); + + for(int i = 0; i < 5; ++i) + test_equal_insertion >( + v[i].begin(), v[i].end()); + + for(int i2 = 0; i2 < 5; ++i2) + test_equal_insertion >( + v[i2].begin(), v[i2].end()); +} + +int main() +{ + set_tests(); + map_tests(); + + return boost::report_errors(); +} diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp new file mode 100644 index 00000000..5ca4f641 --- /dev/null +++ b/test/unordered/erase_tests.cpp @@ -0,0 +1,140 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" +#include "../helpers/helpers.hpp" + +#include + +template +void erase_tests1(Container* = 0) +{ + std::cerr<<"Erase by key.\n"; + { + test::random_values v(1000); + Container x(v.begin(), v.end()); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + std::size_t count = x.count(test::get_key(*it)); + std::size_t old_size = x.size(); + BOOST_TEST(count == x.erase(test::get_key(*it))); + BOOST_TEST(x.size() == old_size - count); + BOOST_TEST(x.count(test::get_key(*it)) == 0); + BOOST_TEST(x.find(test::get_key(*it)) == x.end()); + } + } + + std::cerr<<"erase(begin()).\n"; + { + test::random_values v(1000); + Container x(v.begin(), v.end()); + std::size_t size = x.size(); + while(size > 0 && !x.empty()) + { + typename Container::key_type key = test::get_key(*x.begin()); + std::size_t count = x.count(key); + typename Container::iterator pos = x.erase(x.begin()); + --size; + BOOST_TEST(pos == x.begin()); + BOOST_TEST(x.count(key) == count - 1); + BOOST_TEST(x.size() == size); + } + BOOST_TEST(x.empty()); + } + + std::cerr<<"erase(random position).\n"; + { + test::random_values v(1000); + Container x(v.begin(), v.end()); + std::size_t size = x.size(); + while(size > 0 && !x.empty()) + { + using namespace std; + int index = rand() % x.size(); + typename Container::const_iterator prev, pos, next; + if(index == 0) { + prev = pos = x.begin(); + } + else { + prev = boost::next(x.begin(), index - 1); + pos = boost::next(prev); + } + next = boost::next(pos); + typename Container::key_type key = test::get_key(*x.begin()); + std::size_t count = x.count(key); + BOOST_TEST(next == x.erase(pos)); + --size; + if(size > 0) + BOOST_TEST(next == + (index == 0 ? x.begin() : boost::next(prev))); + BOOST_TEST(x.count(key) == count - 1); + std::cerr< v(500); + Container x(v.begin(), v.end()); + + std::size_t size = x.size(); + + // I'm actually stretching it a little here, as the standard says it + // returns 'the iterator immediately following the erase elements' + // and if nothing is erased, then there's nothing to follow. But I + // think this is the only sensible option... + BOOST_TEST(x.erase(x.end(), x.end()) == x.end()); + BOOST_TEST(x.erase(x.begin(), x.begin()) == x.begin()); + BOOST_TEST(x.size() == size); + + BOOST_TEST(x.erase(x.begin(), x.end()) == x.end()); + BOOST_TEST(x.empty()); + BOOST_TEST(x.begin() == x.end()); + + BOOST_TEST(x.erase(x.begin(), x.end()) == x.begin()); + } + + // TODO: More range erase tests. + + std::cerr<<"clear().\n"; + { + test::random_values v(500); + Container x(v.begin(), v.end()); + x.clear(); + BOOST_TEST(x.empty()); + BOOST_TEST(x.begin() == x.end()); + } +} + +int main() +{ + std::cerr<<"Erase unordered_set.\n"; + erase_tests1((boost::unordered_set*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + erase_tests1((boost::unordered_multiset*) 0); + std::cerr<<"\nErase unordered_map.\n"; + erase_tests1((boost::unordered_map*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + erase_tests1((boost::unordered_multimap*) 0); + + std::cerr<<"\nErase unordered_set.\n"; + erase_tests1((boost::unordered_set >*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + erase_tests1((boost::unordered_multiset >*) 0); + std::cerr<<"\nErase unordered_map.\n"; + erase_tests1((boost::unordered_map >*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + erase_tests1((boost::unordered_multimap >*) 0); +} diff --git a/test/unordered/find_tests.cpp b/test/unordered/find_tests.cpp new file mode 100644 index 00000000..89b53870 --- /dev/null +++ b/test/unordered/find_tests.cpp @@ -0,0 +1,90 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/helpers.hpp" + +template +void find_tests1(X*) +{ + { + test::random_values v(500); + X x(v.begin(), v.end()); + X const& x_const = x; + test::ordered tracker = test::create_ordered(x); + tracker.insert(v.begin(), v.end()); + + for(typename test::ordered::const_iterator it = + tracker.begin(); it != tracker.end(); ++it) + { + typename X::key_type key = test::get_key(*it); + typename X::iterator pos = x.find(key); + typename X::const_iterator const_pos = x_const.find(key); + BOOST_TEST(pos != x.end() && + x.key_eq()(key, test::get_key(*pos))); + BOOST_TEST(const_pos != x_const.end() && + x_const.key_eq()(key, test::get_key(*const_pos))); + + BOOST_TEST(x.count(key) == tracker.count(key)); + + test::compare_pairs(x.equal_range(key), + tracker.equal_range(key), + (typename test::non_const_value_type::type*) 0); + test::compare_pairs(x_const.equal_range(key), + tracker.equal_range(key), + (typename test::non_const_value_type::type*) 0); + } + + test::random_values v2(500); + for(typename test::random_values::const_iterator it = + v2.begin(); it != v2.end(); ++it) + { + typename X::key_type key = test::get_key(*it); + if(tracker.find(test::get_key(key)) == tracker.end()) + { + BOOST_TEST(x.find(key) == x.end()); + BOOST_TEST(x_const.find(key) == x_const.end()); + BOOST_TEST(x.count(key) == 0); + std::pair range = x.equal_range(key); + BOOST_TEST(range.first == range.second); + } + } + } + + { + X x; + + test::random_values v2(5); + for(typename test::random_values::const_iterator it = + v2.begin(); it != v2.end(); ++it) + { + typename X::key_type key = test::get_key(*it); + BOOST_TEST(x.find(key) == x.end()); + BOOST_TEST(x.count(key) == 0); + std::pair range = x.equal_range(key); + BOOST_TEST(range.first == range.second); + } + } +} + +int main() +{ + find_tests1((boost::unordered_set*) 0); + find_tests1((boost::unordered_multiset*) 0); + find_tests1((boost::unordered_map*) 0); + find_tests1((boost::unordered_multimap*) 0); + + find_tests1((boost::unordered_set >*) 0); + find_tests1((boost::unordered_multiset >*) 0); + find_tests1((boost::unordered_map >*) 0); + find_tests1((boost::unordered_multimap >*) 0); +} diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp new file mode 100644 index 00000000..14d98347 --- /dev/null +++ b/test/unordered/insert_tests.cpp @@ -0,0 +1,188 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" +#include "../helpers/invariants.hpp" + +#include + +template +void unique_insert_tests1(Container* = 0) +{ + std::cerr<<"insert(value) tests for containers with unique keys.\n"; + + Container x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + std::pair r1 = x.insert(*it); + std::pair::iterator, bool> r2 + = tracker.insert(*it); + + BOOST_TEST(r1.second == r2.second); + BOOST_TEST(*r1.first == *r2.first); + + tracker.compare_key(x, *it); + } + + test::check_equivalent_keys(x); +} + +template +void equivalent_insert_tests1(Container* = 0) +{ + std::cerr<<"insert(value) tests for containers with equivalent keys.\n"; + + Container x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename Container::iterator r1 = x.insert(*it); + typename test::ordered::iterator r2 = tracker.insert(*it); + + BOOST_TEST(*r1 == *r2); + + tracker.compare_key(x, *it); + } + + test::check_equivalent_keys(x); +} + +template +void insert_tests2(Container* = 0) +{ + typedef typename test::ordered tracker_type; + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + typedef typename tracker_type::iterator tracker_iterator; + + std::cerr<<"insert(begin(), value) tests.\n"; + + { + Container x; + tracker_type tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + iterator r1 = x.insert(x.begin(), *it); + tracker_iterator r2 = tracker.insert(tracker.begin(), *it); + BOOST_TEST(*r1 == *r2); + tracker.compare_key(x, *it); + } + + test::check_equivalent_keys(x); + } + + std::cerr<<"insert(end(), value) tests.\n"; + + { + Container x; + Container const& x_const = x; + tracker_type tracker = test::create_ordered(x); + + test::random_values v(100); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + const_iterator r1 = x.insert(x_const.end(), *it); + tracker_iterator r2 = tracker.insert(tracker.end(), *it); + BOOST_TEST(*r1 == *r2); + tracker.compare_key(x, *it); + } + + test::check_equivalent_keys(x); + } + + std::cerr<<"insert(pos, value) tests.\n"; + + { + Container x; + const_iterator pos = x.begin(); + tracker_type tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + pos = x.insert(pos, *it); + tracker_iterator r2 = tracker.insert(tracker.begin(), *it); + BOOST_TEST(*pos == *r2); + tracker.compare_key(x, *it); + } + + test::check_equivalent_keys(x); + } + + std::cerr<<"insert single item range tests.\n"; + + { + Container x; + tracker_type tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + x.insert(it, boost::next(it)); + tracker.insert(*it); + tracker.compare_key(x, *it); + } + + test::check_equivalent_keys(x); + } + + std::cerr<<"insert range tests.\n"; + + { + Container x; + const_iterator pos = x.begin(); + + test::random_values v(1000); + x.insert(v.begin(), v.end()); + check_container(x, v); + + test::check_equivalent_keys(x); + } +} + +int main() +{ + unique_insert_tests1((boost::unordered_set*) 0); + equivalent_insert_tests1((boost::unordered_multiset*) 0); + unique_insert_tests1((boost::unordered_map*) 0); + equivalent_insert_tests1((boost::unordered_multimap*) 0); + + unique_insert_tests1((boost::unordered_set >*) 0); + equivalent_insert_tests1((boost::unordered_multiset >*) 0); + unique_insert_tests1((boost::unordered_map >*) 0); + equivalent_insert_tests1((boost::unordered_multimap >*) 0); + + insert_tests2((boost::unordered_set*) 0); + insert_tests2((boost::unordered_multiset*) 0); + insert_tests2((boost::unordered_map*) 0); + insert_tests2((boost::unordered_multimap*) 0); + + insert_tests2((boost::unordered_set >*) 0); + insert_tests2((boost::unordered_multiset >*) 0); + insert_tests2((boost::unordered_map >*) 0); + insert_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} From 822b0c7ffd74258c80e64550953c509a26f4be1c Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 21 May 2006 17:10:03 +0000 Subject: [PATCH 034/175] Remove use of compressed_pair for unordered containers. Put a lower bound on the maximum load factor. [SVN r2958] --- include/boost/unordered/detail/hash_table.hpp | 7 ++-- .../unordered/detail/hash_table_impl.hpp | 34 +++---------------- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index aabe0586..59c7f072 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -35,10 +35,6 @@ #include -#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) -#include -#endif - // See hash_table::swap() for details about this. #if !defined(BOOST_UNORDERED_SWAP_METHOD) #define BOOST_UNORDERED_SWAP_METHOD 3 @@ -65,6 +61,7 @@ namespace boost { template struct type_wrapper {}; const static std::size_t default_initial_bucket_count = 50; + const static float minimum_max_load_factor = 1e-3; inline std::size_t next_prime(std::size_t n); template @@ -73,7 +70,7 @@ namespace boost { #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) std::swap(x,y); #else - using namespace std; + using std::swap; swap(x, y); #endif } diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 62c3c22f..7d52c9f6 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1056,25 +1056,9 @@ namespace boost { private: - // From the compressed functions docs: - // - // "Finally, a word of caution for Visual C++ 6 users: if either - // argument is an empty type, then assigning to that member will - // produce memory corruption, unless the empty type has a "do - // nothing" assignment operator defined. This is due to a bug in - // the way VC6 generates implicit assignment operators." - // - // Nice. - // - // So use std::pair for Visual C++. - class functions { -#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) - boost::compressed_pair functions_; -#else std::pair functions_; -#endif public: @@ -1083,20 +1067,12 @@ namespace boost { hasher const& hash_function() const { -#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) - return functions_.first(); -#else return functions_.first; -#endif } key_equal const& key_eq() const { -#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) - return functions_.second(); -#else return functions_.second; -#endif } }; @@ -1429,8 +1405,9 @@ namespace boost { { bool need_to_reserve = n >= max_load_; // throws - basic: - if (need_to_reserve) rehash_impl(min_buckets_for_size(n)); - BOOST_ASSERT(n < max_load_); + if (need_to_reserve) rehash_impl(min_buckets_for_size(n)); + // TODO: Deal with this special case better: + BOOST_ASSERT(n < max_load_ || this->bucket_count_ == max_bucket_count()); return need_to_reserve; } @@ -1443,13 +1420,10 @@ namespace boost { } // no throw - // - // TODO: the argument is a hint. So don't use it if it's - // unreasonably small. void max_load_factor(float z) { BOOST_ASSERT(z > 0); - mlf_ = z; + mlf_ = (std::max)(z, minimum_max_load_factor); calculate_max_load(); } From fbd37a946bab0c3ab8d644a3f2b537083c3016cf Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 21 May 2006 17:14:11 +0000 Subject: [PATCH 035/175] More tests for unordered associative containers. [SVN r2959] --- test/Jamfile.v2 | 1 + test/container/simple_tests.cpp | 111 ++++++ test/exception/Jamfile.v2 | 29 ++ test/exception/assign_tests.cpp | 79 ++++ test/exception/constructor_tests.cpp | 119 ++++++ test/exception/containers.hpp | 28 ++ test/exception/copy_tests.cpp | 51 +++ test/exception/erase_tests.cpp | 55 +++ test/exception/insert_tests.cpp | 168 +++++++++ test/exception/rehash_tests.cpp | 80 ++++ test/exception/swap_tests.cpp | 107 ++++++ test/helpers/equivalent.hpp | 28 +- test/helpers/invariants.hpp | 41 ++- test/helpers/metafunctions.hpp | 2 +- test/helpers/strong.hpp | 47 +++ test/objects/exception.hpp | 522 +++++++++++++++++++++++++++ test/objects/fwd.hpp | 4 +- test/objects/test.hpp | 38 +- test/unordered/Jamfile.v2 | 6 + test/unordered/assign_tests.cpp | 17 +- test/unordered/bucket_tests.cpp | 60 +++ test/unordered/constructor_tests.cpp | 41 ++- test/unordered/copy_tests.cpp | 13 +- test/unordered/erase_tests.cpp | 4 +- test/unordered/insert_tests.cpp | 136 +++++-- test/unordered/load_factor_tests.cpp | 72 ++++ test/unordered/rehash_tests.cpp | 67 ++++ test/unordered/swap_tests.cpp | 135 +++++++ 28 files changed, 1975 insertions(+), 86 deletions(-) create mode 100644 test/container/simple_tests.cpp create mode 100644 test/exception/Jamfile.v2 create mode 100644 test/exception/assign_tests.cpp create mode 100644 test/exception/constructor_tests.cpp create mode 100644 test/exception/containers.hpp create mode 100644 test/exception/copy_tests.cpp create mode 100644 test/exception/erase_tests.cpp create mode 100644 test/exception/insert_tests.cpp create mode 100644 test/exception/rehash_tests.cpp create mode 100644 test/exception/swap_tests.cpp create mode 100644 test/helpers/strong.hpp create mode 100644 test/objects/exception.hpp create mode 100644 test/unordered/bucket_tests.cpp create mode 100644 test/unordered/load_factor_tests.cpp create mode 100644 test/unordered/rehash_tests.cpp create mode 100644 test/unordered/swap_tests.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 122f58d6..e4fcf07d 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -5,3 +5,4 @@ build-project container ; build-project unordered ; +build-project exception ; diff --git a/test/container/simple_tests.cpp b/test/container/simple_tests.cpp new file mode 100644 index 00000000..94c9f79a --- /dev/null +++ b/test/container/simple_tests.cpp @@ -0,0 +1,111 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +// This test checks the runtime requirements of containers. + +#include +#include +#include +#include +#include "../helpers/equivalent.hpp" + +template +void simple_test(X const& a) +{ + test::unordered_equivalence_tester equivalent(a); + + { + X u; + BOOST_TEST(u.size() == 0); + BOOST_TEST(X().size() == 0); + } + + { + BOOST_TEST(equivalent(X(a))); + } + + { + X u(a); + BOOST_TEST(equivalent(u)); + } + + { + X u = a; + BOOST_TEST(equivalent(u)); + } + + { + X b(a); + BOOST_TEST(b.begin() == const_cast(b).cbegin()); + BOOST_TEST(b.end() == const_cast(b).cend()); + } + + { + // TODO: Also test with a random container... + X b(a); + X c; + BOOST_TEST(equivalent(b)); + BOOST_TEST(c.empty()); + b.swap(c); + BOOST_TEST(b.empty()); + BOOST_TEST(equivalent(c)); + b.swap(c); + BOOST_TEST(b.empty()); + BOOST_TEST(equivalent(c)); + } + + { + X u; + X& r(u); + // TODO: I can't actually see a requirement for that assignment + // returns a reference to itself (just that it returns a reference). + BOOST_TEST(&(r = r) == &r); + BOOST_TEST(r.empty()); + BOOST_TEST(&(r = a) == &r); + BOOST_TEST(equivalent(r)); + BOOST_TEST(&(r = r) == &r); + BOOST_TEST(equivalent(r)); + } + + { + BOOST_TEST(a.size() == + (typename X::size_type) std::distance(a.begin(), a.end())); + } + + // TODO: Test max_size against allocator? + + { + BOOST_TEST(a.empty() == (a.size() == 0)); + } + + { + BOOST_TEST(a.empty() == (a.begin() == a.end())); + X u; + BOOST_TEST(u.begin() == u.end()); + } + + // TODO: Test construction with allocator? +} + +int main() +{ + std::cout<<"Test unordered_set.\n"; + boost::unordered_set set; + simple_test(set); + + std::cout<<"Test unordered_multiset.\n"; + boost::unordered_multiset multiset; + simple_test(multiset); + + std::cout<<"Test unordered_map.\n"; + boost::unordered_map map; + simple_test(map); + + std::cout<<"Test unordered_multimap.\n"; + boost::unordered_multimap multimap; + simple_test(multimap); + + return boost::report_errors(); +} diff --git a/test/exception/Jamfile.v2 b/test/exception/Jamfile.v2 new file mode 100644 index 00000000..158eded5 --- /dev/null +++ b/test/exception/Jamfile.v2 @@ -0,0 +1,29 @@ + +# Copyright Daniel James 2006. Use, modification, and distribution are +# 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) + +import testing ; + +alias framework : /boost/test//boost_unit_test_framework/speed ; + +project unordered-test/exception-tests + : requirements + intel-linux:"-strict_ansi -cxxlib-icc" + ; + +test-suite unordered-tests + : + [ run constructor_tests.cpp framework ] + [ run copy_tests.cpp framework ] + [ run assign_tests.cpp framework ] + [ run insert_tests.cpp framework ] + [ run erase_tests.cpp framework ] + [ run rehash_tests.cpp framework ] + [ run swap_tests.cpp framework : : : + BOOST_UNORDERED_SWAP_METHOD=1 : swap_tests1 ] + [ run swap_tests.cpp framework : : : + BOOST_UNORDERED_SWAP_METHOD=2 : swap_tests2 ] + [ run swap_tests.cpp framework : : : + BOOST_UNORDERED_SWAP_METHOD=3 : swap_tests3 ] + ; diff --git a/test/exception/assign_tests.cpp b/test/exception/assign_tests.cpp new file mode 100644 index 00000000..914b9ef3 --- /dev/null +++ b/test/exception/assign_tests.cpp @@ -0,0 +1,79 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" + +template +struct self_assign_base : public test::exception_base +{ + test::random_values values; + self_assign_base(int count = 0) : values(count) {} + + typedef T data_type; + T init() const { return T(values.begin(), values.end()); } + void run(T& x) const { x = x; } + void check(T const& x) const { test::check_equivalent_keys(x); } +}; + +template +struct self_assign_test1 : self_assign_base {}; + +template +struct self_assign_test2 : self_assign_base +{ + self_assign_test2() : self_assign_base(100) {} +}; + +template +struct assign_base : public test::exception_base +{ + const test::random_values x_values, y_values; + const T x,y; + + assign_base(unsigned int count1, unsigned int count2, int tag1, int tag2) + : x_values(count1), y_values(count2), + x(x_values.begin(), x_values.end(), 0, typename T::hasher(tag1), typename T::key_equal(tag1), typename T::allocator_type(tag1)), + y(y_values.begin(), y_values.end(), 0, typename T::hasher(tag2), typename T::key_equal(tag2), typename T::allocator_type(tag2)) {} + + typedef T data_type; + T init() const { return T(x); } + void run(T& x1) const { x1 = y; } + void check(T const& x1) const { test::check_equivalent_keys(x1); } +}; + +template +struct assign_test1 : assign_base +{ + assign_test1() : assign_base(0, 0, 0, 0) {} +}; + +template +struct assign_test2 : assign_base +{ + assign_test2() : assign_base(60, 0, 0, 0) {} +}; + +template +struct assign_test3 : assign_base +{ + assign_test3() : assign_base(0, 60, 0, 0) {} +}; + +template +struct assign_test4 : assign_base +{ + assign_test4() : assign_base(10, 10, 1, 2) {} +}; + +RUN_EXCEPTION_TESTS( + (self_assign_test1)(self_assign_test2) + (assign_test1)(assign_test2)(assign_test3)(assign_test4), + CONTAINER_SEQ) diff --git a/test/exception/constructor_tests.cpp b/test/exception/constructor_tests.cpp new file mode 100644 index 00000000..ac9206b6 --- /dev/null +++ b/test/exception/constructor_tests.cpp @@ -0,0 +1,119 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" + +struct objects +{ + test::exception::object obj; + test::exception::hash hash; + test::exception::equal_to equal_to; + test::exception::allocator allocator; +}; + +template +struct construct_test1 : public objects, test::exception_base +{ + void run() const { + T x; + } +}; + +template +struct construct_test2 : public objects, test::exception_base +{ + void run() const { + T x(300); + } +}; + +template +struct construct_test3 : public objects, test::exception_base +{ + void run() const { + T x(0, hash); + } +}; + +template +struct construct_test4 : public objects, test::exception_base +{ + void run() const { + T x(0, hash, equal_to); + } +}; + +template +struct construct_test5 : public objects, test::exception_base +{ + void run() const { + T x(50, hash, equal_to, allocator); + } +}; + +template +struct range : public test::exception_base +{ + test::random_values values; + + range() : values(5) {} + range(unsigned int count) : values(count) {} +}; + +template +struct range_construct_test1 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end()); + } +}; + +template +struct range_construct_test2 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 0); + } +}; + +template +struct range_construct_test3 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 0, hash); + } +}; + +template +struct range_construct_test4 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 100, hash, equal_to); + } +}; + +// Need to run at least one test with a fairly large number +// of objects in case it triggers a rehash. +template +struct range_construct_test5 : public range, objects +{ + range_construct_test5() : range(60) {} + + void run() const { + T x(this->values.begin(), this->values.end(), 0, hash, equal_to, allocator); + } +}; + +// TODO: Write a test using an input iterator. + +RUN_EXCEPTION_TESTS( + (construct_test1)(construct_test2)(construct_test3)(construct_test4)(construct_test5) + (range_construct_test1)(range_construct_test2)(range_construct_test3)(range_construct_test4)(range_construct_test5), + CONTAINER_SEQ) diff --git a/test/exception/containers.hpp b/test/exception/containers.hpp new file mode 100644 index 00000000..62132eb1 --- /dev/null +++ b/test/exception/containers.hpp @@ -0,0 +1,28 @@ +#include +#include +#include "../objects/exception.hpp" + +typedef boost::unordered_set< + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > set; +typedef boost::unordered_multiset< + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > multiset; +typedef boost::unordered_map< + test::exception::object, + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > map; +typedef boost::unordered_multimap< + test::exception::object, + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > multimap; + +#define CONTAINER_SEQ (set)(multiset)(map)(multimap) diff --git a/test/exception/copy_tests.cpp b/test/exception/copy_tests.cpp new file mode 100644 index 00000000..d8b653cd --- /dev/null +++ b/test/exception/copy_tests.cpp @@ -0,0 +1,51 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" + +template +struct copy_test1 : public test::exception_base +{ + T x; + + void run() const { + T y(x); + } +}; + +template +struct copy_test2 : public test::exception_base +{ + test::random_values values; + T x; + + copy_test2() : values(5), x(values.begin(), values.end()) {} + + void run() const { + T y(x); + } +}; + +template +struct copy_test3 : public test::exception_base +{ + test::random_values values; + T x; + + copy_test3() : values(100), x(values.begin(), values.end()) {} + + void run() const { + T y(x); + } +}; + +RUN_EXCEPTION_TESTS( + (copy_test1)(copy_test2)(copy_test3), + CONTAINER_SEQ) diff --git a/test/exception/erase_tests.cpp b/test/exception/erase_tests.cpp new file mode 100644 index 00000000..dca138d5 --- /dev/null +++ b/test/exception/erase_tests.cpp @@ -0,0 +1,55 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/helpers.hpp" + +template +struct erase_test_base : public test::exception_base +{ + test::random_values values; + erase_test_base(unsigned int count = 5) : values(count) {} + + typedef T data_type; + + data_type init() const { + return T(values.begin(), values.end()); + } + + void check(T const& x) const { + // TODO: Check that exception was thrown by hash or predicate object? + test::check_equivalent_keys(x); + } +}; + +template +struct erase_by_key_test1 : public erase_test_base +{ + void run(T& x) const + { + typedef typename test::random_values::const_iterator iterator; + + for(iterator it = this->values.begin(), end = this->values.end(); + it != end; ++it) + { + x.erase(test::get_key(*it)); + } + } +}; + +// TODO: More tests... +// Better test by key. +// Test other erase signatures - generally they won't throw, but the standard +// does allow them to. And test clear as well. + +RUN_EXCEPTION_TESTS( + (erase_by_key_test1), + CONTAINER_SEQ) diff --git a/test/exception/insert_tests.cpp b/test/exception/insert_tests.cpp new file mode 100644 index 00000000..7f14c308 --- /dev/null +++ b/test/exception/insert_tests.cpp @@ -0,0 +1,168 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/strong.hpp" + +#include + +template +struct insert_test_base : public test::exception_base +{ + test::random_values values; + insert_test_base(unsigned int count = 5) : values(count) {} + + typedef T data_type; + typedef test::strong strong_type; + + data_type init() const { + return T(); + } + + void check(T const& x, strong_type const& strong) const { + std::string scope(test::scope); + + if(scope.find_first_of("hash::operator()") == std::string::npos) + strong.test(x); + test::check_equivalent_keys(x); + } +}; + +template +struct insert_test1 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(*it); + } + } +}; + +template +struct insert_test2 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(x.begin(), *it); + } + } +}; + +template +struct insert_test3 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + // I don't think there's any need for this here. + //strong.store(x); + x.insert(this->values.begin(), this->values.end()); + } + + void check(T const& x) const { + test::check_equivalent_keys(x); + } +}; + +template +struct insert_test4 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(it, boost::next(it)); + } + } +}; + +template +struct insert_test_rehash1 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + insert_test_rehash1() : insert_test_base(1000) {} + + T init() const { + typedef typename T::size_type size_type; + + T x; + x.max_load_factor(0.25); + size_type bucket_count = x.bucket_count(); + size_type initial_elements = static_cast( + std::ceil(bucket_count * x.max_load_factor()) - 1); + BOOST_REQUIRE(initial_elements < this->values.size()); + x.insert(this->values.begin(), + boost::next(this->values.begin(), initial_elements)); + BOOST_REQUIRE(bucket_count == x.bucket_count()); + return x; + } + + void run(T& x, strong_type& strong) const { + typename T::size_type bucket_count = x.bucket_count(); + int count = 0; + typename T::const_iterator pos = x.cbegin(); + + for(typename test::random_values::const_iterator + it = boost::next(this->values.begin(), x.size()), end = this->values.end(); + it != end && count < 10; ++it, ++count) + { + strong.store(x); + pos = x.insert(pos, *it); + } + + // This isn't actually a failure, but it means the test isn't doing its + // job. + BOOST_REQUIRE(x.bucket_count() != bucket_count); + } +}; + +template +struct insert_test_rehash2 : public insert_test_rehash1 +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + typename T::size_type bucket_count = x.bucket_count(); + int count = 0; + + for(typename test::random_values::const_iterator + it = boost::next(this->values.begin(), x.size()), end = this->values.end(); + it != end && count < 10; ++it, ++count) + { + strong.store(x); + x.insert(*it); + } + + // This isn't actually a failure, but it means the test isn't doing its + // job. + BOOST_REQUIRE(x.bucket_count() != bucket_count); + } +}; + +RUN_EXCEPTION_TESTS( + (insert_test1)(insert_test2)(insert_test3)(insert_test4) + (insert_test_rehash1)(insert_test_rehash2), + CONTAINER_SEQ) diff --git a/test/exception/rehash_tests.cpp b/test/exception/rehash_tests.cpp new file mode 100644 index 00000000..7f67aaed --- /dev/null +++ b/test/exception/rehash_tests.cpp @@ -0,0 +1,80 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/strong.hpp" + +#include + +template +struct rehash_test_base : public test::exception_base +{ + test::random_values values; + unsigned int n; + rehash_test_base(unsigned int count = 100, unsigned int n = 0) : values(count), n(n) {} + + typedef T data_type; + typedef test::strong strong_type; + + data_type init() const { + T x(values.begin(), values.end(), n); + return x; + } + + void check(T const& x, strong_type const& strong) const { + std::string scope(test::scope); + + if(scope.find_first_of("hash::operator()") == std::string::npos && + scope.find_first_of("equal_to::operator()") == std::string::npos) + strong.test(x); + test::check_equivalent_keys(x); + } +}; + +template +struct rehash_test0 : rehash_test_base +{ + rehash_test0() : rehash_test_base(0) {} + void run(T& x) const { x.rehash(0); } +}; + +template +struct rehash_test1 : rehash_test_base +{ + rehash_test1() : rehash_test_base(0) {} + void run(T& x) const { x.rehash(200); } +}; + +template +struct rehash_test2 : rehash_test_base +{ + rehash_test2() : rehash_test_base(0, 200) {} + void run(T& x) const { x.rehash(0); } +}; + +template +struct rehash_test3 : rehash_test_base +{ + rehash_test3() : rehash_test_base(10, 0) {} + void run(T& x) const { x.rehash(200); } +}; + +template +struct rehash_test4 : rehash_test_base +{ + rehash_test4() : rehash_test_base(10, 200) {} + void run(T& x) const { x.rehash(0); } +}; + +RUN_EXCEPTION_TESTS( + (rehash_test0)(rehash_test1)(rehash_test2)(rehash_test3)(rehash_test4), + CONTAINER_SEQ) diff --git a/test/exception/swap_tests.cpp b/test/exception/swap_tests.cpp new file mode 100644 index 00000000..4be40fce --- /dev/null +++ b/test/exception/swap_tests.cpp @@ -0,0 +1,107 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" + +template +struct self_swap_base : public test::exception_base +{ + test::random_values values; + self_swap_base(int count = 0) : values(count) {} + + typedef T data_type; + T init() const { return T(values.begin(), values.end()); } + void run(T& x) const { x.swap(x); } + void check(T const& x) const { + std::string scope(test::scope); + +#if BOOST_UNORDERED_SWAP_METHOD != 2 + BOOST_CHECK( + scope == "hash::operator(hash)" || + scope == "hash::operator=(hash)" || + scope == "equal_to::operator(equal_to)" || + scope == "equal_to::operator=(equal_to)"); +#endif + + test::check_equivalent_keys(x); + } +}; + +template +struct self_swap_test1 : self_swap_base {}; + +template +struct self_swap_test2 : self_swap_base +{ + self_swap_test2() : self_swap_base(100) {} +}; + +template +struct swap_base : public test::exception_base +{ + const test::random_values x_values, y_values; + const T initial_x, initial_y; + + swap_base(unsigned int count1, unsigned int count2, int tag1, int tag2) + : x_values(count1), y_values(count2), + initial_x(x_values.begin(), x_values.end(), 0, typename T::hasher(tag1), + typename T::key_equal(tag1), typename T::allocator_type(tag1)), + initial_y(y_values.begin(), y_values.end(), 0, typename T::hasher(tag2), + typename T::key_equal(tag2), typename T::allocator_type(tag2)) + {} + + struct data_type { + data_type(T const& x, T const& y) + : x(x), y(y) {} + + T x, y; + }; + + data_type init() const { return data_type(initial_x, initial_y); } + void run(data_type& d) const { + try { + d.x.swap(d.y); + } catch (std::runtime_error) {} + } + void check(data_type const& d) const { + test::check_equivalent_keys(d.x); + test::check_equivalent_keys(d.y); + } +}; + +template +struct swap_test1 : swap_base +{ + swap_test1() : swap_base(0, 0, 0, 0) {} +}; + +template +struct swap_test2 : swap_base +{ + swap_test2() : swap_base(60, 0, 0, 0) {} +}; + +template +struct swap_test3 : swap_base +{ + swap_test3() : swap_base(0, 60, 0, 0) {} +}; + +template +struct swap_test4 : swap_base +{ + swap_test4() : swap_base(10, 10, 1, 2) {} +}; + +RUN_EXCEPTION_TESTS( + (self_swap_test1)(self_swap_test2) + (swap_test1)(swap_test2)(swap_test3)(swap_test4), + CONTAINER_SEQ) diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp index 8d44fafd..9b4e6c8e 100644 --- a/test/helpers/equivalent.hpp +++ b/test/helpers/equivalent.hpp @@ -15,20 +15,38 @@ namespace test { template - bool equivalent(T const& x, T const& y) { + bool equivalent_impl(T const& x, T const& y) { return x == y; } template - bool equivalent(boost::hash const&, boost::hash const&) { + bool equivalent_impl(boost::hash const&, boost::hash const&) { return true; } template - bool equivalent(std::equal_to const&, std::equal_to const&) { + bool equivalent_impl(std::equal_to const&, std::equal_to const&) { return true; } + template + bool equivalent_impl(std::pair const& x1, + std::pair const& x2) { + return equivalent_impl(x1.first, x2.first) && + equivalent_impl(x1.second, x2.second); + } + + struct equivalent_type { + template + bool operator()(T1 const& x, T2 const& y) { + return equivalent_impl(x, y); + } + }; + + namespace { + equivalent_type equivalent; + } + template class unordered_equivalence_tester { @@ -56,8 +74,8 @@ namespace test bool operator()(Container const& x) const { if(!((size_ == x.size()) && - (test::equivalent(hasher_, x.hash_function())) && - (test::equivalent(key_equal_, x.key_eq())) && + (test::equivalent_impl(hasher_, x.hash_function())) && + (test::equivalent_impl(key_equal_, x.key_eq())) && (max_load_factor_ == x.max_load_factor()) && (values_.size() == x.size()))) return false; diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index ac37891b..38b8148e 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -51,23 +51,30 @@ namespace test if(x1.count(key) != count) BOOST_ERROR("Incorrect output of count."); - // Check that the keys are in the correct bucket and are adjacent in - // the bucket. - typename X::size_type bucket = x1.bucket(key); - typename X::const_local_iterator lit = x1.begin(bucket), lend = x1.end(bucket); - for(; lit != lend && !eq(get_key(*lit), key); ++lit) continue; - if(lit == lend) - BOOST_ERROR("Unable to find element with a local_iterator"); - unsigned int count2 = 0; - for(; lit != lend && eq(get_key(*lit), key); ++lit) ++count2; - if(count != count2) - BOOST_ERROR("Element count doesn't match local_iterator."); - for(; lit != lend; ++lit) { - if(eq(get_key(*lit), key)) { - BOOST_ERROR("Non-adjacent element with equivalent key in bucket."); - break; - } - } + // I'm not bothering with the following test for now, as the + // previous test is probably more enough to catch the kind of + // errors that this would catch (if an element was in the wrong + // bucket it not be found by the call to count, if elements are not + // adjacent then they would be caught when checking against + // found_. + + // // Check that the keys are in the correct bucket and are + // // adjacent in the bucket. + // typename X::size_type bucket = x1.bucket(key); + // typename X::const_local_iterator lit = x1.begin(bucket), lend = x1.end(bucket); + // for(; lit != lend && !eq(get_key(*lit), key); ++lit) continue; + // if(lit == lend) + // BOOST_ERROR("Unable to find element with a local_iterator"); + // unsigned int count2 = 0; + // for(; lit != lend && eq(get_key(*lit), key); ++lit) ++count2; + // if(count != count2) + // BOOST_ERROR("Element count doesn't match local_iterator."); + // for(; lit != lend; ++lit) { + // if(eq(get_key(*lit), key)) { + // BOOST_ERROR("Non-adjacent element with equivalent key in bucket."); + // break; + // } + // } }; // Finally, check that size matches up. diff --git a/test/helpers/metafunctions.hpp b/test/helpers/metafunctions.hpp index 5f8a8ab9..05f786d5 100644 --- a/test/helpers/metafunctions.hpp +++ b/test/helpers/metafunctions.hpp @@ -1,5 +1,5 @@ -// Copyright Daniel James 2005. Use, modification, and distribution are +// Copyright Daniel James 2005-2006. Use, modification, and distribution are // 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) diff --git a/test/helpers/strong.hpp b/test/helpers/strong.hpp new file mode 100644 index 00000000..46a836e8 --- /dev/null +++ b/test/helpers/strong.hpp @@ -0,0 +1,47 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER + +#include +#include +#include +#include "./metafunctions.hpp" +#include "./equivalent.hpp" +#include "../objects/exception.hpp" + +namespace test +{ + template + class strong + { + typedef std::vector::type> values_type; + values_type values_; + public: + void store(X const& x) { + // TODO: Well, I shouldn't be disabling exceptions here. Instead + // the test runner should provide a method to the test which + // disables exceptions and calls this. + // + // Actually, the test runner could also keep track of how many times + // store is called in a run. Because it knows that in the next run + // the first n-1 stores are unnecessary - since no exceptions will + // be thrown for them. + DISABLE_EXCEPTIONS; + values_.clear(); + values_.reserve(x.size()); + std::copy(x.cbegin(), x.cend(), std::back_inserter(values_)); + } + + void test(X const& x) const { + if(!(x.size() == values_.size() && + std::equal(x.cbegin(), x.cend(), values_.begin(), test::equivalent))) + BOOST_ERROR("Strong exception safety failure."); + } + }; +} + +#endif diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp new file mode 100644 index 00000000..4727ef28 --- /dev/null +++ b/test/objects/exception.hpp @@ -0,0 +1,522 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) +#define BOOST_UNORDERED_TEST_OBJECTS_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../helpers/fwd.hpp" + +// TODO: +// a) This can only be included in compile unit. +// b) This stuff should be somewhere else. +// but I'm feeling too lazy right now (although sadly not lazy enough to +// avoid reinventing yet another wheel). + +#define RUN_EXCEPTION_TESTS(test_seq, param_seq) \ + BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_EXCEPTION_TESTS_OP, (test_seq)(param_seq)) + +#define RUN_EXCEPTION_TESTS_OP(r, product) \ + RUN_EXCEPTION_TESTS_OP2( \ + BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(1, product)) \ + ), \ + BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_SEQ_ELEM(1, product) \ + ) + +#define RUN_EXCEPTION_TESTS_OP2(name, test_func, type) \ + BOOST_AUTO_TEST_CASE(name) \ + { \ + test_func< type > fixture; \ + ::test::exception_safety(fixture, BOOST_STRINGIZE(test_func)); \ + } + +#define SCOPE(scope_name) \ + BOOST_ITEST_SCOPE(scope_name); \ + for(::test::scope_guard unordered_test_guard( \ + BOOST_STRINGIZE(scope_name)); \ + !unordered_test_guard.dismissed(); \ + unordered_test_guard.dismiss()) + +#define EPOINT(name) \ + if(::test::exceptions_enabled) { \ + BOOST_ITEST_EPOINT(name); \ + } + +#define ENABLE_EXCEPTIONS \ + ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(true) +#define DISABLE_EXCEPTIONS \ + ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false) + +namespace test { + static char const* scope = ""; + bool exceptions_enabled = false; + + class scope_guard { + scope_guard& operator=(scope_guard const&); + scope_guard(scope_guard const&); + + char const* old_scope_; + char const* scope_; + bool dismissed_; + public: + scope_guard(char const* name) + : old_scope_(scope), + scope_(name), + dismissed_(false) + { + scope = scope_; + } + + ~scope_guard() { + if(dismissed_) scope = old_scope_; + } + + void dismiss() { + dismissed_ = true; + } + + bool dismissed() const { + return dismissed_; + } + }; + + class exceptions_enable + { + exceptions_enable& operator=(exceptions_enable const&); + exceptions_enable(exceptions_enable const&); + + bool old_value_; + public: + exceptions_enable(bool enable) + : old_value_(exceptions_enabled) + { + exceptions_enabled = enable; + } + + ~exceptions_enable() + { + exceptions_enabled = old_value_; + } + }; + + struct exception_base { + struct data_type {}; + struct strong_type { + template void store(T const&) {} + template void test(T const&) const {} + }; + data_type init() const { return data_type(); } + void check() const {} + }; + + template + inline void call_with_increased_arity(void (T::*fn)() const, T2 const& obj, + P1&, P2&) + { + (obj.*fn)(); + } + + template + inline void call_with_increased_arity(void (T::*fn)(P1&) const, T2 const& obj, + P1& p1, P2&) + { + (obj.*fn)(p1); + } + + template + inline void call_with_increased_arity(void (T::*fn)(P1&, P2&) const, T2 const& obj, + P1& p1, P2& p2) + { + (obj.*fn)(p1, p2); + } + + template + T const& constant(T const& x) { + return x; + } + + template + class test_runner + { + Test const& test_; + public: + test_runner(Test const& t) : test_(t) {} + void operator()() const { + DISABLE_EXCEPTIONS; + typename Test::data_type x(test_.init()); + typename Test::strong_type strong; + strong.store(x); + try { + ENABLE_EXCEPTIONS; + call_with_increased_arity(&Test::run, test_, x, strong); + } + catch(...) { + call_with_increased_arity(&Test::check, test_, + constant(x), constant(strong)); + throw; + } + } + }; + + template + void exception_safety(Test const& f, char const* name) { + test_runner runner(f); + ::boost::itest::exception_safety(runner, name); + } +} + +namespace test +{ +namespace exception +{ + class object; + class hash; + class equal_to; + template class allocator; + + class object + { + public: + int tag1_, tag2_; + + explicit object() : tag1_(0), tag2_(0) + { + SCOPE(object::object()) { + EPOINT("Mock object default constructor."); + } + } + + explicit object(int t1, int t2 = 0) : tag1_(t1), tag2_(t2) + { + SCOPE(object::object(int)) { + EPOINT("Mock object constructor by value."); + } + } + + object(object const& x) + : tag1_(x.tag1_), tag2_(x.tag2_) + { + SCOPE(object::object(object)) { + EPOINT("Mock object copy constructor."); + } + } + + object& operator=(object const& x) + { + SCOPE(object::operator=(object)) { + tag1_ = x.tag1_; + EPOINT("Mock object assign operator 1."); + tag2_ = x.tag2_; + //EPOINT("Mock object assign operator 2."); + } + return *this; + } + + friend bool operator==(object const& x1, object const& x2) { + SCOPE(operator==(object, object)) { + EPOINT("Mock object equality operator."); + } + + return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; + } + + friend bool operator!=(object const& x1, object const& x2) { + SCOPE(operator!=(object, object)) { + EPOINT("Mock object inequality operator."); + } + + return !(x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_); + } + + // None of the last few functions are used by the unordered associative + // containers - so there aren't any exception points. + friend bool operator<(object const& x1, object const& x2) { + return x1.tag1_ < x2.tag1_ || + (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); + } + + friend object generate(object const*) { + int* x = 0; + return object(::test::generate(x), ::test::generate(x)); + } + + friend std::ostream& operator<<(std::ostream& out, object const& o) + { + return out<<"("< + class allocator + { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef allocator other; }; + + explicit allocator(int t = 0) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator default constructor."); + } + } + + template allocator(allocator const& x) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator template copy constructor."); + } + } + + allocator(allocator const&) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator copy constructor."); + } + } + + ~allocator() {} + + allocator& operator=(allocator const&) { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator assignment operator."); + } + return *this; + } + + pointer address(reference r) { + // TODO: Is this no throw? Major problems if it isn't. + //SCOPE(allocator::address(reference)) { + // EPOINT("Mock allocator address function."); + //} + return pointer(&r); + } + + const_pointer address(const_reference r) { + // TODO: Is this no throw? Major problems if it isn't. + //SCOPE(allocator::address(const_reference)) { + // EPOINT("Mock allocator const address function."); + //} + return const_pointer(&r); + } + + pointer allocate(size_type n) { + T* ptr = 0; + SCOPE(allocator::allocate(size_type)) { + EPOINT("Mock allocator allocate function."); + + using namespace std; + ptr = (T*) malloc(n * sizeof(T)); + if(!ptr) throw std::bad_alloc(); + } + return pointer(ptr); + + //return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + pointer allocate(size_type n, const_pointer u) + { + T* ptr = 0; + SCOPE(allocator::allocate(size_type, const_pointer)) { + EPOINT("Mock allocator allocate function."); + + using namespace std; + ptr = (T*) malloc(n * sizeof(T)); + if(!ptr) throw std::bad_alloc(); + } + return pointer(ptr); + + //return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + void deallocate(pointer p, size_type n) + { + //::operator delete((void*) p); + if(p) { + using namespace std; + free(p); + } + } + + void construct(pointer p, T const& t) { + SCOPE(allocator::construct(pointer, T)) { + EPOINT("Mock allocator construct function."); + new(p) T(t); + } + } + + void destroy(pointer p) { p->~T(); } + + size_type max_size() const { + SCOPE(allocator::construct(pointer, T)) { + EPOINT("Mock allocator max_size function."); + } + return (std::numeric_limits::max)(); + } + }; + + template + inline bool operator==(allocator const& x, allocator const& y) + { + // TODO: I can't meet the exception requirements for swap if this + // throws. Does the standard specify that allocator comparisons can't + // throw? + // + //SCOPE(operator==(allocator, allocator)) { + // EPOINT("Mock allocator equality operator."); + //} + return true; + } + + template + inline bool operator!=(allocator const& x, allocator const& y) + { + //SCOPE(operator!=(allocator, allocator)) { + // EPOINT("Mock allocator inequality operator."); + //} + return false; + } +} +} + +#endif diff --git a/test/objects/fwd.hpp b/test/objects/fwd.hpp index c055de70..aea15586 100644 --- a/test/objects/fwd.hpp +++ b/test/objects/fwd.hpp @@ -3,8 +3,8 @@ // 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) -#if !defined(BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER +#if !defined(BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER) +#define BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER namespace test { diff --git a/test/objects/test.hpp b/test/objects/test.hpp index aacedfca..15114033 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -147,6 +147,12 @@ namespace test template class allocator { +# ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS + public: +# else + template friend class allocator; +# endif + int tag_; public: typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; @@ -158,9 +164,9 @@ namespace test template struct rebind { typedef allocator other; }; - explicit allocator(int t = 0) {} - template allocator(allocator const& x) {} - allocator(allocator const&) {} + explicit allocator(int t = 0) : tag_(t) {} + template allocator(allocator const& x) : tag_(x.tag_) {} + allocator(allocator const& x) : tag_(x.tag_) {} ~allocator() {} pointer address(reference r) { return pointer(&r); } @@ -183,20 +189,20 @@ namespace test void construct(pointer p, T const& t) { new(p) T(t); } void destroy(pointer p) { p->~T(); } - size_type max_size() const { return 1000; } + size_type max_size() const { + return (std::numeric_limits::max)(); + } + + friend bool operator==(allocator const& x, allocator const& y) + { + return x.tag_ == y.tag_; + } + + friend bool operator!=(allocator const& x, allocator const& y) + { + return x.tag_ != y.tag_; + } }; - - template - inline bool operator==(allocator const& x, allocator const& y) - { - return true; - } - - template - inline bool operator!=(allocator const& x, allocator const& y) - { - return false; - } } #endif diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 007f7759..d7c2efc4 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -20,4 +20,10 @@ test-suite unordered-tests [ run insert_tests.cpp ] [ run erase_tests.cpp ] [ run find_tests.cpp ] + [ run bucket_tests.cpp ] + [ run load_factor_tests.cpp ] + [ run rehash_tests.cpp ] + [ run swap_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=1 : swap_tests1 ] + [ run swap_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=2 : swap_tests2 ] + [ run swap_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=3 : swap_tests3 ] ; diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index 91b47371..b7053b97 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -53,9 +53,11 @@ void assign_tests2(T* = 0) typename T::hasher hf; typename T::key_equal eq; typename T::hasher hf1(1); - typename T::key_equal eq1(1); typename T::hasher hf2(2); + typename T::key_equal eq1(1); typename T::key_equal eq2(2); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); std::cerr<<"assign_tests2.1\n"; { @@ -68,6 +70,19 @@ void assign_tests2(T* = 0) BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); check_container(x2, v); } + + std::cerr<<"assign_tests2.2\n"; + { + // TODO: Need to generate duplicates... + test::random_values v1(100), v2(100); + T x1(v1.begin(), v1.end(), 0, hf1, eq1, al1); + T x2(v2.begin(), v2.end(), 0, hf2, eq2, al2); + x2 = x1; + BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x2.get_allocator(), al2)); + check_container(x2, v1); + } } int main() diff --git a/test/unordered/bucket_tests.cpp b/test/unordered/bucket_tests.cpp new file mode 100644 index 00000000..b4b78041 --- /dev/null +++ b/test/unordered/bucket_tests.cpp @@ -0,0 +1,60 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/helpers.hpp" + +template +void bucket_tests(X* = 0) +{ + typedef typename X::size_type size_type; + typedef typename X::const_local_iterator const_local_iterator; + test::random_values v(1000); + + X x(v.begin(), v.end()); + + BOOST_TEST(x.bucket_count() < x.max_bucket_count()); + std::cerr<::const_iterator + it = v.begin(), end = v.end(); it != end; ++it) + { + size_type bucket = x.bucket(test::get_key(*it)); + + BOOST_TEST(bucket < x.bucket_count()); + if(bucket < x.max_bucket_count()) { + // lit? lend?? I need a new naming scheme. + const_local_iterator lit = x.begin(bucket), lend = x.end(bucket); + while(lit != lend && test::get_key(*it) != test::get_key(*lit)) ++lit; + BOOST_TEST(lit != lend); + } + } + + for(size_type i = 0; i < x.bucket_count(); ++i) { + BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x.begin(i), x.end(i))); + X const& x_ref(x); + BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x_ref.begin(i), x_ref.end(i))); + } +} + +int main() +{ + bucket_tests((boost::unordered_set*) 0); + bucket_tests((boost::unordered_multiset*) 0); + bucket_tests((boost::unordered_map*) 0); + bucket_tests((boost::unordered_multimap*) 0); + + bucket_tests((boost::unordered_set >*) 0); + bucket_tests((boost::unordered_multiset >*) 0); + bucket_tests((boost::unordered_map >*) 0); + bucket_tests((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 5114331c..c8d58dd3 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -18,6 +18,7 @@ void constructor_tests1(T* = 0) { typename T::hasher hf; typename T::key_equal eq; + typename T::allocator_type al; std::cerr<<"Construct 1\n"; { @@ -26,6 +27,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 0); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 2\n"; @@ -35,6 +37,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 100); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 3\n"; @@ -44,6 +47,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 2000); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 4\n"; @@ -52,6 +56,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.empty()); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 5\n"; @@ -61,6 +66,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 10000); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -71,6 +77,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 10000); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -81,6 +88,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 100); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -90,6 +98,28 @@ void constructor_tests1(T* = 0) T x(v.begin(), v.end()); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + check_container(x, v); + } + + std::cerr<<"Construct 9\n"; + { + T x(0, hf, eq, al); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 0); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + std::cerr<<"Construct 10\n"; + { + test::random_values v(1000); + T x(v.begin(), v.end(), 10000, hf, eq, al); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } } @@ -103,6 +133,9 @@ void constructor_tests2(T* = 0) typename T::key_equal eq; typename T::key_equal eq1(1); typename T::key_equal eq2(2); + typename T::allocator_type al; + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); std::cerr<<"Construct 1\n"; { @@ -110,6 +143,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(x.bucket_count() >= 10000); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 2\n"; @@ -119,6 +153,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(x.bucket_count() >= 100); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 3\n"; @@ -127,6 +162,7 @@ void constructor_tests2(T* = 0) T x(v.begin(), v.end(), 0, hf1, eq1); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -137,6 +173,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(x.bucket_count() >= 1000); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -144,8 +181,8 @@ void constructor_tests2(T* = 0) std::cerr<<"Construct 5\n"; { test::random_values v(100); - T x(v.begin(), v.end(), 0, hf, eq); - T y(x.begin(), x.end(), 0, hf1, eq1); + T x(v.begin(), v.end(), 0, hf, eq, al1); + T y(x.begin(), x.end(), 0, hf1, eq1, al2); check_container(x, v); check_container(y, x); } diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index 1a72bf9f..202286b0 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -17,6 +17,7 @@ void copy_construct_tests1(T* = 0) { typename T::hasher hf; typename T::key_equal eq; + typename T::allocator_type al; { T x; @@ -24,6 +25,7 @@ void copy_construct_tests1(T* = 0) BOOST_TEST(y.empty()); BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); BOOST_TEST(x.max_load_factor() == y.max_load_factor()); test::check_equivalent_keys(y); } @@ -42,15 +44,14 @@ void copy_construct_tests1(T* = 0) // In this test I drop the original containers max load factor, so it // is much lower than the load factor. The hash table is not allowed // to rehash, but the destination container should probably allocate - // enough buckets to decrease the load factor appropriately. Although, - // I don't think it has to. + // enough buckets to decrease the load factor appropriately. test::random_values v(1000); T x(v.begin(), v.end()); x.max_load_factor(x.load_factor() / 4); T y(x); test::unordered_equivalence_tester equivalent(x); equivalent(y); - // I don't think this is guaranteed: + // This isn't guaranteed: BOOST_TEST(y.load_factor() < y.max_load_factor()); test::check_equivalent_keys(y); } @@ -63,14 +64,16 @@ void copy_construct_tests2(T* ptr = 0) typename T::hasher hf(1); typename T::key_equal eq(1); + typename T::allocator_type al(1); { // TODO: I could check how many buckets y has, it should be lower (QOI issue). - T x(10000, hf, eq); + T x(10000, hf, eq, al); T y(x); BOOST_TEST(y.empty()); BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); BOOST_TEST(x.max_load_factor() == y.max_load_factor()); test::check_equivalent_keys(y); } @@ -79,7 +82,7 @@ void copy_construct_tests2(T* ptr = 0) // TODO: Invariant checks are especially important here. test::random_values v(1000); - T x(v.begin(), v.end(), 0, hf, eq); + T x(v.begin(), v.end(), 0, hf, eq, al); T y(x); test::unordered_equivalence_tester equivalent(x); equivalent(y); diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index 5ca4f641..a920a7a6 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -70,7 +70,7 @@ void erase_tests1(Container* = 0) pos = boost::next(prev); } next = boost::next(pos); - typename Container::key_type key = test::get_key(*x.begin()); + typename Container::key_type key = test::get_key(*pos); std::size_t count = x.count(key); BOOST_TEST(next == x.erase(pos)); --size; @@ -137,4 +137,6 @@ int main() erase_tests1((boost::unordered_map >*) 0); std::cerr<<"\nErase unordered_multimap.\n"; erase_tests1((boost::unordered_multimap >*) 0); + + return boost::report_errors(); } diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 14d98347..1d3be884 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -15,76 +15,94 @@ #include -template -void unique_insert_tests1(Container* = 0) +template +void unique_insert_tests1(X* = 0) { std::cerr<<"insert(value) tests for containers with unique keys.\n"; - Container x; - test::ordered tracker = test::create_ordered(x); + X x; + test::ordered tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { - std::pair r1 = x.insert(*it); - std::pair::iterator, bool> r2 + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + std::pair r1 = x.insert(*it); + std::pair::iterator, bool> r2 = tracker.insert(*it); BOOST_TEST(r1.second == r2.second); BOOST_TEST(*r1.first == *r2.first); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); } -template -void equivalent_insert_tests1(Container* = 0) +template +void equivalent_insert_tests1(X* = 0) { std::cerr<<"insert(value) tests for containers with equivalent keys.\n"; - Container x; - test::ordered tracker = test::create_ordered(x); + X x; + test::ordered tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { - typename Container::iterator r1 = x.insert(*it); - typename test::ordered::iterator r2 = tracker.insert(*it); + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + typename X::iterator r1 = x.insert(*it); + typename test::ordered::iterator r2 = tracker.insert(*it); BOOST_TEST(*r1 == *r2); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); } -template -void insert_tests2(Container* = 0) +template +void insert_tests2(X* = 0) { - typedef typename test::ordered tracker_type; - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; + typedef typename test::ordered tracker_type; + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; typedef typename tracker_type::iterator tracker_iterator; std::cerr<<"insert(begin(), value) tests.\n"; { - Container x; + X x; tracker_type tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + iterator r1 = x.insert(x.begin(), *it); tracker_iterator r2 = tracker.insert(tracker.begin(), *it); BOOST_TEST(*r1 == *r2); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); @@ -93,18 +111,24 @@ void insert_tests2(Container* = 0) std::cerr<<"insert(end(), value) tests.\n"; { - Container x; - Container const& x_const = x; + X x; + X const& x_const = x; tracker_type tracker = test::create_ordered(x); - test::random_values v(100); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(100); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + const_iterator r1 = x.insert(x_const.end(), *it); tracker_iterator r2 = tracker.insert(tracker.end(), *it); BOOST_TEST(*r1 == *r2); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); @@ -113,18 +137,24 @@ void insert_tests2(Container* = 0) std::cerr<<"insert(pos, value) tests.\n"; { - Container x; + X x; const_iterator pos = x.begin(); tracker_type tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + pos = x.insert(pos, *it); tracker_iterator r2 = tracker.insert(tracker.begin(), *it); BOOST_TEST(*pos == *r2); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); @@ -133,16 +163,22 @@ void insert_tests2(Container* = 0) std::cerr<<"insert single item range tests.\n"; { - Container x; + X x; tracker_type tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + x.insert(it, boost::next(it)); tracker.insert(*it); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); @@ -151,10 +187,10 @@ void insert_tests2(Container* = 0) std::cerr<<"insert range tests.\n"; { - Container x; + X x; const_iterator pos = x.begin(); - test::random_values v(1000); + test::random_values v(1000); x.insert(v.begin(), v.end()); check_container(x, v); @@ -162,6 +198,31 @@ void insert_tests2(Container* = 0) } } +template +void map_tests(X* = 0) +{ + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + x[it->first] = it->second; + tracker[it->first] = it->second; + + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); +} + int main() { unique_insert_tests1((boost::unordered_set*) 0); @@ -184,5 +245,8 @@ int main() insert_tests2((boost::unordered_map >*) 0); insert_tests2((boost::unordered_multimap >*) 0); + map_tests((boost::unordered_map*) 0); + map_tests((boost::unordered_map >*) 0); + return boost::report_errors(); } diff --git a/test/unordered/load_factor_tests.cpp b/test/unordered/load_factor_tests.cpp new file mode 100644 index 00000000..973c8abf --- /dev/null +++ b/test/unordered/load_factor_tests.cpp @@ -0,0 +1,72 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include +#include "../helpers/random_values.hpp" + +template +void load_factor_tests(X* = 0) +{ + X x; + + BOOST_TEST(x.max_load_factor() == 1.0); + BOOST_TEST(x.load_factor() == 0); + + // A valid implementation could fail these tests, but I think they're + // reasonable. + x.max_load_factor(2.0); BOOST_TEST(x.max_load_factor() == 2.0); + x.max_load_factor(0.5); BOOST_TEST(x.max_load_factor() == 0.5); +} + +template +void insert_test(X*, float mlf) +{ + X x; + x.max_load_factor(mlf); + float b = x.max_load_factor(); + + test::random_values values(1000); + + for(typename test::random_values::const_iterator + it = values.begin(), end = values.end(); it != end; ++it) + { + typename X::size_type old_size = x.size(), + old_bucket_count = x.bucket_count(); + x.insert(*it); + if(old_size + 1 < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } +} + +template +void load_factor_insert_tests(X* ptr = 0) +{ + insert_test(ptr, 1.0); + insert_test(ptr, 0.1); + insert_test(ptr, 100); + + insert_test(ptr, (std::numeric_limits::min)()); + + if(std::numeric_limits::has_infinity) + insert_test(ptr, std::numeric_limits::infinity()); +} + +int main() +{ + load_factor_tests((boost::unordered_set*) 0); + load_factor_tests((boost::unordered_multiset*) 0); + load_factor_tests((boost::unordered_map*) 0); + load_factor_tests((boost::unordered_multimap*) 0); + + load_factor_insert_tests((boost::unordered_set*) 0); + load_factor_insert_tests((boost::unordered_multiset*) 0); + load_factor_insert_tests((boost::unordered_map*) 0); + load_factor_insert_tests((boost::unordered_multimap*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp new file mode 100644 index 00000000..9892be90 --- /dev/null +++ b/test/unordered/rehash_tests.cpp @@ -0,0 +1,67 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" + +template +bool postcondition(X const& x, typename X::size_type n) +{ + return x.bucket_count() > x.size() / x.max_load_factor() && x.bucket_count() >= n; +} + +template +void rehash_empty_test1(X* = 0) +{ + X x; + + x.rehash(10000); + BOOST_TEST(postcondition(x, 10000)); + + x.rehash(0); + BOOST_TEST(postcondition(x, 0)); +} + +template +void rehash_test1(X* = 0) +{ + test::random_values v(1000); + test::ordered tracker; + tracker.insert(v.begin(), v.end()); + X x(v.begin(), v.end()); + + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.max_load_factor(0.25); + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.max_load_factor(50.0); + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.rehash(1000); BOOST_TEST(postcondition(x, 1000)); + tracker.compare(x); +} + +template +void rehash_tests(X* ptr = 0) +{ + rehash_empty_test1(ptr); + rehash_test1(ptr); +} + +int main() { + rehash_tests((boost::unordered_set*) 0); + rehash_tests((boost::unordered_multiset*) 0); + rehash_tests((boost::unordered_map*) 0); + rehash_tests((boost::unordered_multimap*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp new file mode 100644 index 00000000..7b074283 --- /dev/null +++ b/test/unordered/swap_tests.cpp @@ -0,0 +1,135 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/invariants.hpp" + +template +void swap_test_impl(X& x1, X& x2) +{ + test::ordered tracker1 = test::create_ordered(x1); + test::ordered tracker2 = test::create_ordered(x2); + tracker1.insert(x1.begin(), x1.end()); + tracker2.insert(x2.begin(), x2.end()); + x1.swap(x2); + tracker1.compare(x2); + tracker2.compare(x1); +} + +template +void swap_tests1(X* = 0) +{ + { + X x; + swap_test_impl(x, x); + } + + { + X x,y; + swap_test_impl(x, y); + } + + { + test::random_values v(1000); + X x, y(v.begin(), v.end()); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end()), y(vy.begin(), vy.end()); + swap_test_impl(x, y); + swap_test_impl(x, y); + } +} + +template +void swap_tests2(X* ptr = 0) +{ + swap_tests1(ptr); + + typedef typename X::hasher hasher; + typedef typename X::key_equal key_equal; + typedef typename X::allocator_type allocator_type; + + { + X x(0, hasher(1), key_equal(1)); + X y(0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + } + + { + test::random_values v(1000); + X x(v.begin(), v.end(), 0, hasher(1), key_equal(1)); + X y(0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1)); + X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + +#if BOOST_UNORDERED_SWAP_METHOD == 1 + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); + try { + swap_test_impl(x, y); + BOOST_ERROR("Using swap method 1, swapping with unequal allocators didn't throw."); + } catch (std::runtime_error) {} + } +#else + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2), allocator_type(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } +#endif +} + +int main() +{ + std::cerr<<"Erase unordered_set.\n"; + swap_tests1((boost::unordered_set*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + swap_tests1((boost::unordered_multiset*) 0); + std::cerr<<"\nErase unordered_map.\n"; + swap_tests1((boost::unordered_map*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + swap_tests1((boost::unordered_multimap*) 0); + + std::cerr<<"\nErase unordered_set.\n"; + swap_tests2((boost::unordered_set >*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + swap_tests2((boost::unordered_multiset >*) 0); + std::cerr<<"\nErase unordered_map.\n"; + swap_tests2((boost::unordered_map >*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + swap_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} From 653a58f39bf07f632a272fb43466eebf079765d6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 11 Jun 2006 19:34:30 +0000 Subject: [PATCH 036/175] Improve insert. [SVN r2979] --- .../unordered/detail/hash_table_impl.hpp | 110 +++++++++++------- 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 7d52c9f6..8388165d 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1339,6 +1339,13 @@ namespace boost { } // no throw + // + // TODO: Is this a good value? The reason why I've set it to this + // as it's the largest value that all the functions can be + // implemented for (rehash's post conditions are impossible for + // larger sizes). But this can be significantly more that the + // allocator's max_size. Also, I should probably check again + // size_type's maximum value. size_type max_size() const { // size < mlf_ * count @@ -1546,7 +1553,6 @@ namespace boost { bucket_ptr dst_bucket = dst.buckets_ + dst.index_from_hash(hf(extract_key(*it))); // throws, strong - // dst.create_node(*it, dst_bucket); dst.copy_group(it, dst_bucket); } } @@ -1561,39 +1567,6 @@ namespace boost { #if BOOST_UNORDERED_HASH_EQUIVALENT - private: - - // Insert node without checking if a resize is necessary. - // (equivalent key containers) - - // strong exception safety. - iterator_base unchecked_insert(value_type const& v) - { - key_type const& k = extract_key(v); - bucket_ptr bucket = get_bucket(k); - local_iterator_base position = find_iterator(bucket, k); - - // No effects until here, this is strong. - return this->create_node(v, bucket, position); - } - - // strong exception safety - iterator_base unchecked_insert(iterator_base const& it, - value_type const& v) - { - // The condition can throw, but no side effects. - if(it != this->end() && equal(extract_key(v), *it)) { - // Strong exception safety: - return this->create_node(v, it); - } - else { - // Strong exception safety: - return unchecked_insert(v); - } - } - - public: - // Insert (equivalent key containers) // if hash function throws, basic exception safety @@ -1665,6 +1638,7 @@ namespace boost { // Insert from iterator range (equivalent key containers) private: + // if hash function throws, or inserting > 1 element, basic exception safety // strong otherwise template @@ -1677,7 +1651,14 @@ namespace boost { else { // Only require basic exception safety here reserve(size() + distance); - for (; i != j; ++i) unchecked_insert(*i); + for (; i != j; ++i) { + key_type const& k = extract_key(*i); + bucket_ptr bucket = get_bucket(k); + local_iterator_base position = find_iterator(bucket, k); + + // No effects until here, this is strong. + this->create_node(*i, bucket, position); + } } } @@ -1712,7 +1693,8 @@ namespace boost { !boost::is_same::value)); typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type; - bucket_ptr bucket = get_bucket(k); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->buckets_ + this->index_from_hash(hash_value); local_iterator_base pos = find_iterator(bucket, k); if (pos.not_finished()) @@ -1728,11 +1710,8 @@ namespace boost { // reserve has basic exception safety if the hash function // throws, strong otherwise. - if (reserve(size() + 1)) { - // get_bucket can only throw if the hash function throws (in - // which case basic exception safety is okay). - bucket = get_bucket(k); - } + if (reserve(size() + 1)) + bucket = this->buckets_ + this->index_from_hash(hash_value); // Nothing after this point can throw. @@ -1752,8 +1731,7 @@ namespace boost { // No side effects in this initial code key_type const& k = extract_key(v); size_type hash_value = hash_function()(k); - bucket_ptr bucket = this->buckets_ - + this->index_from_hash(hash_value); + bucket_ptr bucket = this->buckets_ + this->index_from_hash(hash_value); local_iterator_base pos = find_iterator(bucket, k); if (pos.not_finished()) { @@ -1799,6 +1777,18 @@ namespace boost { // Insert from iterators (unique keys) + template + std::size_t insert_size(I i, I j, boost::forward_traversal_tag) + { + return std::distance(i, j); + } + + template + std::size_t insert_size(I i, I j, boost::incrementable_traversal_tag) + { + return 1; + } + // if hash function throws, or inserting > 1 element, basic exception safety // strong otherwise template @@ -1806,10 +1796,40 @@ namespace boost { { // If only inserting 1 element, get the required // safety since insert is only called once. - for (; i != j; ++i) insert(*i); + for (; i != j; ++i) { + // No side effects in this initial code + key_type const& k = extract_key(*i); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->buckets_ + + this->index_from_hash(hash_value); + local_iterator_base pos = find_iterator(bucket, k); + + if (!pos.not_finished()) { + // Doesn't already exist, add to bucket. + // Side effects only in this block. + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->node_alloc_, this->bucket_alloc_); + value_type const& v(*i); + a.construct(v); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(size() + 1 >= max_load_) { + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type + iterator_traversal_tag; + + reserve(size() + insert_size(i, j, iterator_traversal_tag)); + bucket = this->buckets_ + this->index_from_hash(hash_value); + } + + // Nothing after this point can throw. + this->link_node(a.release(), bucket); + } + } } #endif - public: // erase From 58dda1527328831a40f0d34769ff1fbdad2b6dcc Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 11 Jun 2006 19:42:55 +0000 Subject: [PATCH 037/175] Improved support for testing on older versions of gcc. Link tests. Plus more... [SVN r2980] --- doc/intro.qbk | 3 +- test/container/Jamfile.v2 | 1 + test/container/link_test_1.cpp | 22 +++++ test/container/link_test_2.cpp | 18 ++++ test/exception/containers.hpp | 10 +- test/helpers/equivalent.hpp | 4 +- test/helpers/fwd.hpp | 2 + test/helpers/generators.hpp | 12 ++- test/helpers/invariants.hpp | 2 +- test/helpers/tracker.hpp | 7 +- test/objects/test.hpp | 164 +++++++++++++++++++++++++++++++-- 11 files changed, 222 insertions(+), 23 deletions(-) create mode 100644 test/container/link_test_1.cpp create mode 100644 test/container/link_test_2.cpp diff --git a/doc/intro.qbk b/doc/intro.qbk index 25f1f392..b5919847 100644 --- a/doc/intro.qbk +++ b/doc/intro.qbk @@ -1,4 +1,5 @@ -[def __tr1__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf +[def __tr1__ + [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf C++ Standard Library Technical Report]] [def __draft__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf diff --git a/test/container/Jamfile.v2 b/test/container/Jamfile.v2 index 6ec50a34..7427bb56 100644 --- a/test/container/Jamfile.v2 +++ b/test/container/Jamfile.v2 @@ -15,4 +15,5 @@ test-suite container-tests [ run set_compile.cpp ] [ run map_compile.cpp ] [ run simple_tests.cpp ] + [ run link_test_1.cpp link_test_2.cpp ] ; diff --git a/test/container/link_test_1.cpp b/test/container/link_test_1.cpp new file mode 100644 index 00000000..82e1994a --- /dev/null +++ b/test/container/link_test_1.cpp @@ -0,0 +1,22 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include + +void foo(boost::unordered_set&, + boost::unordered_map&, + boost::unordered_multiset&, + boost::unordered_multimap&); + +int main() +{ + boost::unordered_set x1; + boost::unordered_map x2; + boost::unordered_multiset x3; + boost::unordered_multimap x4; + + foo(x1, x2, x3, x4); +} diff --git a/test/container/link_test_2.cpp b/test/container/link_test_2.cpp new file mode 100644 index 00000000..2a419886 --- /dev/null +++ b/test/container/link_test_2.cpp @@ -0,0 +1,18 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// 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) + +#include +#include + +void foo(boost::unordered_set& x1, + boost::unordered_map& x2, + boost::unordered_multiset& x3, + boost::unordered_multimap& x4) +{ + x1.insert(1); + x2[2] = 2; + x3.insert(3); + x4.insert(std::make_pair(4, 5)); +} diff --git a/test/exception/containers.hpp b/test/exception/containers.hpp index 62132eb1..475f34f1 100644 --- a/test/exception/containers.hpp +++ b/test/exception/containers.hpp @@ -6,23 +6,23 @@ typedef boost::unordered_set< test::exception::object, test::exception::hash, test::exception::equal_to, - test::exception::allocator > set; + test::exception::allocator > test_set; typedef boost::unordered_multiset< test::exception::object, test::exception::hash, test::exception::equal_to, - test::exception::allocator > multiset; + test::exception::allocator > test_multiset; typedef boost::unordered_map< test::exception::object, test::exception::object, test::exception::hash, test::exception::equal_to, - test::exception::allocator > map; + test::exception::allocator > test_map; typedef boost::unordered_multimap< test::exception::object, test::exception::object, test::exception::hash, test::exception::equal_to, - test::exception::allocator > multimap; + test::exception::allocator > test_multimap; -#define CONTAINER_SEQ (set)(multiset)(map)(multimap) +#define CONTAINER_SEQ (test_set)(test_multiset)(test_map)(test_multimap) diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp index 9b4e6c8e..cd9033b2 100644 --- a/test/helpers/equivalent.hpp +++ b/test/helpers/equivalent.hpp @@ -14,8 +14,8 @@ namespace test { - template - bool equivalent_impl(T const& x, T const& y) { + template + bool equivalent_impl(T1 const& x, T2 const& y) { return x == y; } diff --git a/test/helpers/fwd.hpp b/test/helpers/fwd.hpp index 77032bf9..99c84721 100644 --- a/test/helpers/fwd.hpp +++ b/test/helpers/fwd.hpp @@ -6,6 +6,8 @@ #if !defined(BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER) #define BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER +#include + namespace test { int generate(int const*); diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp index 3ed8cf1b..6e2c1af7 100644 --- a/test/helpers/generators.hpp +++ b/test/helpers/generators.hpp @@ -36,15 +36,19 @@ namespace test inline int generate(int const*) { + integer_generator_type gen; + boost::uniform_int<> dist(0, 1000); static boost::variate_generator > - vg((integer_generator_type()), boost::uniform_int<>(0, 1000)); + vg(gen, dist); return vg(); } inline char generate(char const*) { + integer_generator_type gen; + boost::uniform_int dist(32, 128); static boost::variate_generator > - vg((integer_generator_type()), boost::uniform_int(32, 128)); + vg(gen, dist); return vg(); } @@ -70,8 +74,10 @@ namespace test float generate(float const*) { + real_generator_type gen; + boost::uniform_real dist; static boost::variate_generator > - vg((real_generator_type()), boost::uniform_real()); + vg(gen, dist); return vg(); } diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index 38b8148e..20afe3d5 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -80,7 +80,7 @@ namespace test // Finally, check that size matches up. if(x1.size() != size) BOOST_ERROR("x1.size() doesn't match actual size."); - if(static_cast(size) / x1.bucket_count() != x1.load_factor()) + if(static_cast(size) / static_cast(x1.bucket_count()) != x1.load_factor()) BOOST_ERROR("x1.load_factor() doesn't match actual load_factor."); } } diff --git a/test/helpers/tracker.hpp b/test/helpers/tracker.hpp index d64bd92d..a70fffcb 100644 --- a/test/helpers/tracker.hpp +++ b/test/helpers/tracker.hpp @@ -19,6 +19,7 @@ #include "../objects/fwd.hpp" #include "./metafunctions.hpp" #include "./helpers.hpp" +#include "./equivalent.hpp" namespace test { @@ -45,7 +46,8 @@ namespace test std::copy(x2.begin(), x2.end(), std::back_inserter(values2)); std::sort(values1.begin(), values1.end()); std::sort(values2.begin(), values2.end()); - BOOST_TEST(values1 == values2); + BOOST_TEST(values1.size() == values2.size() && + std::equal(values1.begin(), values1.end(), values2.begin(), test::equivalent)); } template @@ -58,7 +60,8 @@ namespace test std::copy(x2.first, x2.second, std::back_inserter(values2)); std::sort(values1.begin(), values1.end()); std::sort(values2.begin(), values2.end()); - BOOST_TEST(values1 == values2); + BOOST_TEST(values1.size() == values2.size() && + std::equal(values1.begin(), values1.end(), values2.begin(), test::equivalent)); } template diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 15114033..4b55fa10 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -6,9 +6,12 @@ #if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) #define BOOST_UNORDERED_TEST_OBJECTS_HEADER +#include +#include #include #include "../helpers/fwd.hpp" -#include +#include +#include namespace test { @@ -144,6 +147,136 @@ namespace test } }; + namespace detail + { + namespace { + struct memory_area { + void const* start; + void const* end; + + memory_area(void const* s, void const* e) + : start(s), end(e) + { + } + + // This is a bit dodgy as it defines overlapping + // areas as 'equal', so this isn't a total ordering. + // But it is for non-overlapping memory regions - which + // is what'll be stored. + // + // All searches will be for areas entirely contained by + // a member of the set - so it should find the area that contains + // the region that is searched for. + bool operator<(memory_area const& other) const { + return end < other.start; + } + }; + + struct memory_track { + explicit memory_track(int tag = -1) : + constructed_(0), + tag_(tag) {} + + int constructed_; + int tag_; + }; + + std::map allocated_memory; + unsigned int count_allocators = 0; + unsigned int count_allocations = 0; + unsigned int count_constructions = 0; + } + + void allocator_ref() + { + if(count_allocators == 0) { + count_allocations = 0; + count_constructions = 0; + allocated_memory.clear(); + } + ++count_allocators; + } + + void allocator_unref() + { + BOOST_TEST(count_allocators > 0); + if(count_allocators > 0) { + --count_allocators; + if(count_allocators == 0) { + bool no_allocations_left = (count_allocations == 0); + bool no_constructions_left = (count_constructions == 0); + bool allocated_memory_empty = allocated_memory.empty(); + + // Clearing the data before the checks terminate the tests. + count_allocations = 0; + count_constructions = 0; + allocated_memory.clear(); + + BOOST_TEST(no_allocations_left); + BOOST_TEST(no_constructions_left); + BOOST_TEST(allocated_memory_empty); + } + } + } + + void track_allocate(void *ptr, std::size_t n, std::size_t size, int tag) + { + if(n == 0) { + // TODO: This is unspecified - not undefined, so what to do? + } + else { + ++count_allocations; + allocated_memory[memory_area(ptr, (char*) ptr + n * size)] = + memory_track(tag); + } + } + + void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag) + { + std::map::iterator pos + = allocated_memory.find(memory_area(ptr, ptr)); + if(pos == allocated_memory.end()) { + BOOST_ERROR("Deallocating unknown pointer."); + } else { + // TODO: Not exception safe. + BOOST_TEST(pos->first.start == ptr); + BOOST_TEST(pos->first.end == (char*) ptr + n * size); + BOOST_TEST(pos->second.tag_ == tag); + BOOST_TEST(pos->second.constructed_ == 0); + allocated_memory.erase(pos); + } + BOOST_TEST(count_allocations > 0); + if(count_allocations > 0) --count_allocations; + } + + void track_construct(void* ptr, std::size_t size, int tag) + { + std::map::iterator pos + = allocated_memory.find(memory_area(ptr, ptr)); + if(pos == allocated_memory.end()) + BOOST_ERROR("Constructing unknown pointer."); + BOOST_TEST(pos->second.tag_ == tag); + //TODO: Track the number of allocations, and make sure the number + // of constructions doesn't exceed it. If you're feeling keen, + // perhaps track the individual objects in the array. + ++count_constructions; + ++pos->second.constructed_; + } + + void track_destroy(void* ptr, std::size_t size, int tag) + { + std::map::iterator pos + = allocated_memory.find(memory_area(ptr, ptr)); + if(pos == allocated_memory.end()) + BOOST_ERROR("Destroying unknown pointer."); + BOOST_TEST(count_constructions > 0); + BOOST_TEST(pos->second.tag_ == tag); + BOOST_TEST(pos->second.constructed_ > 0); + if(count_constructions > 0) --count_constructions; + if(pos->second.constructed_ > 0) --pos->second.constructed_; + } + } + template class allocator { @@ -164,30 +297,43 @@ namespace test template struct rebind { typedef allocator other; }; - explicit allocator(int t = 0) : tag_(t) {} - template allocator(allocator const& x) : tag_(x.tag_) {} - allocator(allocator const& x) : tag_(x.tag_) {} - ~allocator() {} + explicit allocator(int t = 0) : tag_(t) { detail::allocator_ref(); } + template allocator(allocator const& x) : tag_(x.tag_) { detail::allocator_ref(); } + allocator(allocator const& x) : tag_(x.tag_) { detail::allocator_ref(); } + ~allocator() { detail::allocator_unref(); } + // TODO: Shall I check these? pointer address(reference r) { return pointer(&r); } const_pointer address(const_reference r) { return const_pointer(&r); } pointer allocate(size_type n) { - return pointer(static_cast(::operator new(n * sizeof(T)))); + pointer ptr(static_cast(::operator new(n * sizeof(T)))); + detail::track_allocate((void*) ptr, n, sizeof(T), tag_); + return ptr; } pointer allocate(size_type n, const_pointer u) { - return pointer(static_cast(::operator new(n * sizeof(T)))); + pointer ptr(static_cast(::operator new(n * sizeof(T)))); + detail::track_allocate((void*) ptr, n, sizeof(T), tag_); + return ptr; } void deallocate(pointer p, size_type n) { + detail::track_deallocate((void*) p, n, sizeof(T), tag_); ::operator delete((void*) p); } - void construct(pointer p, T const& t) { new(p) T(t); } - void destroy(pointer p) { p->~T(); } + void construct(pointer p, T const& t) { + detail::track_construct((void*) p, sizeof(T), tag_); + new(p) T(t); + } + + void destroy(pointer p) { + detail::track_destroy((void*) p, sizeof(T), tag_); + p->~T(); + } size_type max_size() const { return (std::numeric_limits::max)(); From ff91c72eec257de4725392388a144ddf95c2ec42 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 12 Jun 2006 23:30:46 +0000 Subject: [PATCH 038/175] Improved support for Visual C++. [SVN r2985] --- include/boost/unordered/detail/hash_table.hpp | 4 +-- .../unordered/detail/hash_table_impl.hpp | 2 +- test/container/link_test_1.cpp | 2 ++ test/container/simple_tests.cpp | 2 +- test/helpers/equivalent.hpp | 21 ++++++------ test/helpers/generators.hpp | 23 ++++++++++++- test/helpers/helpers.hpp | 31 ++++++++++++------ test/helpers/invariants.hpp | 5 ++- test/helpers/tracker.hpp | 31 +++++++++++++----- test/objects/test.hpp | 20 +++++++++--- test/unordered/assign_tests.cpp | 6 ++-- test/unordered/bucket_tests.cpp | 2 +- test/unordered/compile_tests.cpp | 2 +- test/unordered/constructor_tests.cpp | 26 +++++++-------- test/unordered/find_tests.cpp | 32 ++++++++++--------- test/unordered/insert_tests.cpp | 11 ++++--- test/unordered/load_factor_tests.cpp | 6 ++-- test/unordered/rehash_tests.cpp | 2 +- test/unordered/swap_tests.cpp | 7 ++-- 19 files changed, 154 insertions(+), 81 deletions(-) diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 59c7f072..5a2423d5 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -51,7 +51,7 @@ #endif #if BOOST_WORKAROUND(BOOST_MSVC, < 1300) -#define BOOST_HASH_MSVC_RESET_PTR(x) unordered_detail::reset(node_pointer_) +#define BOOST_HASH_MSVC_RESET_PTR(x) unordered_detail::reset(x) #else #define BOOST_HASH_MSVC_RESET_PTR(x) #endif @@ -61,7 +61,7 @@ namespace boost { template struct type_wrapper {}; const static std::size_t default_initial_bucket_count = 50; - const static float minimum_max_load_factor = 1e-3; + const static float minimum_max_load_factor = 1e-3f; inline std::size_t next_prime(std::size_t n); template diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 8388165d..4eee2693 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1811,7 +1811,7 @@ namespace boost { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). node_constructor a(this->node_alloc_, this->bucket_alloc_); - value_type const& v(*i); + value_type const& v = *i; a.construct(v); // reserve has basic exception safety if the hash function diff --git a/test/container/link_test_1.cpp b/test/container/link_test_1.cpp index 82e1994a..73efe2b4 100644 --- a/test/container/link_test_1.cpp +++ b/test/container/link_test_1.cpp @@ -19,4 +19,6 @@ int main() boost::unordered_multimap x4; foo(x1, x2, x3, x4); + + return 0; } diff --git a/test/container/simple_tests.cpp b/test/container/simple_tests.cpp index 94c9f79a..c4a7bbd0 100644 --- a/test/container/simple_tests.cpp +++ b/test/container/simple_tests.cpp @@ -58,7 +58,7 @@ void simple_test(X const& a) { X u; - X& r(u); + X& r = u; // TODO: I can't actually see a requirement for that assignment // returns a reference to itself (just that it returns a reference). BOOST_TEST(&(r = r) == &r); diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp index cd9033b2..924c79f6 100644 --- a/test/helpers/equivalent.hpp +++ b/test/helpers/equivalent.hpp @@ -14,32 +14,35 @@ namespace test { + struct base_type {} base; + struct derived_type : base_type {} derived; + template - bool equivalent_impl(T1 const& x, T2 const& y) { + bool equivalent_impl(T1 const& x, T2 const& y, base_type) { return x == y; } template - bool equivalent_impl(boost::hash const&, boost::hash const&) { + bool equivalent_impl(boost::hash const&, boost::hash const&, derived_type) { return true; } template - bool equivalent_impl(std::equal_to const&, std::equal_to const&) { + bool equivalent_impl(std::equal_to const&, std::equal_to const&, derived_type) { return true; } template bool equivalent_impl(std::pair const& x1, - std::pair const& x2) { - return equivalent_impl(x1.first, x2.first) && - equivalent_impl(x1.second, x2.second); + std::pair const& x2, derived_type) { + return equivalent_impl(x1.first, x2.first, derived) && + equivalent_impl(x1.second, x2.second, derived); } struct equivalent_type { template bool operator()(T1 const& x, T2 const& y) { - return equivalent_impl(x, y); + return equivalent_impl(x, y, derived); } }; @@ -74,8 +77,8 @@ namespace test bool operator()(Container const& x) const { if(!((size_ == x.size()) && - (test::equivalent_impl(hasher_, x.hash_function())) && - (test::equivalent_impl(key_equal_, x.key_eq())) && + (test::equivalent(hasher_, x.hash_function())) && + (test::equivalent(key_equal_, x.key_eq())) && (max_load_factor_ == x.max_load_factor()) && (values_.size() == x.size()))) return false; diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp index 6e2c1af7..93c93950 100644 --- a/test/helpers/generators.hpp +++ b/test/helpers/generators.hpp @@ -11,18 +11,24 @@ #include #include +#if 0 #include #include #include #include #include +#else +#include +#endif #include "./fwd.hpp" namespace test { +#if 0 typedef boost::hellekalek1995 integer_generator_type; typedef boost::lagged_fibonacci607 real_generator_type; +#endif template struct generator @@ -36,20 +42,30 @@ namespace test inline int generate(int const*) { +#if 0 integer_generator_type gen; boost::uniform_int<> dist(0, 1000); static boost::variate_generator > vg(gen, dist); return vg(); +#else + using namespace std; + return rand(); +#endif } inline char generate(char const*) { +#if 0 integer_generator_type gen; - boost::uniform_int dist(32, 128); + boost::uniform_int dist(32, 127); static boost::variate_generator > vg(gen, dist); return vg(); +#else + using namespace std; + return rand() % (128 - 32) + 32; +#endif } inline std::string generate(std::string const*) @@ -74,11 +90,16 @@ namespace test float generate(float const*) { +#if 0 real_generator_type gen; boost::uniform_real dist; static boost::variate_generator > vg(gen, dist); return vg(); +#else + using namespace std; + return (double) rand() / (double) RAND_MAX; +#endif } template std::pair generate( diff --git a/test/helpers/helpers.hpp b/test/helpers/helpers.hpp index fa219cda..875a78a2 100644 --- a/test/helpers/helpers.hpp +++ b/test/helpers/helpers.hpp @@ -9,21 +9,32 @@ namespace test { template - inline typename Container::key_type get_key(typename Container::key_type const& x) + struct get_key_impl { - return x; - } + typedef typename Container::key_type key_type; - template - inline typename Container::key_type get_key(std::pair const& x) - { - return x.first; - } + static key_type get_key(key_type const& x) + { + return x; + } + template + static key_type get_key(std::pair const& x, char = 0) + { + return x.first; + } + + template + static key_type get_key(std::pair const& x, unsigned char = 0) + { + return x.first; + } + }; + template - inline typename Container::key_type get_key(std::pair const& x) + inline typename Container::key_type get_key(T const& x) { - return x.first; + return get_key_impl::get_key(x); } } diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index 20afe3d5..b6c6842c 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -10,6 +10,7 @@ #define BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER #include +#include #include "./metafunctions.hpp" #include "./helpers.hpp" @@ -80,7 +81,9 @@ namespace test // Finally, check that size matches up. if(x1.size() != size) BOOST_ERROR("x1.size() doesn't match actual size."); - if(static_cast(size) / static_cast(x1.bucket_count()) != x1.load_factor()) + float load_factor = static_cast(size) / static_cast(x1.bucket_count()); + using namespace std; + if(fabs(x1.load_factor() - load_factor) > x1.load_factor() / 64) BOOST_ERROR("x1.load_factor() doesn't match actual load_factor."); } } diff --git a/test/helpers/tracker.hpp b/test/helpers/tracker.hpp index a70fffcb..ba7376e5 100644 --- a/test/helpers/tracker.hpp +++ b/test/helpers/tracker.hpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "../objects/fwd.hpp" #include "./metafunctions.hpp" #include "./helpers.hpp" @@ -24,15 +26,19 @@ namespace test { template - struct equals_to_compare + struct equals_to_compare2 + : public boost::mpl::identity > { - typedef std::less type; }; - template <> - struct equals_to_compare + template + struct equals_to_compare + : public boost::mpl::eval_if< + boost::is_same, + boost::mpl::identity, + equals_to_compare2 + > { - typedef test::less type; }; template @@ -121,13 +127,22 @@ namespace test (typename non_const_value_type::type*) 0 ); } + + template + void insert_range(It begin, It end) { + while(begin != end) { + this->insert(*begin); + ++begin; + } + } }; template typename equals_to_compare::type create_compare( - Equals equals) + Equals const& equals) { - return typename equals_to_compare::type(); + typename equals_to_compare::type x; + return x; } template @@ -140,7 +155,7 @@ namespace test void check_container(X1 const& container, X2 const& values) { ordered tracker = create_ordered(container); - tracker.insert(values.begin(), values.end()); + tracker.insert_range(values.begin(), values.end()); tracker.compare(container); } } diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 4b55fa10..6fda7bbb 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -47,7 +47,7 @@ namespace test friend object generate(object const*) { - int* x; + int* x = 0; return object(generate(x), generate(x)); } @@ -339,10 +339,7 @@ namespace test return (std::numeric_limits::max)(); } - friend bool operator==(allocator const& x, allocator const& y) - { - return x.tag_ == y.tag_; - } + friend bool operator==(allocator const& x, allocator const& y); friend bool operator!=(allocator const& x, allocator const& y) { @@ -351,4 +348,17 @@ namespace test }; } +#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +namespace test +{ +#endif + template + bool operator==(test::allocator const& x, test::allocator const& y) + { + return x.tag_ == y.tag_; + } +#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +} +#endif + #endif diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index b7053b97..3915b81c 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -34,7 +34,7 @@ void assign_tests1(T* = 0) T x(v.begin(), v.end()); test::ordered tracker = test::create_ordered(x); - tracker.insert(v.begin(), v.end()); + tracker.insert_range(v.begin(), v.end()); x = x; tracker.compare(x); @@ -68,7 +68,7 @@ void assign_tests2(T* = 0) x2 = x1; BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); - check_container(x2, v); + test::check_container(x2, v); } std::cerr<<"assign_tests2.2\n"; @@ -81,7 +81,7 @@ void assign_tests2(T* = 0) BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); BOOST_TEST(test::equivalent(x2.get_allocator(), al2)); - check_container(x2, v1); + test::check_container(x2, v1); } } diff --git a/test/unordered/bucket_tests.cpp b/test/unordered/bucket_tests.cpp index b4b78041..ba42769d 100644 --- a/test/unordered/bucket_tests.cpp +++ b/test/unordered/bucket_tests.cpp @@ -39,7 +39,7 @@ void bucket_tests(X* = 0) for(size_type i = 0; i < x.bucket_count(); ++i) { BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x.begin(i), x.end(i))); - X const& x_ref(x); + X const& x_ref = x; BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x_ref.begin(i), x_ref.end(i))); } } diff --git a/test/unordered/compile_tests.cpp b/test/unordered/compile_tests.cpp index ccc90b0c..b9e9c3ad 100644 --- a/test/unordered/compile_tests.cpp +++ b/test/unordered/compile_tests.cpp @@ -106,7 +106,7 @@ void unordered_test(X& ref, Key& k, T& t, Hash& hf, Pred& eq) X(10); X a3(10); X(); - X a4(); + X a4; typename X::value_type* i = 0; typename X::value_type* j = 0; diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index c8d58dd3..082d4d5f 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -67,7 +67,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); - check_container(x, v); + test::check_container(x, v); } std::cerr<<"Construct 6\n"; @@ -78,7 +78,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); - check_container(x, v); + test::check_container(x, v); } std::cerr<<"Construct 7\n"; @@ -89,7 +89,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); - check_container(x, v); + test::check_container(x, v); } std::cerr<<"Construct 8\n"; @@ -99,7 +99,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); - check_container(x, v); + test::check_container(x, v); } std::cerr<<"Construct 9\n"; @@ -120,7 +120,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); - check_container(x, v); + test::check_container(x, v); } } @@ -163,7 +163,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq1)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); - check_container(x, v); + test::check_container(x, v); } std::cerr<<"Construct 4\n"; @@ -174,7 +174,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); - check_container(x, v); + test::check_container(x, v); } @@ -183,8 +183,8 @@ void constructor_tests2(T* = 0) test::random_values v(100); T x(v.begin(), v.end(), 0, hf, eq, al1); T y(x.begin(), x.end(), 0, hf1, eq1, al2); - check_container(x, v); - check_container(y, x); + test::check_container(x, v); + test::check_container(y, x); } std::cerr<<"Construct 6\n"; @@ -192,8 +192,8 @@ void constructor_tests2(T* = 0) test::random_values v(100); T x(v.begin(), v.end(), 0, hf1, eq1); T y(x.begin(), x.end(), 0, hf, eq); - check_container(x, v); - check_container(y, x); + test::check_container(x, v); + test::check_container(y, x); } std::cerr<<"Construct 7\n"; @@ -201,8 +201,8 @@ void constructor_tests2(T* = 0) test::random_values v(100); T x(v.begin(), v.end(), 0, hf1, eq1); T y(x.begin(), x.end(), 0, hf2, eq2); - check_container(x, v); - check_container(y, x); + test::check_container(x, v); + test::check_container(y, x); } } diff --git a/test/unordered/find_tests.cpp b/test/unordered/find_tests.cpp index 89b53870..6b00435e 100644 --- a/test/unordered/find_tests.cpp +++ b/test/unordered/find_tests.cpp @@ -14,18 +14,20 @@ template void find_tests1(X*) { + typedef typename X::iterator iterator; + { test::random_values v(500); X x(v.begin(), v.end()); X const& x_const = x; test::ordered tracker = test::create_ordered(x); - tracker.insert(v.begin(), v.end()); + tracker.insert_range(v.begin(), v.end()); - for(typename test::ordered::const_iterator it = - tracker.begin(); it != tracker.end(); ++it) + for(typename test::ordered::const_iterator it1 = + tracker.begin(); it1 != tracker.end(); ++it1) { - typename X::key_type key = test::get_key(*it); - typename X::iterator pos = x.find(key); + typename X::key_type key = test::get_key(*it1); + iterator pos = x.find(key); typename X::const_iterator const_pos = x_const.find(key); BOOST_TEST(pos != x.end() && x.key_eq()(key, test::get_key(*pos))); @@ -43,17 +45,16 @@ void find_tests1(X*) } test::random_values v2(500); - for(typename test::random_values::const_iterator it = - v2.begin(); it != v2.end(); ++it) + for(typename test::random_values::const_iterator it2 = + v2.begin(); it2 != v2.end(); ++it2) { - typename X::key_type key = test::get_key(*it); + typename X::key_type key = test::get_key(*it2); if(tracker.find(test::get_key(key)) == tracker.end()) { BOOST_TEST(x.find(key) == x.end()); BOOST_TEST(x_const.find(key) == x_const.end()); BOOST_TEST(x.count(key) == 0); - std::pair range = x.equal_range(key); + std::pair range = x.equal_range(key); BOOST_TEST(range.first == range.second); } } @@ -63,14 +64,13 @@ void find_tests1(X*) X x; test::random_values v2(5); - for(typename test::random_values::const_iterator it = - v2.begin(); it != v2.end(); ++it) + for(typename test::random_values::const_iterator it3 = + v2.begin(); it3 != v2.end(); ++it3) { - typename X::key_type key = test::get_key(*it); + typename X::key_type key = test::get_key(*it3); BOOST_TEST(x.find(key) == x.end()); BOOST_TEST(x.count(key) == 0); - std::pair range = x.equal_range(key); + std::pair range = x.equal_range(key); BOOST_TEST(range.first == range.second); } } @@ -87,4 +87,6 @@ int main() find_tests1((boost::unordered_multiset >*) 0); find_tests1((boost::unordered_map >*) 0); find_tests1((boost::unordered_multimap >*) 0); + + return 0; } diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 1d3be884..20e60643 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -18,6 +18,10 @@ template void unique_insert_tests1(X* = 0) { + typedef typename X::iterator iterator; + typedef test::ordered ordered; + typedef typename test::ordered::iterator ordered_iterator; + std::cerr<<"insert(value) tests for containers with unique keys.\n"; X x; @@ -30,9 +34,8 @@ void unique_insert_tests1(X* = 0) typename X::size_type old_bucket_count = x.bucket_count(); float b = x.max_load_factor(); - std::pair r1 = x.insert(*it); - std::pair::iterator, bool> r2 - = tracker.insert(*it); + std::pair r1 = x.insert(*it); + std::pair r2 = tracker.insert(*it); BOOST_TEST(r1.second == r2.second); BOOST_TEST(*r1.first == *r2.first); @@ -192,7 +195,7 @@ void insert_tests2(X* = 0) test::random_values v(1000); x.insert(v.begin(), v.end()); - check_container(x, v); + test::check_container(x, v); test::check_equivalent_keys(x); } diff --git a/test/unordered/load_factor_tests.cpp b/test/unordered/load_factor_tests.cpp index 973c8abf..677b4659 100644 --- a/test/unordered/load_factor_tests.cpp +++ b/test/unordered/load_factor_tests.cpp @@ -46,9 +46,9 @@ void insert_test(X*, float mlf) template void load_factor_insert_tests(X* ptr = 0) { - insert_test(ptr, 1.0); - insert_test(ptr, 0.1); - insert_test(ptr, 100); + insert_test(ptr, 1.0f); + insert_test(ptr, 0.1f); + insert_test(ptr, 100.0f); insert_test(ptr, (std::numeric_limits::min)()); diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp index 9892be90..516e1cd7 100644 --- a/test/unordered/rehash_tests.cpp +++ b/test/unordered/rehash_tests.cpp @@ -32,7 +32,7 @@ void rehash_test1(X* = 0) { test::random_values v(1000); test::ordered tracker; - tracker.insert(v.begin(), v.end()); + tracker.insert_range(v.begin(), v.end()); X x(v.begin(), v.end()); x.rehash(0); BOOST_TEST(postcondition(x, 0)); diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp index 7b074283..f9dcddb3 100644 --- a/test/unordered/swap_tests.cpp +++ b/test/unordered/swap_tests.cpp @@ -3,6 +3,9 @@ // 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) +#include +#include +#include #include #include #include @@ -16,8 +19,8 @@ void swap_test_impl(X& x1, X& x2) { test::ordered tracker1 = test::create_ordered(x1); test::ordered tracker2 = test::create_ordered(x2); - tracker1.insert(x1.begin(), x1.end()); - tracker2.insert(x2.begin(), x2.end()); + tracker1.insert_range(x1.begin(), x1.end()); + tracker2.insert_range(x2.begin(), x2.end()); x1.swap(x2); tracker1.compare(x2); tracker2.compare(x1); From f9907e4a5505657a5627360e3b0cdf69ce431cec Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 18 Jun 2006 13:24:38 +0000 Subject: [PATCH 039/175] Get unordered working on Visual C++ 6.5 [SVN r2992] --- .../unordered/detail/hash_table_impl.hpp | 19 ++-- test/helpers/equivalent.hpp | 4 +- test/helpers/fwd.hpp | 3 + test/objects/test.hpp | 25 +++--- test/unordered/compile_tests.cpp | 86 ++++++++++--------- 5 files changed, 72 insertions(+), 65 deletions(-) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 4eee2693..61c1da8e 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1677,10 +1677,10 @@ namespace boost { // if hash function throws, or inserting > 1 element, basic exception safety // strong otherwise - template - void insert(InputIterator i, InputIterator j) + template + void insert(I i, I j) { - BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type iterator_traversal_tag; insert_for_range(i, j, iterator_traversal_tag); } @@ -1789,6 +1789,14 @@ namespace boost { return 1; } + template + std::size_t insert_size(I i, I j) + { + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type + iterator_traversal_tag; + return insert_size(i, j, iterator_traversal_tag); + }; + // if hash function throws, or inserting > 1 element, basic exception safety // strong otherwise template @@ -1817,10 +1825,7 @@ namespace boost { // reserve has basic exception safety if the hash function // throws, strong otherwise. if(size() + 1 >= max_load_) { - BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type - iterator_traversal_tag; - - reserve(size() + insert_size(i, j, iterator_traversal_tag)); + reserve(size() + insert_size(i, j)); bucket = this->buckets_ + this->index_from_hash(hash_value); } diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp index 924c79f6..1b9339aa 100644 --- a/test/helpers/equivalent.hpp +++ b/test/helpers/equivalent.hpp @@ -11,12 +11,10 @@ #include #include #include "./metafunctions.hpp" +#include "./fwd.hpp" namespace test { - struct base_type {} base; - struct derived_type : base_type {} derived; - template bool equivalent_impl(T1 const& x, T2 const& y, base_type) { return x == y; diff --git a/test/helpers/fwd.hpp b/test/helpers/fwd.hpp index 99c84721..95b4ef5a 100644 --- a/test/helpers/fwd.hpp +++ b/test/helpers/fwd.hpp @@ -16,6 +16,9 @@ namespace test float generate(float const*); template std::pair generate(std::pair*); + + struct base_type {} base; + struct derived_type : base_type {} derived; } #endif diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 6fda7bbb..6777a1d5 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -339,26 +339,21 @@ namespace test return (std::numeric_limits::max)(); } - friend bool operator==(allocator const& x, allocator const& y); - - friend bool operator!=(allocator const& x, allocator const& y) + bool operator==(allocator const& x) const { - return x.tag_ != y.tag_; + return tag_ == x.tag_; + } + + bool operator!=(allocator const& x) const + { + return tag_ != x.tag_; } }; -} - -#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) -namespace test -{ -#endif + template - bool operator==(test::allocator const& x, test::allocator const& y) - { - return x.tag_ == y.tag_; + bool equivalent_impl(allocator const& x, allocator const& y, test::derived_type) { + return x == y; } -#if !defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) } -#endif #endif diff --git a/test/unordered/compile_tests.cpp b/test/unordered/compile_tests.cpp index b9e9c3ad..f52324a9 100644 --- a/test/unordered/compile_tests.cpp +++ b/test/unordered/compile_tests.cpp @@ -20,17 +20,18 @@ template void sink(T const&) {} template void unordered_set_test(X& r, Key const& k) { - BOOST_MPL_ASSERT((boost::is_same< - typename X::value_type, - typename X::key_type>)); + typedef typename X::value_type value_type; + typedef typename X::key_type key_type; + + BOOST_MPL_ASSERT((boost::is_same)); } template void unordered_map_test(X& r, Key const& k, T const& t) { - BOOST_MPL_ASSERT((boost::is_same< - typename X::value_type, - std::pair >)); + typedef typename X::value_type value_type; + typedef typename X::key_type key_type; + BOOST_MPL_ASSERT((boost::is_same >)); } template @@ -50,14 +51,35 @@ void unordered_equivalent_test(X& r, T const& t) template void unordered_test(X& ref, Key& k, T& t, Hash& hf, Pred& eq) { + typedef typename X::key_type key_type; + typedef typename X::hasher hasher; + typedef typename X::key_equal key_equal; + typedef typename X::size_type size_type; + typedef typename X::iterator iterator; typedef typename X::const_iterator const_iterator; typedef typename X::local_iterator local_iterator; typedef typename X::const_local_iterator const_local_iterator; - - typedef typename X::key_type key_type; - typedef typename X::hasher hasher; - typedef typename X::key_equal key_equal; + + typedef typename boost::BOOST_ITERATOR_CATEGORY::type iterator_category; + typedef typename boost::iterator_difference::type iterator_difference; + typedef typename boost::iterator_pointer::type iterator_pointer; + typedef typename boost::iterator_reference::type iterator_reference; + + typedef typename boost::BOOST_ITERATOR_CATEGORY::type local_iterator_category; + typedef typename boost::iterator_difference::type local_iterator_difference; + typedef typename boost::iterator_pointer::type local_iterator_pointer; + typedef typename boost::iterator_reference::type local_iterator_reference; + + typedef typename boost::BOOST_ITERATOR_CATEGORY::type const_iterator_category; + typedef typename boost::iterator_difference::type const_iterator_difference; + typedef typename boost::iterator_pointer::type const_iterator_pointer; + typedef typename boost::iterator_reference::type const_iterator_reference; + + typedef typename boost::BOOST_ITERATOR_CATEGORY::type const_local_iterator_category; + typedef typename boost::iterator_difference::type const_local_iterator_difference; + typedef typename boost::iterator_pointer::type const_local_iterator_pointer; + typedef typename boost::iterator_reference::type const_local_iterator_reference; BOOST_MPL_ASSERT((boost::is_same)); boost::function_requires >(); @@ -72,32 +94,16 @@ void unordered_test(X& ref, Key& k, T& t, Hash& hf, Pred& eq) // tests. boost::function_requires >(); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::BOOST_ITERATOR_CATEGORY::type, - typename boost::BOOST_ITERATOR_CATEGORY::type >)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_difference::type, - typename boost::iterator_difference::type >)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_pointer::type, - typename boost::iterator_pointer::type >)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_reference::type, - typename boost::iterator_reference::type >)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); boost::function_requires >(); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::BOOST_ITERATOR_CATEGORY::type, - typename boost::BOOST_ITERATOR_CATEGORY::type >)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_difference::type, - typename boost::iterator_difference::type >)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_pointer::type, - typename boost::iterator_pointer::type >)); - BOOST_MPL_ASSERT((boost::is_same< - typename boost::iterator_reference::type, - typename boost::iterator_reference::type >)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); X(10, hf, eq); X a(10, hf, eq); @@ -135,7 +141,7 @@ void unordered_test(X& ref, Key& k, T& t, Hash& hf, Pred& eq) // TODO: void return? a.insert(i, j); - test::check_return_type::equals(a.erase(k)); + test::check_return_type::equals(a.erase(k)); BOOST_TEST(a.empty()); if(a.empty()) { @@ -162,15 +168,15 @@ void unordered_test(X& ref, Key& k, T& t, Hash& hf, Pred& eq) test::check_return_type::equals(a.find(k)); test::check_return_type::equals(b.find(k)); - test::check_return_type::equals(b.count(k)); + test::check_return_type::equals(b.count(k)); test::check_return_type >::equals( a.equal_range(k)); test::check_return_type >::equals( b.equal_range(k)); - test::check_return_type::equals(b.bucket_count()); - test::check_return_type::equals(b.max_bucket_count()); - test::check_return_type::equals(b.bucket(k)); - test::check_return_type::equals(b.bucket_size(0)); + test::check_return_type::equals(b.bucket_count()); + test::check_return_type::equals(b.max_bucket_count()); + test::check_return_type::equals(b.bucket(k)); + test::check_return_type::equals(b.bucket_size(0)); test::check_return_type::equals(a.begin(0)); test::check_return_type::equals(b.begin(0)); From dd2ea7222085c890621bbf58ea793959b7f3e46c Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 1 Jul 2006 22:31:26 +0000 Subject: [PATCH 040/175] Update the copyright in the unordered container tests. [SVN r3040] --- test/Jamfile.v2 | 6 +++--- test/container/Jamfile.v2 | 6 +++--- test/container/compile_tests.hpp | 6 +++--- test/container/link_test_1.cpp | 6 +++--- test/container/link_test_2.cpp | 6 +++--- test/container/map_compile.cpp | 6 +++--- test/container/set_compile.cpp | 6 +++--- test/container/simple_tests.cpp | 6 +++--- test/exception/Jamfile.v2 | 6 +++--- test/exception/assign_tests.cpp | 6 +++--- test/exception/constructor_tests.cpp | 6 +++--- test/exception/containers.hpp | 5 +++++ test/exception/copy_tests.cpp | 6 +++--- test/exception/erase_tests.cpp | 6 +++--- test/exception/insert_tests.cpp | 6 +++--- test/exception/rehash_tests.cpp | 6 +++--- test/exception/swap_tests.cpp | 6 +++--- test/helpers/check_return_type.hpp | 6 +++--- test/helpers/equivalent.hpp | 6 +++--- test/helpers/fwd.hpp | 6 +++--- test/helpers/generators.hpp | 6 +++--- test/helpers/helpers.hpp | 6 +++--- test/helpers/invariants.hpp | 6 +++--- test/helpers/metafunctions.hpp | 6 +++--- test/helpers/random_values.hpp | 6 +++--- test/helpers/strong.hpp | 6 +++--- test/helpers/tracker.hpp | 6 +++--- test/objects/exception.hpp | 6 +++--- test/objects/fwd.hpp | 6 +++--- test/objects/minimal.hpp | 6 +++--- test/objects/test.hpp | 6 +++--- test/unordered/Jamfile.v2 | 6 +++--- test/unordered/assign_tests.cpp | 6 +++--- test/unordered/bucket_tests.cpp | 6 +++--- test/unordered/compile_tests.cpp | 6 +++--- test/unordered/constructor_tests.cpp | 6 +++--- test/unordered/copy_tests.cpp | 6 +++--- test/unordered/equivalent_keys_tests.cpp | 6 +++--- test/unordered/erase_tests.cpp | 6 +++--- test/unordered/find_tests.cpp | 6 +++--- test/unordered/insert_tests.cpp | 6 +++--- test/unordered/load_factor_tests.cpp | 6 +++--- test/unordered/rehash_tests.cpp | 6 +++--- test/unordered/swap_tests.cpp | 6 +++--- 44 files changed, 134 insertions(+), 129 deletions(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e4fcf07d..ac689dfb 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1,7 +1,7 @@ -# Copyright Daniel James 2006. Use, modification, and distribution are -# 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) +# Copyright 2006 Daniel James. +# Distributed under 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) build-project container ; build-project unordered ; diff --git a/test/container/Jamfile.v2 b/test/container/Jamfile.v2 index 7427bb56..b9a378d4 100644 --- a/test/container/Jamfile.v2 +++ b/test/container/Jamfile.v2 @@ -1,7 +1,7 @@ -# Copyright Daniel James 2005. Use, modification, and distribution are -# 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) +# Copyright 2005 Daniel James. +# Distributed under 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) import testing ; diff --git a/test/container/compile_tests.hpp b/test/container/compile_tests.hpp index 95479b6c..70c02a13 100644 --- a/test/container/compile_tests.hpp +++ b/test/container/compile_tests.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2005-2006. Use, modification, and distribution are -// 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) +// Copyright 2005-2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/container/link_test_1.cpp b/test/container/link_test_1.cpp index 73efe2b4..7f3e8226 100644 --- a/test/container/link_test_1.cpp +++ b/test/container/link_test_1.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/container/link_test_2.cpp b/test/container/link_test_2.cpp index 2a419886..8aa03e5d 100644 --- a/test/container/link_test_2.cpp +++ b/test/container/link_test_2.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/container/map_compile.cpp b/test/container/map_compile.cpp index 281b9c62..1d47cad5 100644 --- a/test/container/map_compile.cpp +++ b/test/container/map_compile.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) // This test creates the containers with members that meet their minimum // requirements. Makes sure everything compiles and is defined correctly. diff --git a/test/container/set_compile.cpp b/test/container/set_compile.cpp index 469f6630..a93e8920 100644 --- a/test/container/set_compile.cpp +++ b/test/container/set_compile.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) // This test creates the containers with members that meet their minimum // requirements. Makes sure everything compiles and is defined correctly. diff --git a/test/container/simple_tests.cpp b/test/container/simple_tests.cpp index c4a7bbd0..265df168 100644 --- a/test/container/simple_tests.cpp +++ b/test/container/simple_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) // This test checks the runtime requirements of containers. diff --git a/test/exception/Jamfile.v2 b/test/exception/Jamfile.v2 index 158eded5..7f32840e 100644 --- a/test/exception/Jamfile.v2 +++ b/test/exception/Jamfile.v2 @@ -1,7 +1,7 @@ -# Copyright Daniel James 2006. Use, modification, and distribution are -# 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) +# Copyright 2006 Daniel James. +# Distributed under 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) import testing ; diff --git a/test/exception/assign_tests.cpp b/test/exception/assign_tests.cpp index 914b9ef3..7d35da3b 100644 --- a/test/exception/assign_tests.cpp +++ b/test/exception/assign_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include "./containers.hpp" diff --git a/test/exception/constructor_tests.cpp b/test/exception/constructor_tests.cpp index ac9206b6..87230879 100644 --- a/test/exception/constructor_tests.cpp +++ b/test/exception/constructor_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include "./containers.hpp" diff --git a/test/exception/containers.hpp b/test/exception/containers.hpp index 475f34f1..c71215c1 100644 --- a/test/exception/containers.hpp +++ b/test/exception/containers.hpp @@ -1,3 +1,8 @@ + +// Copyright 2006 Daniel James. +// Distributed under 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) + #include #include #include "../objects/exception.hpp" diff --git a/test/exception/copy_tests.cpp b/test/exception/copy_tests.cpp index d8b653cd..10ae1666 100644 --- a/test/exception/copy_tests.cpp +++ b/test/exception/copy_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include "./containers.hpp" diff --git a/test/exception/erase_tests.cpp b/test/exception/erase_tests.cpp index dca138d5..6e8339d0 100644 --- a/test/exception/erase_tests.cpp +++ b/test/exception/erase_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include "./containers.hpp" diff --git a/test/exception/insert_tests.cpp b/test/exception/insert_tests.cpp index 7f14c308..925330f8 100644 --- a/test/exception/insert_tests.cpp +++ b/test/exception/insert_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include "./containers.hpp" diff --git a/test/exception/rehash_tests.cpp b/test/exception/rehash_tests.cpp index 7f67aaed..07801021 100644 --- a/test/exception/rehash_tests.cpp +++ b/test/exception/rehash_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include "./containers.hpp" diff --git a/test/exception/swap_tests.cpp b/test/exception/swap_tests.cpp index 4be40fce..66747e08 100644 --- a/test/exception/swap_tests.cpp +++ b/test/exception/swap_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include "./containers.hpp" diff --git a/test/helpers/check_return_type.hpp b/test/helpers/check_return_type.hpp index 87ce7e28..696ef898 100644 --- a/test/helpers/check_return_type.hpp +++ b/test/helpers/check_return_type.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2005-2006. Use, modification, and distribution are -// 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) +// Copyright 2005-2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_HELPERS_CHECK_RETURN_TYPE_HEADER) #define BOOST_UNORDERED_TEST_HELPERS_CHECK_RETURN_TYPE_HEADER diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp index 1b9339aa..da00cf3e 100644 --- a/test/helpers/equivalent.hpp +++ b/test/helpers/equivalent.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2005-2006. Use, modification, and distribution are -// 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) +// Copyright 2005-2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER) #define BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER diff --git a/test/helpers/fwd.hpp b/test/helpers/fwd.hpp index 95b4ef5a..907aa5a6 100644 --- a/test/helpers/fwd.hpp +++ b/test/helpers/fwd.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER) #define BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp index 93c93950..84350fa0 100644 --- a/test/helpers/generators.hpp +++ b/test/helpers/generators.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2005-2006. Use, modification, and distribution are -// 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) +// Copyright 2005-2006 Daniel James. +// Distributed under 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) // A crude wrapper round Boost.Random to make life easier. diff --git a/test/helpers/helpers.hpp b/test/helpers/helpers.hpp index 875a78a2..6a1cadef 100644 --- a/test/helpers/helpers.hpp +++ b/test/helpers/helpers.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_HELPERS_HEADER) #define BOOST_UNORDERED_TEST_HELPERS_HEADER diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index b6c6842c..50dcfd2b 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) // This header contains metafunctions/functions to get the equivalent // associative container for an unordered container, and compare the contents. diff --git a/test/helpers/metafunctions.hpp b/test/helpers/metafunctions.hpp index 05f786d5..56b57223 100644 --- a/test/helpers/metafunctions.hpp +++ b/test/helpers/metafunctions.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2005-2006. Use, modification, and distribution are -// 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) +// Copyright 2005-2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER) #define BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER diff --git a/test/helpers/random_values.hpp b/test/helpers/random_values.hpp index 91e41902..ada5b593 100644 --- a/test/helpers/random_values.hpp +++ b/test/helpers/random_values.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2005-2006. Use, modification, and distribution are -// 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) +// Copyright 2005-2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER) #define BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER diff --git a/test/helpers/strong.hpp b/test/helpers/strong.hpp index 46a836e8..c3fcfaa5 100644 --- a/test/helpers/strong.hpp +++ b/test/helpers/strong.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2005-2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER) #define BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER diff --git a/test/helpers/tracker.hpp b/test/helpers/tracker.hpp index ba7376e5..217724b2 100644 --- a/test/helpers/tracker.hpp +++ b/test/helpers/tracker.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) // This header contains metafunctions/functions to get the equivalent // associative container for an unordered container, and compare the contents. diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index 4727ef28..fe7e31a6 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) #define BOOST_UNORDERED_TEST_OBJECTS_HEADER diff --git a/test/objects/fwd.hpp b/test/objects/fwd.hpp index aea15586..8c824db2 100644 --- a/test/objects/fwd.hpp +++ b/test/objects/fwd.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER) #define BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index 81648e0c..587b81fe 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER) #define BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 6777a1d5..ba61a68c 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) #define BOOST_UNORDERED_TEST_OBJECTS_HEADER diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index d7c2efc4..68d54582 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -1,7 +1,7 @@ -# Copyright Daniel James 2006. Use, modification, and distribution are -# 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) +# Copyright 2006 Daniel James. +# Distributed under 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) import testing ; diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index 3915b81c..434e1c3e 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/bucket_tests.cpp b/test/unordered/bucket_tests.cpp index ba42769d..61c3a26d 100644 --- a/test/unordered/bucket_tests.cpp +++ b/test/unordered/bucket_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/compile_tests.cpp b/test/unordered/compile_tests.cpp index f52324a9..0c9beb98 100644 --- a/test/unordered/compile_tests.cpp +++ b/test/unordered/compile_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 082d4d5f..fa72a454 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index 202286b0..6111c3ba 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/equivalent_keys_tests.cpp b/test/unordered/equivalent_keys_tests.cpp index d6c6a792..9ebb7937 100644 --- a/test/unordered/equivalent_keys_tests.cpp +++ b/test/unordered/equivalent_keys_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index a920a7a6..05319e5c 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/find_tests.cpp b/test/unordered/find_tests.cpp index 6b00435e..03dd9759 100644 --- a/test/unordered/find_tests.cpp +++ b/test/unordered/find_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 20e60643..98cfe5f9 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/load_factor_tests.cpp b/test/unordered/load_factor_tests.cpp index 677b4659..2f5f040f 100644 --- a/test/unordered/load_factor_tests.cpp +++ b/test/unordered/load_factor_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp index 516e1cd7..1e705ebc 100644 --- a/test/unordered/rehash_tests.cpp +++ b/test/unordered/rehash_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp index f9dcddb3..9473c7a0 100644 --- a/test/unordered/swap_tests.cpp +++ b/test/unordered/swap_tests.cpp @@ -1,7 +1,7 @@ -// Copyright Daniel James 2006. Use, modification, and distribution are -// 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) +// Copyright 2006 Daniel James. +// Distributed under 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) #include #include From e9e503be3fb044db65c4d24d1c5b9d868d38de2a Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 1 Jul 2006 22:32:46 +0000 Subject: [PATCH 041/175] Update the copyright for the documentation Jamfile. [SVN r3041] --- doc/Jamfile.v2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 4ae69db4..4d5c2671 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -1,7 +1,7 @@ -# Copyright Daniel James 2005. Use, modification, and distribution are -# 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) +# Copyright 2005 Daniel James. +# Distributed under 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) using quickbook ; From f6222b10e26d0f1161f992c953f57ef337b467b9 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 1 Jul 2006 22:33:29 +0000 Subject: [PATCH 042/175] Fill in more of the unordered container documentation. [SVN r3042] --- doc/buckets.qbk | 104 ++++++++++++++++++-------------------- doc/comparison.qbk | 7 +-- doc/hash_equality.qbk | 114 +++++++++++++++++++++++++++++++++++++++++- doc/intro.qbk | 5 +- 4 files changed, 168 insertions(+), 62 deletions(-) diff --git a/doc/buckets.qbk b/doc/buckets.qbk index 9e3cec3f..099ca7ff 100644 --- a/doc/buckets.qbk +++ b/doc/buckets.qbk @@ -1,28 +1,26 @@ [section:buckets The Data Structure] The containers are made up of a number of 'buckets', each of which can contain -any number of elements. For example, the following -diagram shows an [classref boost::unordered_set unordered_set] with 7 -buckets containing 5 elements, `A`, `B`, `C`, `D` and `E` -(this is just for illustrations, the containers have more buckets, even when -empty). +any number of elements. For example, the following diagram shows an [classref +boost::unordered_set unordered_set] with 7 buckets containing 5 elements, `A`, +`B`, `C`, `D` and `E` (this is just for illustration, in practise containers +will have more buckets). [$../diagrams/buckets.png] -In order to decide which bucket to place an element in, the container -applies `Hash` to the element (for maps it applies it to the element's `Key` -part). This gives a `std::size_t`. `std::size_t` has a much greater range of -values then the number of buckets, so that container applies another -transformation to that value to choose a bucket (in the case of -[classref boost::unordered_set] this is just the modulous of the number of -buckets). +In order to decide which bucket to place an element in, the container applies +`Hash` to the element's key (for `unordered_set` and `unordered_multiset` the +key is the whole element, but this refered to as the key so that the same +terminology can be used for sets and maps). This gives a `std::size_t`. +`std::size_t` has a much greater range of values then the number of buckets, so +that container applies another transformation to that value to choose a bucket +to place the element in. -If at a later date the container wants to find an element in the container -it just has to apply the same process to the element (or key for maps) to -discover which bucket to find it in. This means that you only have to look at -the elements within a bucket when searching, and if the hash function has -worked well an evenly distributed the elements among the buckets, this should -be a small number. +If at a later date the container wants to find an element in the container it +just has to apply the same process to the element's key to discover which +bucket to find it in. This means that you only have to look at the elements +within a single bucket. If the hash function has worked well the elements will +be evenly distributed amongst the buckets. You can see in the diagram that `A` & `D` have been placed in the same bucket. This means that when looking in this bucket, up to 2 comparison have to be @@ -44,6 +42,10 @@ fast we try to keep these to a minimum. [``size_type bucket_size(size_type n) const``] [The number of elements in bucket `n`.] ] + [ + [``size_type bucket(key_type const& k) const``] + [Returns the index of the bucket which would contain k] + ] [ [`` local_iterator begin(size_type n); @@ -65,36 +67,34 @@ The standard gives you two methods to influence the bucket count. First you can specify the minimum number of buckets in the constructor, and later, by calling `rehash`. -The other method is the `max_load_factor` member function. This lets you -/hint/ at the maximum load that the buckets should hold. -The 'load factor' is the average number of elements per bucket, -the container tries to keep this below the maximum load factor, which is -initially set to 1.0. -`max_load_factor` tells the container to change the maximum load factor, -using your supplied hint as a suggestion. +The other method is the `max_load_factor` member function. The 'load factor' +is the average number of elements per bucket, `max_load_factor` can be used +to give a /hint/ of a value that the load factor should be kept below. The +draft standard doesn't actually require the container to pay much attention +to this value. The only time the load factor is /required/ to be less than the +maximum is following a call to `rehash`. But most implementations will probably +try to keep the number of elements below the max load factor, and set the +maximum load factor something the same or near to your hint - unless your hint +is unreasonably small. -The draft standard doesn't actually require the container to pay much attention -to this value. The only time the load factor is required to be less than the -maximum is following a call to `rehash`. +It is not specified anywhere how member functions other than `rehash` affect +the bucket count, although `insert` is only allowed to invalidate iterators +when the insertion causes the load factor to reach the maximum. Which will +typically mean that insert will only change the number of buckets when an +insert causes this. -It is not specified anywhere how other member functions affect the bucket count. -But most implementations will invalidate the iterators whenever they change -the bucket count - which is only allowed when an -`insert` causes the load factor to be more than or equal to the maximum. -But it is possible to implement the containers such that the iterators are -never invalidated. +In a similar manner to using `reserve` for `vector`s, it can be a good idea +to call `rehash` before inserting a large number of elements. This will get +the expensive rehashing out of the way and let you store iterators, safe in +the knowledge that they won't be invalidated. If you are inserting `n` +elements into container `x`, you could first call: -(TODO: This might not be right. I'm not sure what is allowed for -std::unordered_set and std::unordered_map when insert is called with enough -elements to exceed the maximum, but the maximum isn't exceeded because -the elements are already in the container) + x.rehash((x.size() + n) / x.max_load_factor() + 1); -(TODO: Ah, I forgot about local iterators - rehashing must invalidate ranges -made up of local iterators, right?). - -This all sounds quite gloomy, but it's not that bad. Most implementations -will probably respect the maximum load factor hint. This implementation -certainly does. +[blurb Note: `rehash`'s argument is the number of buckets, not the number of +elements, which is why the new size is divided by the maximum load factor. The +`+ 1` is required because the container is allowed to resize when the load +factor is equal to the maximum load factor.] [table Methods for Controlling Bucket Size [[Method] [Description]] @@ -119,20 +119,14 @@ certainly does. ] -[h2 Rehash Techniques] +[/ I'm not at all happy with this section. So I've commented it out.] -If the container has a load factor much smaller than the maximum, `rehash` +[/ h2 Rehash Techniques] + +[/If the container has a load factor much smaller than the maximum, `rehash` might decrease the number of buckets, reducing the memory usage. This isn't guaranteed by the standard but this implementation will do it. -When inserting many elements, it is a good idea to first call `rehash` to -make sure you have enough buckets. This will get the expensive rehashing out -of the way and let you store iterators, safe in the knowledge that they -won't be invalidated. If you are inserting `n` elements into container `x`, -you could first call: - - x.rehash((x.size() + n) / x.max_load_factor() + 1); - If you want to stop the table from ever rehashing due to an insert, you can set the maximum load factor to infinity (or perhaps a load factor that it'll never reach - say `x.max_size()`. As you can only give a 'hint' for the maximum @@ -144,6 +138,6 @@ maybe the implementation should cope with that). If you do this and want to make the container rehash, `rehash` will still work. But be careful that you only ever call it with a sufficient number of buckets - otherwise it's very likely that the container will decrease the bucket -count to an overly small amount. +count to an overly small amount.] [endsect] diff --git a/doc/comparison.qbk b/doc/comparison.qbk index 8684283c..3fc88b0b 100644 --- a/doc/comparison.qbk +++ b/doc/comparison.qbk @@ -1,8 +1,9 @@ -[section:comparison Comparison to Associative Containers] +[section:comparison Comparison with Associative Containers] * The elements in an unordered container are organised into buckets, in an - unpredictable order. There are member functions to.... TODO -* The unordered associative containers don't support the comparison operators. + unpredictable order. There are member functions to access these buckets which + was described earlier. +* The unordered associative containers don't support any comparison operators. * Instead of being parameterized by an ordering relation `Compare`, the unordered associative container are parameterized by a function object `Hash` and an equivalence realtion `Pred`. The member types and accessor diff --git a/doc/hash_equality.qbk b/doc/hash_equality.qbk index f0d9bdf9..9cc1ef7a 100644 --- a/doc/hash_equality.qbk +++ b/doc/hash_equality.qbk @@ -18,6 +18,118 @@ but not the equality predicate, while if you were to change the behaviour of the equality predicate you would have to change the hash function to match it. -For example, if you wanted to use +For example, if you wanted to use the +[@http://www.isthe.com/chongo/tech/comp/fnv/ FNV-1 hash] you could write: + + ``[classref boost::unordered_set]`` words; + +An example implementation of FNV-1, and some other hash functions are supplied +in the examples directory. + +Alternatively, you might wish to use a different equality function. If so, make +sure you use a hash function that matches it. For example, a +case-insensitive dictionary: + + struct iequal_to + : std::binary_function + { + bool operator()(std::string const& x, + std::string const& y) const + { + return boost::algorithm::iequals(x, y); + } + }; + + struct ihash + : std::unary_function + { + bool operator()(std::string const& x) const + { + std::size_t seed = 0; + + for(std::string::const_iterator it = x.begin(); + it != x.end(); ++it) + { + boost::hash_combine(seed, std::tolower(*it)); + } + + return seed; + } + }; + + struct word_info { + // ... + }; + + boost::unordered_map + idictionary; + +[h2 Custom Types] + +Similarly, a custom hash function can be used for custom types: + + struct point { + int x; + int y; + }; + + bool operator==(point const& p1, point const& p2) + { + return p1.x == p2.x && p1.y == p2.y; + } + + struct point_hash + : std::unary_function + { + std::size_t operator()(point const& p) const + { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } + } + + boost::unordered_multiset, point_hash> + points; + +Although, customizing Boost.Hash is probably a better solution: + + struct point { + int x; + int y; + }; + + bool operator==(point const& p1, point const& p2) + { + return p1.x == p2.x && p1.y == p2.y; + } + + std::size_t hash_value(point const& x) { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } + + // Now the default functions work. + boost::unordered_multiset points; + +See the Boost.Hash documentation for more detail on how to do this. Remember +that it relies on extensions to the draft standard - so it won't work on other +implementations of the unordered associative containers. + +[table Methods for accessing the hash and euqality functions. + [[Method] [Description]] + + [ + [``hasher hash_function() const``] + [Returns the container's hash function.] + ] + [ + [``key_equal key_eq() const``] + [Returns the container's key equality function.] + ] +] [endsect] diff --git a/doc/intro.qbk b/doc/intro.qbk index b5919847..5c44afad 100644 --- a/doc/intro.qbk +++ b/doc/intro.qbk @@ -20,9 +20,8 @@ on average. The worst case complexity is linear, but that occurs rarely and with some care, can be avoided. Also, the existing containers require a 'less than' comparison object -to order their elements. For some data types this is impracticle. -It might be slow to calculate, or even impossible. On the other hand, in a hash -table, then elements aren't ordered - but you need an equality function +to order their elements. For some data types this is impossible to implement +or isn't practicle. For a hash table you need an equality function and a hash function for the key. So the __tr1__ introduced the unordered associative containers, which are From cb578defef2eb60cc0affff368c6d1db459f0eea Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 1 Jul 2006 22:34:48 +0000 Subject: [PATCH 043/175] Update copyright. [SVN r3043] --- include/boost/unordered/detail/allocator.hpp | 8 ++++---- include/boost/unordered/detail/hash_table.hpp | 6 ++---- include/boost/unordered/detail/hash_table_impl.hpp | 6 ++---- include/boost/unordered_map.hpp | 6 ++---- include/boost/unordered_set.hpp | 6 ++---- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/include/boost/unordered/detail/allocator.hpp b/include/boost/unordered/detail/allocator.hpp index 8ff1c1f6..46b4f154 100644 --- a/include/boost/unordered/detail/allocator.hpp +++ b/include/boost/unordered/detail/allocator.hpp @@ -1,7 +1,7 @@ -// (C) Copyright Daniel James 2005. -// Use, modification and distribution are 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) + +// Copyright 2005 Daniel James. +// Distributed under 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) #ifndef BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED #define BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 5a2423d5..ecd2a08a 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -1,10 +1,8 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2006 Daniel James - -// 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) +// Distributed under 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) #ifndef BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED #define BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 61c1da8e..e2315550 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1,10 +1,8 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2006 Daniel James - -// 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) +// Distributed under 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) #if BOOST_UNORDERED_HASH_EQUIVALENT #define HASH_TABLE hash_table_equivalent_keys diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 9f40c291..810474b9 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -1,10 +1,8 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2006 Daniel James. - -// 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) +// Distributed under 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) #ifndef BOOST_UNORDERED_MAP_HPP_INCLUDED #define BOOST_UNORDERED_MAP_HPP_INCLUDED diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index 8785bf08..f0db4d3d 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -1,10 +1,8 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2006 Daniel James. - -// 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) +// Distributed under 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) #ifndef BOOST_UNORDERED_SET_HPP_INCLUDED #define BOOST_UNORDERED_SET_HPP_INCLUDED From f2c7ae0fc78cc0c8f4616cff27cd0b5d17926d55 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 2 Jul 2006 12:59:54 +0000 Subject: [PATCH 044/175] Filled in the comparison between associative containers and unordered containers. Now needs to be paired back down. [SVN r3044] --- doc/comparison.qbk | 183 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 160 insertions(+), 23 deletions(-) diff --git a/doc/comparison.qbk b/doc/comparison.qbk index 3fc88b0b..77c503b9 100644 --- a/doc/comparison.qbk +++ b/doc/comparison.qbk @@ -1,27 +1,164 @@ [section:comparison Comparison with Associative Containers] -* The elements in an unordered container are organised into buckets, in an - unpredictable order. There are member functions to access these buckets which - was described earlier. -* The unordered associative containers don't support any comparison operators. -* Instead of being parameterized by an ordering relation `Compare`, - the unordered associative container are parameterized by a function object - `Hash` and an equivalence realtion `Pred`. The member types and accessor - member functions reflect this. -* Because of this, equivalent keys for unordered container are defined in - terms of `Pred`, while for the associative containers it's defined in terms - of `Compare`. -* Unordered associative containers' iterators can be invalidated by rehashing - or by inserting elements. -* Unordered associative containers' iterators are of at least the forward - iterator category. Associative containers' iterators are bidirectional. -* The unordered associative containers' constructors have extra parameters - for the number of buckets, the hash function and the equality predicate. -* The unordered associative container don't have a `lower_bound` or - `upper_bound` method - they wouldn't make any sense since the elements - aren't ordered. -* TODO: Complexity guarantees. -* TODO: Behaviour when exceptions throw. The unordered containers seem - a lot stronger defined here? +TODO: This page probably contains too much information. Some of the comparisons +can probably be paired down (especially the complexity stuff) and some of the +extra details belong elsewhere. + +TODO: I've omitted some similarities - perhaps I should include them. + +[table Interface differences. + [[Associative Containers] [Unordered Associative Containers]] + + [ + [Parameterized by an ordering relation `Compare`] + [Parameterized by a function object `Hash` and an equivalence relation + `Pred`] + ] + [ + [`Compare` exposed by member typedef `key_compare`, accessed by member function `key_comp()`] + [`Hash` exposed by member typedef `hasher`, accessed by member function `hash_function()`.\n`Pred` by member typedef `key_equal` and member function `key_eq()`.] + ] + [ + [Member typedef `value_compare` supplies an ordering comparison for member elements, accessed by member function `value_comp()`.] + [No equivalent. No idea why.] + ] + [/TODO: Mention a range? This is meant to be differences but this doesn't + seem to be complete.] + [ + [Constructors have optional extra parameters for the comparison object.] + [Constructors have optional extra parameters for the initial minimum + number of buckets, a hash function and an equality object.] + ] + + [ + [Keys `k1`, `k2` are considered equivalent if + `!Compare(k1, k2) && !Compare(k2, k1)`] + [Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`] + ] + [ + [Member function `lower_bound(k)` and `upper_bound(k)`] + [No equivalent. Since the elements aren't ordered `lower_bound` and + `upper_bound` would be meaningless.] + ] + [ + [`equal_range(k)` returns an empty range at the position that k + would be inserted if k isn't present in the container.] + [`equal_range(k)` returns a range at the end of the container if + k isn't present in the container. It can't return a positioned + range as k could be inserted into multiple place. To find out the + bucket that k would be inserted into use `bucket(k)`. But remember + that an insert can cause the container to rehash - meaning that the + element can be inserted into a different bucket.] + ] + [ + [`iterator`, `const_iterator` are of the biderctional category.] + [`iterator`, `const_iterator` are of at least the forward category.] + ] + [ + [Inserts do not invalidate iterators or references to the container.] + [Inserts can invalidate iterators but not references to the container.] + ] + [ + [Iterators iterate through the container in the order defined by + the comparison object.] + [Iterators iterate through the container in an arbitrary order, that + can change as elements are inserted. Although, equivalent elements + are always adjacent.] + ] + [ + [No equivalent] + [Local iterators can be used to iterate through individual buckets. + (I don't think that the order of local iterators and iterators are + required to have any correspondence.)] + ] + [ + [Can be compared using the `==`, `!=`, `<`, `<=`, `>`, `>=` operators] + [No comparison operators are defined] + ] + [ + [] + [When inserting with a hint, implementations are permitted to ignore + the hint.] + ] + [ + [`erase` never throws an exception] + [The containers hash or predicate function can throw exceptions + from `erase`] + ] +] + +[table Complexity Guarantess + [[Operation] [Associative Containers] [Unordered Associative Containers]] + [ + [Construction of empty container] + [constant] + [/TODO: Do I meet this?] + [O(/n/) where /n/ is the minimum number of buckets.] + ] + [ + [Construction of container from a range of /N/ elements] + [O(/N/ log /N/), O(/N/) if the range is sorted with `value_comp()`] + [Average case O(/N/), worst case + O(/N/'''2''')] + ] + [ + [Insert a single element] + [logarithmic] + [Average case constant, worst case linear] + ] + [ + [Insert a single element with a hint] + [Amortized constant if t elements inserted right after hint, + logarithmic otherwise] + [Average case constant, worst case linear (ie. the same as + a normal insert).] + ] + [ + [Inserting a range of /N/ elements] + [/N/ log(`size()`+/N/)] + [Average case O(/N/), worst case O(/N/ * 'size()')] + ] + [ + [Erase by key, `k`] + [O(log(`size()`) + `count(k)`)] + [Average case: O(`count(k)`), Worst case: O(`size()`)] + ] + [ + [Erase a single element by iterator] + [Amortized constant] + [Average case: O(1), Worst case: O(`size()`)] + ] + [ + [Erase a range of /N/ elements] + [O(log(`size()`) + /N/)] + [Average case: O(/N/), Worst case: O(`size()`)] + ] + [ + [Clearing the container] + [O(`size()`)] + [O(`size()`)] + ] + [ + [Find] + [logarithmic] + [Average case: O(/N/), Worst case: O(`size()`)] + ] + [/ TODO: Average case is probably wrong. ] + [ + [Count] + [O(log(`size()`) + `count(k)`)] + [Average case: O(1), Worst case: O(`size()`)] + ] + [ + [`equal_range(k)`] + [logarithmic] + [Average case: O(`count(k)`), Worst case: O(`size()`)] + ] + [ + [`lower_bound`,`upper_bound`] + [logarithmic] + [n/a] + ] +] [endsect] From ce6b35d9e28c23c4068b65f09787b46c3f07ddd7 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 2 Jul 2006 22:28:35 +0000 Subject: [PATCH 045/175] Clean up the rationale a little, and add a description of the choices made for the data structure. [SVN r3045] --- doc/rationale.qbk | 76 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/doc/rationale.qbk b/doc/rationale.qbk index 2d6d8a3c..757effff 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -4,28 +4,76 @@ [section:rationale Implementation Rationale] -From the start the intent of this library was to implement the unordered +The intent of this library is to implement the unordered containers in the draft standard, so the interface was fixed. But there are -still some implementation desicions to make. The priorities for the library are +still some implementation desicions to make. The priorities are conformance to the standard and portability. +[h2 Data Structure] + +By specifying an interface for accessing the buckets of the container the +standard pretty much requires that the hash table uses chained addressing. + +It would be conceivable to write a hash table that uses another method. +For example, one could use open addressing, +and use the lookup chain to act as a bucket but there are a few problems +with this. Local iterators would be veryinefficient and may not be able to +meet the complexity requirements. Indicating when an entry is the table is +empty or deleted would be impossible without allocating extra storage - +loosing one of the advantages of open addressing. And for containers with +equivalent keys, making sure that they are adjacent would probably require a +chain of some sort anyway. + +But most damaging is perhaps the +restrictions on when iterators can be invalidated. Since open addressing +degrades badly when there are a high number of collisions the implemenation +might sometimes be unable to rehash when it is essential. To avoid such +problems an implementation would need to set its maximum load factor to a +fairly low value - but the standard requires that it is initially set to 1.0. + +And, of course, since the standard is written with a eye towards chained +addressing, users will be suprised if the performance doesn't reflect that. + +So staying with chained addressing is inevitable. + +For containers with unique keys I use a single-linked list to store the +buckets. There are other possible data structures which would allow for +some operations to be faster (such as erasing and iteration) but the gains +seem too small for the extra cost (in memory). The most commonly used +operations (insertion and lookup) would not be improved. + +But for containers with equivalent keys, a single-linked list can degrade badly +when a large number of elements with equivalent keys are inserted. I think it's +reasonable to assume that users who chose to use `unordered_multiset` or +`unordered_multimap`, did so because they are likely to insert elements with +equivalent keys. So I have used an alternative data structure that doesn't +degrade, at the expense of an extra pointer per node. + [h2 Number of Buckets] There are two popular methods for choosing the number of buckets in a hash -table. One is to have a prime number of buckets. This allows .... (TODO) +table. One is to have a prime number of buckets, another is to use a power +of 2. -The other is to always use a power of two. This has a potential efficiency -advantage, since it avoids the costly modulus calculation. It also allows for ... (TODO) +Using a prime number of buckets, and choosing a bucket by using the modulous +of the hash functions's result will usually give a good result. The downside +is that the modulous operation is fairly expensive. -For a power of two hash table to work the hash values need to be -evenly distributed for the subset of the bits it is going to use - and since -the container can take an arbitrary hash function it must do this itself. -For some methods for doing this see __wang__ (TODO: Other references?). -Unfortunately, the most effective methods require the input to be an integer -with a certain number of bits, while ``std::size_t`` can have an arbitrary -range. This leaves the more expensive methods, such as Knuth's Multiplicative -Method which don't tend to work as well as taking the modulous of a prime, -have little efficiency advantage and don't work well for (TODO: what are they called?). +Using a power of 2 allows for much quicker selection of the bucket +to use, but at the expense of loosing the upper bits of the hash value. +For some specially designed hash functions it is possible to do this and +still get a good result but as the containers can take arbitrary hash +functions this can't be relied on. + +To avoid this a transformation could be applied to the hash function, for an +example see __wang__. Unfortunately, a transformation like Wang's requires +knowledge of the number of bits in the hash value, so it isn't portable enough. +This leaves more expensive methods, such as Knuth's Multiplicative Method +(mentioned in Wang's article). These don't tend to work as well as taking the +modulous of a prime, and can take enough time to loose the +efficiency advantage of power of 2 hash tables. + +So, this implementation uses a prime number for the hash table size. [h2 Active Issues] From ef5dda10a706cf74152052254f1b1c3e88f57e97 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 2 Jul 2006 22:54:20 +0000 Subject: [PATCH 046/175] Starting to add examples for using the unordered container library. [SVN r3046] --- examples/hash_functions/fnv-1.hpp | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 examples/hash_functions/fnv-1.hpp diff --git a/examples/hash_functions/fnv-1.hpp b/examples/hash_functions/fnv-1.hpp new file mode 100644 index 00000000..fcfd8cdf --- /dev/null +++ b/examples/hash_functions/fnv-1.hpp @@ -0,0 +1,66 @@ +// See: http://www.isthe.com/chongo/tech/comp/fnv/ + +#include + +namespace hash +{ + template + struct basic_fnv_1 + { + std::size_t operator()(std::string const& text) + { + std::size_t hash = OffsetBias; + for(std::string::const_iterator it = text.begin(), end = text.end(); + it != end; ++it) + { + hash *= FnvPrime; + hash ^= *it; + } + + return hash; + } + }; + + template + struct basic_fnv_1a + { + std::size_t operator()(std::string const& text) + { + std::size_t hash = OffsetBias; + for(std::string::const_iterator it = text.begin(), end = text.end(); + it != end; ++it) + { + hash ^= *it; + hash *= FnvPrime; + } + + return hash; + } + }; + + // TODO: Select Bias & Prime base on the size of std::size_t. + // + // 32 bit FNV_prime = 16777619 + // 64 bit FNV_prime = 1099511628211 + // 128 bit FNV_prime = 309485009821345068724781401 + // 256 bit FNV_prime = 374144419156711147060143317175368453031918731002211 + // + // 32 bit offset_basis = 2166136261 + // 64 bit offset_basis = 14695981039346656037 + // 128 bit offset_basis = 275519064689413815358837431229664493455 + // 256 bit offset_basis = 100029257958052580907070968620625704837092796014241193945225284501741471925557 + + const std::size_t fnv_prime = 16777619; + // 64 bit FNV_prime = 1099511628211 + // 128 bit FNV_prime = 309485009821345068724781401 + // 256 bit FNV_prime = 374144419156711147060143317175368453031918731002211 + + const std::size_t fnv_offset_bias = 2166136261; + // 64 bit offset_basis = 14695981039346656037 + // 128 bit offset_basis = 275519064689413815358837431229664493455 + // 256 bit offset_basis = 100029257958052580907070968620625704837092796014241193945225284501741471925557 + + typedef basic_fnv_1 fnv_1; + typedef basic_fnv_1a fnv_1a; + +} From 034b97fd23cbbac5d38579cfbcbd412fe100cb8a Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 6 Aug 2006 20:36:29 +0000 Subject: [PATCH 047/175] Refactor and rearrange the unordered containers implementation to try and make it a little more readable. And deal with all the TODOs. [SVN r3114] --- .../unordered/detail/hash_table_impl.hpp | 551 +++++++++--------- 1 file changed, 286 insertions(+), 265 deletions(-) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index e2315550..daf02c88 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -69,7 +69,7 @@ namespace boost { // Hash Bucket // - // all no throw (memory management is performed by HASH_TABLE_DATA). + // all no throw class bucket { @@ -102,12 +102,12 @@ namespace boost { { #if BOOST_UNORDERED_HASH_EQUIVALENT public: - node_base() : group_next_() + node_base() : group_prev_() { - BOOST_HASH_MSVC_RESET_PTR(group_next_); + BOOST_HASH_MSVC_RESET_PTR(group_prev_); } - link_ptr group_next_; + link_ptr group_prev_; #endif }; @@ -119,6 +119,13 @@ namespace boost { value_type value_; }; + // node_constructor + // + // Used to construct nodes in an exception safe manner. + // + // When not paranoid constructs the individual parts of the node seperately (avoiding an extra copy). + // When paranoid just use the more generic version. + #if !defined(BOOST_UNORDERED_PARANOID) class node_constructor { @@ -193,21 +200,23 @@ namespace boost { }; #endif + // Methods for navigating groups of elements with equal keys. + #if BOOST_UNORDERED_HASH_EQUIVALENT - static link_ptr& next_in_group(link_ptr p) { - return static_cast(*p).group_next_; + static link_ptr& prev_in_group(link_ptr p) { + return static_cast(*p).group_prev_; } // pre: Must be pointing to the first node in a group. static link_ptr last_in_group(link_ptr p) { - BOOST_ASSERT(p && p != next_in_group(p)->next_); - return next_in_group(p); + BOOST_ASSERT(p && p != prev_in_group(p)->next_); + return prev_in_group(p); } // pre: Must be pointing to the first node in a group. static link_ptr& next_group(link_ptr p) { - BOOST_ASSERT(p && p != next_in_group(p)->next_); - return next_in_group(p)->next_; + BOOST_ASSERT(p && p != prev_in_group(p)->next_); + return prev_in_group(p)->next_; } #else static link_ptr last_in_group(link_ptr p) { @@ -272,13 +281,6 @@ namespace boost { node_pointer_ = node_pointer_->next_; } - // pre: Must be pointing to first element in group. - void last_in_group() - { - node_pointer_ = HASH_TABLE_DATA::last_in_group(node_pointer_); - } - - // pre: Must be pointing to first element in group. void next_group() { node_pointer_ = HASH_TABLE_DATA::next_group(node_pointer_); @@ -294,7 +296,7 @@ namespace boost { iterator_base() : bucket_(), local_() {} - iterator_base(bucket_ptr b) + explicit iterator_base(bucket_ptr b) : bucket_(b), local_(b->next_) {} iterator_base(bucket_ptr b, link_ptr n) @@ -303,11 +305,6 @@ namespace boost { iterator_base(bucket_ptr b, local_iterator_base const& it) : bucket_(b), local_(it) {} - local_iterator_base local() const - { - return local_iterator_base(local_); - } - bool operator==(iterator_base const& x) const { return local_ == x.local_; @@ -345,7 +342,7 @@ namespace boost { bucket_ptr cached_begin_bucket_; size_type size_; - // Constructor + // Constructor/Deconstructor HASH_TABLE_DATA(size_type n, node_allocator const& a) : node_alloc_(a), bucket_alloc_(a), @@ -371,8 +368,12 @@ namespace boost { if(buckets_) { if(buckets_[bucket_count_].next_) remove_end_marker(); - for(size_type i = 0; i < bucket_count_; ++i) - delete_bucket_contents(buckets_ + i); + bucket_ptr begin = cached_begin_bucket_; + bucket_ptr end = buckets_ + bucket_count_; + while(begin != end) { + clear_bucket(begin); + ++begin; + } for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2) bucket_alloc_.destroy(buckets_ + i2); @@ -381,6 +382,15 @@ namespace boost { } } + // This implementation uses a sentinel to mark the end of the + // buckets, so iterators won't fall of the end. This also acts + // as the end iterator. The problem is that these have to point to + // something. Normally I just point to the bucket itself but I have + // a (probably unfounded) worry that the minimum allocator + // requirements don't require that the pointer type for an allocator + // can point to a descendant type. So I have also support allocating + // a dummy node for that situation. + struct normal_end_marker_impl { static void add(HASH_TABLE_DATA* data) { @@ -511,31 +521,46 @@ namespace boost { // Bucket Size // no throw - size_type bucket_size(size_type n) const + size_type node_count(local_iterator_base it) const { - std::size_t count = 0; - local_iterator_base it1 = begin(n); - while(it1.not_finished()) { + size_type count = 0; + while(it.not_finished()) { + ++count; + it.increment(); + } + return count; + } + + size_type node_count(local_iterator_base it1, + local_iterator_base it2) const + { + size_type count = 0; + while(it1 != it2) { ++count; it1.increment(); } return count; } -#if BOOST_UNORDERED_HASH_EQUIVALENT - std::size_t group_count(local_iterator_base pos) const + size_type bucket_size(size_type n) const { + return node_count(begin(n)); + } + +#if BOOST_UNORDERED_HASH_EQUIVALENT + size_type group_count(local_iterator_base pos) const + { + size_type count = 0; link_ptr it = pos.node_pointer_; link_ptr first = it; - size_type count = 0; do { ++count; - it = next_in_group(it); + it = prev_in_group(it); } while (it != first); // throws, strong return count; } #else - std::size_t group_count(local_iterator_base) const + size_type group_count(local_iterator_base) const { return 1; } @@ -550,19 +575,24 @@ namespace boost { #if BOOST_UNORDERED_HASH_EQUIVALENT link_ptr* get_for_erase(iterator_base r) const { - link_ptr pos = r.local().node_pointer_; + link_ptr pos = r.local_.node_pointer_; - link_ptr* it = &next_in_group(pos)->next_; + // If the element isn't the first in its group, then + // the link to it will be found in the previous element + // in the group. + link_ptr* it = &prev_in_group(pos)->next_; if(*it == pos) return it; + // The element is the first in its group, so just + // need to check the first elements. it = &r.bucket_->next_; - while(*it != pos) it = &(*it)->next_; + while(*it != pos) it = &HASH_TABLE_DATA::next_group(*it); return it; } #else link_ptr* get_for_erase(iterator_base r) const { - link_ptr pos = r.local().node_pointer_; + link_ptr pos = r.local_.node_pointer_; link_ptr* it = &r.bucket_->next_; while(*it != pos) it = &(*it)->next_; return it; @@ -581,10 +611,10 @@ namespace boost { { node& node_ref = get_node(n); node& pos_ref = get_node(pos.node_pointer_); - node_ref.next_ = pos_ref.group_next_->next_; - node_ref.group_next_ = pos_ref.group_next_; - pos_ref.group_next_->next_ = n; - pos_ref.group_next_ = n; + node_ref.next_ = pos_ref.group_prev_->next_; + node_ref.group_prev_ = pos_ref.group_prev_; + pos_ref.group_prev_->next_ = n; + pos_ref.group_prev_ = n; ++size_; } @@ -592,28 +622,19 @@ namespace boost { { node& node_ref = get_node(n); node_ref.next_ = base->next_; - node_ref.group_next_ = n; + node_ref.group_prev_ = n; base->next_ = n; ++size_; if(base < cached_begin_bucket_) cached_begin_bucket_ = base; } - void link_group(link_ptr n, bucket_ptr base) + void link_group(link_ptr n, bucket_ptr base, size_type count) { node& node_ref = get_node(n); - node& last_ref = get_node(node_ref.group_next_); + node& last_ref = get_node(node_ref.group_prev_); last_ref.next_ = base->next_; base->next_ = n; - - // TODO: Use group_count... - // or take count as a parameter - we've probably already counted - // this when we unlinked it. - link_ptr it = n; - do { - ++size_; - it = next_in_group(it); - } while(it != n); - + size_ += count; if(base < cached_begin_bucket_) cached_begin_bucket_ = base; } #else @@ -625,65 +646,144 @@ namespace boost { if(base < cached_begin_bucket_) cached_begin_bucket_ = base; } - void link_group(link_ptr n, bucket_ptr base) + void link_group(link_ptr n, bucket_ptr base, size_type) { link_node(n, base); } #endif #if BOOST_UNORDERED_HASH_EQUIVALENT - // TODO: Improve this: - void unlink_node(link_ptr* pos) + void unlink_node(iterator_base x) { - node& to_delete = get_node(*pos); + node& to_delete = get_node(x.local_.node_pointer_); link_ptr next = to_delete.next_; + link_ptr* pos = get_for_erase(x); - if(to_delete.group_next_ == *pos) { + if(to_delete.group_prev_ == *pos) { // The deleted node is the sole node in the group, so // no need to unlink it from a goup. } - else if(next && next_in_group(next) == *pos) + else if(next && prev_in_group(next) == *pos) { - next_in_group(next) = to_delete.group_next_; + // The deleted node is not at the end of the group, so + // change the link from the next node. + prev_in_group(next) = to_delete.group_prev_; } else { - link_ptr it = to_delete.group_next_; - while(next_in_group(it) != *pos) { - it = next_in_group(it); + // The deleted node is at the end of the group, so the + // node in the group pointing to it is at the beginning + // of the group. Find that to change its pointer. + link_ptr it = to_delete.group_prev_; + while(prev_in_group(it) != *pos) { + it = prev_in_group(it); } - next_in_group(it) = to_delete.group_next_; + prev_in_group(it) = to_delete.group_prev_; } *pos = (*pos)->next_; --size_; } - void unlink_group(link_ptr* pos) + size_type unlink_group(link_ptr* pos) { - size_ -= group_count(local_iterator_base(*pos)); + size_type count = group_count(local_iterator_base(*pos)); + size_ -= count; link_ptr last = last_in_group(*pos); *pos = last->next_; + return count; } #else - void unlink_node(link_ptr* pos) + void unlink_node(iterator_base x) { + link_ptr* pos = get_for_erase(x); *pos = (*pos)->next_; --size_; } - void unlink_group(link_ptr* pos) + size_type unlink_group(link_ptr* pos) { *pos = (*pos)->next_; --size_; + return 1; } #endif - void move_group(HASH_TABLE_DATA& src, bucket_ptr src_bucket, bucket_ptr dst_bucket) + void unlink_nodes(iterator_base pos) { - link_ptr n = src_bucket->next_; - src.unlink_group(&src_bucket->next_); - link_group(n, dst_bucket); + link_ptr* it = get_for_erase(pos); + split_group(*it); + unordered_detail::reset(*it); + size_ -= node_count(pos.local_); } + void unlink_nodes(iterator_base begin, iterator_base end) + { + BOOST_ASSERT(begin.bucket_ == end.bucket_); + local_iterator_base local_end = end.local_; + + size_ -= node_count(begin.local_, local_end); + link_ptr* it = get_for_erase(begin); + split_group(*it, local_end.node_pointer_); + *it = local_end.node_pointer_; + } + + void unlink_nodes(bucket_ptr base, iterator_base end) + { + BOOST_ASSERT(base == end.bucket_); + + local_iterator_base local_end = end.local_; + split_group(local_end.node_pointer_); + + link_ptr ptr(base->next_); + base->next_ = local_end.node_pointer_; + + size_ -= node_count(local_iterator_base(ptr), local_end); + } + +#if BOOST_UNORDERED_HASH_EQUIVALENT + link_ptr split_group(link_ptr split) + { + // If split is at the beginning of the group then there's + // nothing to split. + if(prev_in_group(split)->next_ != split) + return link_ptr(); + + // Find the start of the group. + link_ptr start = split; + do { + start = prev_in_group(start); + } while(prev_in_group(start)->next_ == start); + + // Break the ciruclar list into two, one starting at + // 'it' (the beginning of the group) and one starting at + // 'split'. + link_ptr last = prev_in_group(start); + prev_in_group(start) = prev_in_group(split); + prev_in_group(split) = last; + + return start; + } + + void split_group(link_ptr split1, link_ptr split2) + { + link_ptr begin1 = split_group(split1); + link_ptr begin2 = split_group(split2); + + if(begin1 && split1 == begin2) { + link_ptr end1 = prev_in_group(begin1); + prev_in_group(begin1) = prev_in_group(begin2); + prev_in_group(begin2) = end1; + } + } +#else + void split_group(link_ptr) + { + } + + void split_group(link_ptr, link_ptr) + { + } +#endif + // throws, strong exception-safety: link_ptr construct_node(value_type const& v) { @@ -715,7 +815,7 @@ namespace boost { link_ptr n = construct_node(v); // Rest is no throw - link_node(n, position.local()); + link_node(n, position.local_); return iterator_base(position.bucket_, n); } @@ -758,146 +858,40 @@ namespace boost { // // no throw - void delete_node(link_ptr* pos) + void delete_node(link_ptr ptr) { - // TODO: alloc_cast - node_ptr n = node_alloc_.address(static_cast(**pos)); - unlink_node(pos); - + node_ptr n(node_alloc_.address(static_cast(*ptr))); node_alloc_.destroy(n); node_alloc_.deallocate(n, 1); } - // TODO: Rename this: - void delete_bucket_contents(link_ptr* pos) + void delete_to_bucket_end(link_ptr ptr) { - link_ptr ptr = *pos; - unordered_detail::reset(*pos); - while(ptr) { - node_ptr n = node_alloc_.address( - static_cast(*ptr)); - ptr = n->next_; - - node_alloc_.destroy(n); - node_alloc_.deallocate(n, 1); - --size_; + link_ptr pos = ptr; + ptr = ptr->next_; + delete_node(pos); } } - void delete_bucket_contents(bucket_ptr ptr) + void delete_nodes(link_ptr begin, link_ptr end) { - delete_bucket_contents(&ptr->next_); - } - -#if BOOST_UNORDERED_HASH_EQUIVALENT - link_ptr split_group(link_ptr split) - { - link_ptr it = split; - if(next_in_group(it)->next_ != it) - return link_ptr(); - - do { - it = next_in_group(it); - } while(next_in_group(it)->next_ == it); - - link_ptr tmp = next_in_group(it); - next_in_group(it) = next_in_group(split); - next_in_group(split) = tmp; - - return it; - } - - void split_group(link_ptr split1, link_ptr split2) - { - link_ptr it1 = split_group(split1); - link_ptr it2 = split_group(split2); - - if(it1 && it1 == it2) { - link_ptr tmp = next_in_group(it1); - next_in_group(it1) = next_in_group(it2); - next_in_group(it2) = tmp; - } - } -#else - void split_group(link_ptr) - { - } - - void split_group(link_ptr, link_ptr) - { - } -#endif - - void delete_nodes(iterator_base pos) - { - link_ptr* it = get_for_erase(pos); - split_group(*it); - delete_bucket_contents(it); - } - - void delete_nodes(iterator_base begin, local_iterator_base end) - { - if(end.not_finished()) { - link_ptr* it = get_for_erase(begin); - - link_ptr ptr = *it; - split_group(*it, end.node_pointer_); - *it = end.node_pointer_; - - while(ptr != end.node_pointer_) { - node_ptr n = node_alloc_.address(static_cast(*ptr)); - ptr = n->next_; - - node_alloc_.destroy(n); - node_alloc_.deallocate(n, 1); - --size_; - } - } - else { - delete_nodes(begin); - } - } - - void delete_nodes(bucket_ptr base, local_iterator_base end) - { - BOOST_ASSERT(end.not_finished()); - split_group(end.node_pointer_); - - link_ptr ptr(base->next_); - base->next_ = end.node_pointer_; - - while(ptr != end.node_pointer_) { - node_ptr n = node_alloc_.address(static_cast(*ptr)); - ptr = n->next_; - - node_alloc_.destroy(n); - node_alloc_.deallocate(n, 1); - --size_; + while(begin != end) { + link_ptr pos = begin; + begin = begin->next_; + delete_node(pos); } } #if BOOST_UNORDERED_HASH_EQUIVALENT - std::size_t delete_group(link_ptr* pos) + void delete_group(link_ptr pos) { - std::size_t count = 0; - link_ptr first = *pos; - link_ptr end = next_in_group(first)->next_; - unlink_group(pos); - while(first != end) { - node_ptr n = node_alloc_.address(static_cast(*first)); - first = first->next_; - node_alloc_.destroy(n); - node_alloc_.deallocate(n, 1); - ++count; - } - return count; + delete_nodes(pos, prev_in_group(pos)->next_); } #else - std::size_t delete_group(link_ptr* pos) + void delete_group(link_ptr pos) { delete_node(pos); - return 1; } #endif @@ -906,19 +900,26 @@ namespace boost { // Remove all the nodes. // // no throw - // - // TODO: If delete_nodes did throw (it shouldn't but just for - // argument's sake), could this leave cached_begin_bucket_ pointing - // at an empty bucket? + + void clear_bucket(bucket_ptr b) + { + link_ptr ptr = b->next_; + unordered_detail::reset(b->next_); + delete_to_bucket_end(ptr); + } void clear() { + bucket_ptr begin = buckets_; bucket_ptr end = buckets_ + bucket_count_; - while(cached_begin_bucket_ != end) { - delete_bucket_contents(cached_begin_bucket_); - ++cached_begin_bucket_; + + size_ = 0; + cached_begin_bucket_ = end; + + while(begin != end) { + clear_bucket(begin); + ++begin; } - BOOST_ASSERT(!size_); } // Erase @@ -933,7 +934,8 @@ namespace boost { BOOST_ASSERT(r != end()); iterator_base next = r; next.increment(); - delete_node(get_for_erase(r)); + unlink_node(r); + delete_node(r.local_.node_pointer_); // r has been invalidated but its bucket is still valid recompute_begin_bucket(r.bucket_, next.bucket_); return next; @@ -946,7 +948,8 @@ namespace boost { BOOST_ASSERT(r1 != end()); if (r1.bucket_ == r2.bucket_) { - delete_nodes(r1, r2.local()); + unlink_nodes(r1, r2); + delete_nodes(r1.local_.node_pointer_, r2.local_.node_pointer_); // No need to call recompute_begin_bucket because // the nodes are only deleted from one bucket, which @@ -956,12 +959,18 @@ namespace boost { else { BOOST_ASSERT(r1.bucket_ < r2.bucket_); - delete_nodes(r1); + unlink_nodes(r1); + delete_to_bucket_end(r1.local_.node_pointer_); - for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i) - delete_bucket_contents(i); + for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i) { + size_ -= node_count(local_iterator_base(i->next_)); + clear_bucket(i); + } - if(r2 != end()) delete_nodes(r2.bucket_, r2.local()); + if(r2 != end()) { + unlink_nodes(r2.bucket_, r2); + delete_nodes(r2.bucket_->next_, r2.local_.node_pointer_); + } // r1 has been invalidated but its bucket is still // valid. @@ -1006,6 +1015,17 @@ namespace boost { if(i == cached_begin_bucket_ && i->empty()) cached_begin_bucket_ = j; } + + size_type erase_group(link_ptr* it, bucket_ptr bucket) + { + link_ptr pos = *it; + size_type count = unlink_group(it); + delete_group(pos); + + this->recompute_begin_bucket(bucket); + + return count; + } }; #if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) @@ -1115,7 +1135,7 @@ namespace boost { // no throw template - std::size_t initial_size(I i, I j, size_type n, + size_type initial_size(I i, I j, size_type n, boost::forward_traversal_tag) { // max load factor isn't set yet, but when it is, it'll be 1.0. @@ -1123,14 +1143,14 @@ namespace boost { }; template - std::size_t initial_size(I, I, size_type n, + size_type initial_size(I, I, size_type n, boost::incrementable_traversal_tag) { return n; }; template - std::size_t initial_size(I i, I j, size_type x) + size_type initial_size(I i, I j, size_type x) { BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type iterator_traversal_tag; @@ -1182,7 +1202,6 @@ namespace boost { { if(this != &x) { - // TODO: I could rewrite this to use the existing nodes. this->clear(); // no throw func_ = copy_functions(x); // throws, strong mlf_ = x.mlf_; // no throw @@ -1225,7 +1244,10 @@ namespace boost { // Method 2: can throw if copying throws // (memory allocation/hash function object) // Method 3: Can throw if allocator's swap throws - // (TODO: This one is broken right now, double buffering?) + // (This one will go wrong if the allocator's swap throws + // but since there's no guarantee what the allocators + // will contain it's hard to know what to do. Maybe it + // could double buffer the allocators). void swap(HASH_TABLE& x) { @@ -1240,7 +1262,7 @@ namespace boost { else { #if BOOST_UNORDERED_SWAP_METHOD == 1 throw std::runtime_error( - "Swapping containers with different allocators.");; + "Swapping containers with different allocators."); #elif BOOST_UNORDERED_SWAP_METHOD == 2 // Create new buckets in separate HASH_TABLE_DATA objects // which will clean up if any of this throws. @@ -1253,16 +1275,16 @@ namespace boost { x.copy_buckets(*this, new_that, x.*new_func_that); // throws // Start updating the data here, no throw from now on. - new_this.move_end_marker(*this); // no throw - new_that.move_end_marker(x); // no throw - this->data::swap(new_this); // no throw - x.data::swap(new_that); // no throw + new_this.move_end_marker(*this); // no throw + new_that.move_end_marker(x); // no throw + this->data::swap(new_this); // no throw + x.data::swap(new_that); // no throw #elif BOOST_UNORDERED_SWAP_METHOD == 3 hash_swap(this->node_alloc_, - x.node_alloc_); // no throw, or is it? + x.node_alloc_); // no throw, or is it? hash_swap(this->bucket_alloc_, - x.bucket_alloc_); // no throw, or is it? - this->data::swap(x); // no throw + x.bucket_alloc_); // no throw, or is it? + this->data::swap(x); // no throw #else #error "Invalid swap method" #endif @@ -1305,7 +1327,6 @@ namespace boost { // accessors - // TODO: This creates an unnecessary copy. // no throw value_allocator get_allocator() const { @@ -1337,13 +1358,6 @@ namespace boost { } // no throw - // - // TODO: Is this a good value? The reason why I've set it to this - // as it's the largest value that all the functions can be - // implemented for (rehash's post conditions are impossible for - // larger sizes). But this can be significantly more that the - // allocator's max_size. Also, I should probably check again - // size_type's maximum value. size_type max_size() const { // size < mlf_ * count @@ -1411,8 +1425,24 @@ namespace boost { bool need_to_reserve = n >= max_load_; // throws - basic: if (need_to_reserve) rehash_impl(min_buckets_for_size(n)); - // TODO: Deal with this special case better: - BOOST_ASSERT(n < max_load_ || this->bucket_count_ == max_bucket_count()); + BOOST_ASSERT(n < max_load_ || n > max_size()); + return need_to_reserve; + } + + // basic exception safety + // + // This version of reserve is called when inserting a range + // into a container with equivalent keys, it creates more buckets + // if the resulting load factor would be over 80% of the load + // factor. This is to try to avoid excessive rehashes. + bool reserve_extra(size_type n) + { + bool need_to_reserve = n >= max_load_; + // throws - basic: + if (need_to_reserve) { + rehash_impl(static_cast(floor(n / mlf_ * 1.25)) + 1); + } + BOOST_ASSERT(n < max_load_ || n > max_size()); return need_to_reserve; } @@ -1491,23 +1521,18 @@ namespace boost { return; data new_buckets(n, this->node_alloc_); // throws, seperate - move_buckets(*this, new_buckets); // basic/no throw + move_buckets(*this, new_buckets, hash_function()); + // basic/no throw new_buckets.swap(*this); // no throw calculate_max_load(); // no throw } // move_buckets & copy_buckets // - // Note: Because equivalent elements are already - // adjacent to each other in the existing buckets, this - // simple rehashing technique is sufficient to ensure - // that they remain adjacent to each other in the new - // buckets (but in reverse order). - // // if the hash function throws, basic excpetion safety // no throw otherwise - void move_buckets(data& src, data& dst) + static void move_buckets(data& src, data& dst, hasher const& hf) { BOOST_ASSERT(dst.size_ == 0); BOOST_ASSERT(src.node_alloc_ == dst.node_alloc_); @@ -1518,13 +1543,17 @@ namespace boost { ++src.cached_begin_bucket_) { bucket_ptr src_bucket = src.cached_begin_bucket_; while(src_bucket->next_) { + // Move the first group of equivalent nodes in + // src_bucket to dst. + // This next line throws iff the hash function throws. bucket_ptr dst_bucket = dst.buckets_ + dst.index_from_hash( - hash_function()(extract_key( - get_value(src_bucket->next_)))); + hf(extract_key(get_value(src_bucket->next_)))); - dst.move_group(src, src_bucket, dst_bucket); + link_ptr n = src_bucket->next_; + size_type count = src.unlink_group(&src_bucket->next_); + dst.link_group(n, dst_bucket, count); } } @@ -1533,7 +1562,8 @@ namespace boost { dst.move_end_marker(src); } - // basic excpetion safety, will leave dst partially filled. + // basic excpetion safety. If an exception is thrown this will + // leave dst partially filled. static void copy_buckets(data const& src, data& dst, functions const& f) { @@ -1627,7 +1657,7 @@ namespace boost { // Nothing after this point can throw link_ptr n = a.release(); - this->link_node(n, it.local()); + this->link_node(n, it.local_); return iterator_base(base, n); } @@ -1642,19 +1672,20 @@ namespace boost { template void insert_for_range(I i, I j, forward_traversal_tag) { - std::size_t distance = std::distance(i, j); + size_type distance = std::distance(i, j); if(distance == 1) { insert(*i); } else { // Only require basic exception safety here - reserve(size() + distance); + reserve_extra(size() + distance); + for (; i != j; ++i) { key_type const& k = extract_key(*i); bucket_ptr bucket = get_bucket(k); local_iterator_base position = find_iterator(bucket, k); - // No effects until here, this is strong. + // No effects in loop body until here, this is strong. this->create_node(*i, bucket, position); } } @@ -1776,19 +1807,19 @@ namespace boost { // Insert from iterators (unique keys) template - std::size_t insert_size(I i, I j, boost::forward_traversal_tag) + size_type insert_size(I i, I j, boost::forward_traversal_tag) { return std::distance(i, j); } template - std::size_t insert_size(I i, I j, boost::incrementable_traversal_tag) + size_type insert_size(I i, I j, boost::incrementable_traversal_tag) { return 1; } template - std::size_t insert_size(I i, I j) + size_type insert_size(I i, I j) { BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type iterator_traversal_tag; @@ -1850,15 +1881,8 @@ namespace boost { bucket_ptr bucket = get_bucket(k); link_ptr* it = find_for_erase(bucket, k); - // The rest is no throw. - if (*it) { - size_type count = delete_group(it); - this->recompute_begin_bucket(bucket); - return count; - } - else { - return 0; - } + // No throw. + return *it ? this->erase_group(it, bucket) : 0; } // no throw @@ -1898,11 +1922,8 @@ namespace boost { bucket_ptr bucket = get_bucket(k); local_iterator_base it = find_iterator(bucket, k); if (it.not_finished()) { - local_iterator_base last = it; - last.last_in_group(); - iterator_base first(iterator_base(bucket, it)); - iterator_base second(iterator_base(bucket, last)); + iterator_base second(iterator_base(bucket, last_in_group(it.node_pointer_))); second.increment(); return std::pair(first, second); } From 828dbe50783f9231190057083ae936d5fbccc6b2 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 6 Aug 2006 20:42:45 +0000 Subject: [PATCH 048/175] Test inserting and constructing from input iterators. Check thrown exception types properly. Return by reference from 'get_key' so that the keys aren't copied. [SVN r3115] --- test/exception/constructor_tests.cpp | 16 ++++++++++-- test/exception/erase_tests.cpp | 9 ++++++- test/exception/insert_tests.cpp | 3 ++- test/exception/rehash_tests.cpp | 9 +++++-- test/helpers/helpers.hpp | 8 +++--- test/helpers/input_iterator.hpp | 37 ++++++++++++++++++++++++++++ test/unordered/constructor_tests.cpp | 10 ++++++++ test/unordered/erase_tests.cpp | 1 - test/unordered/insert_tests.cpp | 14 +++++++++++ 9 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 test/helpers/input_iterator.hpp diff --git a/test/exception/constructor_tests.cpp b/test/exception/constructor_tests.cpp index 87230879..390f391a 100644 --- a/test/exception/constructor_tests.cpp +++ b/test/exception/constructor_tests.cpp @@ -9,6 +9,7 @@ #include #include #include "../helpers/random_values.hpp" +#include "../helpers/input_iterator.hpp" struct objects { @@ -111,9 +112,20 @@ struct range_construct_test5 : public range, objects } }; -// TODO: Write a test using an input iterator. +template +struct input_range_construct_test : public range, objects +{ + input_range_construct_test() : range(60) {} + + void run() const { + T x(test::input_iterator(this->values.begin()), + test::input_iterator(this->values.end()), + 0, hash, equal_to, allocator); + } +}; RUN_EXCEPTION_TESTS( (construct_test1)(construct_test2)(construct_test3)(construct_test4)(construct_test5) - (range_construct_test1)(range_construct_test2)(range_construct_test3)(range_construct_test4)(range_construct_test5), + (range_construct_test1)(range_construct_test2)(range_construct_test3)(range_construct_test4)(range_construct_test5) + (input_range_construct_test), CONTAINER_SEQ) diff --git a/test/exception/erase_tests.cpp b/test/exception/erase_tests.cpp index 6e8339d0..98cc5b1e 100644 --- a/test/exception/erase_tests.cpp +++ b/test/exception/erase_tests.cpp @@ -25,7 +25,14 @@ struct erase_test_base : public test::exception_base } void check(T const& x) const { - // TODO: Check that exception was thrown by hash or predicate object? + std::string scope(test::scope); + + // TODO: Instead of checking for 'operator==', I should check against + // a scope stack. + BOOST_CHECK(scope.find("hash::") != std::string::npos || + scope.find("equal_to::") != std::string::npos || + scope == "operator==(object, object)"); + test::check_equivalent_keys(x); } }; diff --git a/test/exception/insert_tests.cpp b/test/exception/insert_tests.cpp index 925330f8..ff779be7 100644 --- a/test/exception/insert_tests.cpp +++ b/test/exception/insert_tests.cpp @@ -12,6 +12,7 @@ #include "../helpers/random_values.hpp" #include "../helpers/invariants.hpp" #include "../helpers/strong.hpp" +#include "../helpers/input_iterator.hpp" #include @@ -31,7 +32,7 @@ struct insert_test_base : public test::exception_base void check(T const& x, strong_type const& strong) const { std::string scope(test::scope); - if(scope.find_first_of("hash::operator()") == std::string::npos) + if(scope.find("hash::operator()") == std::string::npos) strong.test(x); test::check_equivalent_keys(x); } diff --git a/test/exception/rehash_tests.cpp b/test/exception/rehash_tests.cpp index 07801021..9811c162 100644 --- a/test/exception/rehash_tests.cpp +++ b/test/exception/rehash_tests.cpp @@ -33,9 +33,13 @@ struct rehash_test_base : public test::exception_base void check(T const& x, strong_type const& strong) const { std::string scope(test::scope); - if(scope.find_first_of("hash::operator()") == std::string::npos && - scope.find_first_of("equal_to::operator()") == std::string::npos) + // TODO: Instead of checking for 'operator==', I should check against + // a scope stack. + if(scope.find("hash::operator()") == std::string::npos && + scope.find("equal_to::operator()") == std::string::npos && + scope != "operator==(object, object)") strong.test(x); + test::check_equivalent_keys(x); } }; @@ -78,3 +82,4 @@ struct rehash_test4 : rehash_test_base RUN_EXCEPTION_TESTS( (rehash_test0)(rehash_test1)(rehash_test2)(rehash_test3)(rehash_test4), CONTAINER_SEQ) + diff --git a/test/helpers/helpers.hpp b/test/helpers/helpers.hpp index 6a1cadef..83f4a432 100644 --- a/test/helpers/helpers.hpp +++ b/test/helpers/helpers.hpp @@ -13,26 +13,26 @@ namespace test { typedef typename Container::key_type key_type; - static key_type get_key(key_type const& x) + static key_type const& get_key(key_type const& x) { return x; } template - static key_type get_key(std::pair const& x, char = 0) + static key_type const& get_key(std::pair const& x, char = 0) { return x.first; } template - static key_type get_key(std::pair const& x, unsigned char = 0) + static key_type const& get_key(std::pair const& x, unsigned char = 0) { return x.first; } }; template - inline typename Container::key_type get_key(T const& x) + inline typename Container::key_type const& get_key(T const& x) { return get_key_impl::get_key(x); } diff --git a/test/helpers/input_iterator.hpp b/test/helpers/input_iterator.hpp new file mode 100644 index 00000000..3731a408 --- /dev/null +++ b/test/helpers/input_iterator.hpp @@ -0,0 +1,37 @@ + +// Copyright 2005-2006 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER + +#include +#include + +namespace test +{ + // TODO: Make this a stricter input iterator. + template + struct input_iterator_adaptor + : boost::iterator_adaptor< + input_iterator_adaptor, Iterator, + boost::use_default, std::input_iterator_tag> + { + typedef boost::iterator_adaptor< + input_iterator_adaptor, Iterator, + boost::use_default, std::input_iterator_tag> base; + + explicit input_iterator_adaptor(Iterator it = Iterator()) + : base(it) {} + }; + + template + input_iterator_adaptor input_iterator(Iterator it) + { + return input_iterator_adaptor(it); + } +} + +#endif + diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index fa72a454..48d7be23 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -10,6 +10,7 @@ #include "../helpers/random_values.hpp" #include "../helpers/tracker.hpp" #include "../helpers/equivalent.hpp" +#include "../helpers/input_iterator.hpp" #include @@ -204,6 +205,15 @@ void constructor_tests2(T* = 0) test::check_container(x, v); test::check_container(y, x); } + + std::cerr<<"Construct 8 - from input iterator\n"; + { + test::random_values v(100); + T x(test::input_iterator(v.begin()), test::input_iterator(v.end()), 0, hf1, eq1); + T y(test::input_iterator(x.begin()), test::input_iterator(x.end()), 0, hf2, eq2); + test::check_container(x, v); + test::check_container(y, x); + } } int main() diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index 05319e5c..d6cb1408 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -78,7 +78,6 @@ void erase_tests1(Container* = 0) BOOST_TEST(next == (index == 0 ? x.begin() : boost::next(prev))); BOOST_TEST(x.count(key) == count - 1); - std::cerr< @@ -199,6 +200,19 @@ void insert_tests2(X* = 0) test::check_equivalent_keys(x); } + + std::cerr<<"insert input iterator range tests.\n"; + + { + X x; + const_iterator pos = x.begin(); + + test::random_values v(1000); + x.insert(test::input_iterator(v.begin()), test::input_iterator(v.end())); + test::check_container(x, v); + + test::check_equivalent_keys(x); + } } template From 10c5150f3995c9ad91c1b1af7700e4b9c83d35d9 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 6 Aug 2006 20:44:47 +0000 Subject: [PATCH 049/175] Clean up the docbook reference somewhat, and add a note about how only iterator overload is need for insert and erase for containers with unique keys. [SVN r3116] --- doc/ref.xml | 693 +++++++++++++++++++++------------------------------- 1 file changed, 285 insertions(+), 408 deletions(-) diff --git a/doc/ref.xml b/doc/ref.xml index 99f869f6..e0125e17 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -47,17 +47,14 @@ Hash - A unary function object type that acts a hash function for a - Value. It takes a single argument of type - Value and returns a value of type std::size_t. + A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. Pred - A binary function object that implements an equivalence relation on values of type - Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + A binary function object that implements an equivalence relation on values of type Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. @@ -175,22 +172,22 @@ size_type - implementation-defined + implementation-defined - hasher const& - hasher() + hasher const& + hasher() - key_equal const& - key_equal() + key_equal const& + key_equal() - allocator_type const& - allocator_type() + allocator_type const& + allocator_type() - size() == 0 + size() == 0 Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. @@ -210,19 +207,19 @@ size_type - implementation-defined + implementation-defined - hasher const& - hasher() + hasher const& + hasher() - key_equal const& - key_equal() + key_equal const& + key_equal() - allocator_type const& - allocator_type() + allocator_type const& + allocator_type() Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. @@ -231,7 +228,7 @@ - unordered_set const& + unordered_set const& The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. @@ -242,9 +239,13 @@ - unordered_set const& + unordered_set const& - unordered_set&The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + unordered_set& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + allocator_type @@ -253,19 +254,18 @@ bool - size() == 0 + size() == 0 size_type - std::distance(begin(), end()) + std::distance(begin(), end()) size_type - - size() of the largest possible container. + size() of the largest possible container. @@ -300,13 +300,12 @@ size_type - - std::distance(begin(), end()) + std::distance(begin(), end()) - value_type const& + value_type const& std::pair<iterator, bool> @@ -320,8 +319,7 @@ - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -335,7 +333,7 @@ iterator - value_type const& + value_type const& iterator @@ -344,7 +342,7 @@ const_iterator - value_type const& + value_type const& const_iterator @@ -359,8 +357,7 @@ - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -368,6 +365,8 @@ Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. + @@ -387,8 +386,7 @@ - When inserting a single element, if an exception is thrown by an operation other than a call to - hasher the function has no effect. + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -410,34 +408,33 @@ const_iterator - Erase the element pointed to by - position. + Erase the element pointed to by position. - The iterator following - position before the erasure. + The iterator following position before the erasure. - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. + + - key_type const& + key_type const& size_type - Erase all elements with key equivalent to - k. + Erase all elements with key equivalent to k. @@ -445,9 +442,7 @@ - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. @@ -471,20 +466,15 @@ const_iterator - Erases the elements in the range from - first to - last. + Erases the elements in the range from first to last. - The iterator following the erased elements - i.e. - last. + The iterator following the erased elements - i.e. last. - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. They don't get called by the current implementation Boost.Unordered but other implementations may call them. @@ -497,8 +487,7 @@ - - size() == 0 + size() == 0 @@ -508,13 +497,11 @@ - unordered_set& + unordered_set& void - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of - key_equal or - hasher. + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. @@ -535,51 +522,46 @@ - key_type const& + key_type const& iterator - key_type const& + key_type const& iterator - An iterator pointing to an element with key equivalent to - k, or - b.end() if no such element exists. + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - key_type const& + key_type const& size_type - The number of elements with key equivalent to - k. + The number of elements with key equivalent to k. - key_type const& + key_type const& std::pair<iterator, iterator> - key_type const& + key_type const& std::pair<iterator, iterator> - A range with containing all elements with key equivalent to - k. If the container doesn't container any such elements, returns - std::make_pair(b.end(),b.end()). + A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). @@ -617,17 +599,15 @@ - key_type const& + key_type const& size_type - The index of the bucket which would contain an element with key - k. + The index of the bucket which would contain an element with key k. - The return value is less than - bucket_count() + The return value is less than bucket_count() @@ -645,14 +625,12 @@ const_local_iterator - - n shall be in the range + n shall be in the range [0, bucket_count()). - A local iterator pointing the first element in the bucket with index - n. + A local iterator pointing the first element in the bucket with index n. @@ -670,14 +648,12 @@ const_local_iterator - - n shall be in the range + n shall be in the range [0, bucket_count()). - A local iterator pointing the 'one past the end' element in the bucket with index - n. + A local iterator pointing the 'one past the end' element in the bucket with index n. @@ -703,8 +679,7 @@ float - Changes the container's maximum load factor, using - z as a hint. + Changes the container's maximum load factor, using z as a hint. @@ -714,8 +689,7 @@ void - Changes the number of buckets so that there at least - n buckets, and so that the load factor is less than the maximum load factor. + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. Invalidates iterators, and changes the order of elements @@ -739,10 +713,10 @@ - unordered_set<Key, T, Hash, Pred, Alloc>& + unordered_set<Key, T, Hash, Pred, Alloc>& - unordered_set<Key, T, Hash, Pred, Alloc>& + unordered_set<Key, T, Hash, Pred, Alloc>& void @@ -750,9 +724,7 @@ - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of - Hash or - Pred. + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. @@ -798,17 +770,14 @@ Hash - A unary function object type that acts a hash function for a - Value. It takes a single argument of type - Value and returns a value of type std::size_t. + A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. Pred - A binary function object that implements an equivalence relation on values of type - Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + A binary function object that implements an equivalence relation on values of type Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. @@ -926,22 +895,22 @@ size_type - implementation-defined + implementation-defined - hasher const& - hasher() + hasher const& + hasher() - key_equal const& - key_equal() + key_equal const& + key_equal() - allocator_type const& - allocator_type() + allocator_type const& + allocator_type() - size() == 0 + size() == 0 Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. @@ -961,19 +930,19 @@ size_type - implementation-defined + implementation-defined - hasher const& - hasher() + hasher const& + hasher() - key_equal const& - key_equal() + key_equal const& + key_equal() - allocator_type const& - allocator_type() + allocator_type const& + allocator_type() Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. @@ -982,7 +951,7 @@ - unordered_multiset const& + unordered_multiset const& The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. @@ -993,9 +962,13 @@ - unordered_multiset const& + unordered_multiset const& - unordered_multiset&The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + unordered_multiset& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + allocator_type @@ -1004,19 +977,18 @@ bool - size() == 0 + size() == 0 size_type - std::distance(begin(), end()) + std::distance(begin(), end()) size_type - - size() of the largest possible container. + size() of the largest possible container. @@ -1051,13 +1023,12 @@ size_type - - std::distance(begin(), end()) + std::distance(begin(), end()) - value_type const& + value_type const& iterator @@ -1069,8 +1040,7 @@ - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -1084,7 +1054,7 @@ iterator - value_type const& + value_type const& iterator @@ -1093,7 +1063,7 @@ const_iterator - value_type const& + value_type const& const_iterator @@ -1108,8 +1078,7 @@ - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -1136,8 +1105,7 @@ - When inserting a single element, if an exception is thrown by an operation other than a call to - hasher the function has no effect. + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -1159,34 +1127,31 @@ const_iterator - Erase the element pointed to by - position. + Erase the element pointed to by position. - The iterator following - position before the erasure. + The iterator following position before the erasure. - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + - key_type const& + key_type const& size_type - Erase all elements with key equivalent to - k. + Erase all elements with key equivalent to k. @@ -1194,9 +1159,7 @@ - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. @@ -1220,20 +1183,15 @@ const_iterator - Erases the elements in the range from - first to - last. + Erases the elements in the range from first to last. - The iterator following the erased elements - i.e. - last. + The iterator following the erased elements - i.e. last. - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. They don't get called by the current implementation Boost.Unordered but other implementations may call them. @@ -1246,8 +1204,7 @@ - - size() == 0 + size() == 0 @@ -1257,13 +1214,11 @@ - unordered_multiset& + unordered_multiset& void - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of - key_equal or - hasher. + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. @@ -1284,51 +1239,46 @@ - key_type const& + key_type const& iterator - key_type const& + key_type const& iterator - An iterator pointing to an element with key equivalent to - k, or - b.end() if no such element exists. + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - key_type const& + key_type const& size_type - The number of elements with key equivalent to - k. + The number of elements with key equivalent to k. - key_type const& + key_type const& std::pair<iterator, iterator> - key_type const& + key_type const& std::pair<iterator, iterator> - A range with containing all elements with key equivalent to - k. If the container doesn't container any such elements, returns - std::make_pair(b.end(),b.end()). + A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). @@ -1366,17 +1316,15 @@ - key_type const& + key_type const& size_type - The index of the bucket which would contain an element with key - k. + The index of the bucket which would contain an element with key k. - The return value is less than - bucket_count() + The return value is less than bucket_count() @@ -1394,14 +1342,12 @@ const_local_iterator - - n shall be in the range + n shall be in the range [0, bucket_count()). - A local iterator pointing the first element in the bucket with index - n. + A local iterator pointing the first element in the bucket with index n. @@ -1419,14 +1365,12 @@ const_local_iterator - - n shall be in the range + n shall be in the range [0, bucket_count()). - A local iterator pointing the 'one past the end' element in the bucket with index - n. + A local iterator pointing the 'one past the end' element in the bucket with index n. @@ -1452,8 +1396,7 @@ float - Changes the container's maximum load factor, using - z as a hint. + Changes the container's maximum load factor, using z as a hint. @@ -1463,8 +1406,7 @@ void - Changes the number of buckets so that there at least - n buckets, and so that the load factor is less than the maximum load factor. + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. Invalidates iterators, and changes the order of elements @@ -1488,10 +1430,10 @@ - unordered_multiset<Key, T, Hash, Pred, Alloc>& + unordered_multiset<Key, T, Hash, Pred, Alloc>& - unordered_multiset<Key, T, Hash, Pred, Alloc>& + unordered_multiset<Key, T, Hash, Pred, Alloc>& void @@ -1499,9 +1441,7 @@ - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of - Hash or - Pred. + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. @@ -1564,17 +1504,14 @@ Hash - A unary function object type that acts a hash function for a - Key. It takes a single argument of type - Key and returns a value of type std::size_t. + A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. Pred - A binary function object that implements an equivalence relation on values of type - Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + A binary function object that implements an equivalence relation on values of type Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. @@ -1695,22 +1632,22 @@ size_type - implementation-defined + implementation-defined - hasher const& - hasher() + hasher const& + hasher() - key_equal const& - key_equal() + key_equal const& + key_equal() - allocator_type const& - allocator_type() + allocator_type const& + allocator_type() - size() == 0 + size() == 0 Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. @@ -1730,19 +1667,19 @@ size_type - implementation-defined + implementation-defined - hasher const& - hasher() + hasher const& + hasher() - key_equal const& - key_equal() + key_equal const& + key_equal() - allocator_type const& - allocator_type() + allocator_type const& + allocator_type() Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. @@ -1751,7 +1688,7 @@ - unordered_map const& + unordered_map const& The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. @@ -1762,9 +1699,13 @@ - unordered_map const& + unordered_map const& - unordered_map&The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + unordered_map& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + allocator_type @@ -1773,19 +1714,18 @@ bool - size() == 0 + size() == 0 size_type - std::distance(begin(), end()) + std::distance(begin(), end()) size_type - - size() of the largest possible container. + size() of the largest possible container. @@ -1820,13 +1760,12 @@ size_type - - std::distance(begin(), end()) + std::distance(begin(), end()) - value_type const& + value_type const& std::pair<iterator, bool> @@ -1840,8 +1779,7 @@ - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -1855,7 +1793,7 @@ iterator - value_type const& + value_type const& iterator @@ -1864,7 +1802,7 @@ const_iterator - value_type const& + value_type const& const_iterator @@ -1879,8 +1817,7 @@ - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -1888,6 +1825,8 @@ Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. + @@ -1907,8 +1846,7 @@ - When inserting a single element, if an exception is thrown by an operation other than a call to - hasher the function has no effect. + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -1930,34 +1868,33 @@ const_iterator - Erase the element pointed to by - position. + Erase the element pointed to by position. - The iterator following - position before the erasure. + The iterator following position before the erasure. - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. + + - key_type const& + key_type const& size_type - Erase all elements with key equivalent to - k. + Erase all elements with key equivalent to k. @@ -1965,9 +1902,7 @@ - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. @@ -1991,20 +1926,15 @@ const_iterator - Erases the elements in the range from - first to - last. + Erases the elements in the range from first to last. - The iterator following the erased elements - i.e. - last. + The iterator following the erased elements - i.e. last. - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. They don't get called by the current implementation Boost.Unordered but other implementations may call them. @@ -2017,8 +1947,7 @@ - - size() == 0 + size() == 0 @@ -2028,13 +1957,11 @@ - unordered_map& + unordered_map& void - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of - key_equal or - hasher. + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. @@ -2055,72 +1982,64 @@ - key_type const& + key_type const& iterator - key_type const& + key_type const& iterator - An iterator pointing to an element with key equivalent to - k, or - b.end() if no such element exists. + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - key_type const& + key_type const& size_type - The number of elements with key equivalent to - k. + The number of elements with key equivalent to k. - key_type const& + key_type const& std::pair<iterator, iterator> - key_type const& + key_type const& std::pair<iterator, iterator> - A range with containing all elements with key equivalent to - k. If the container doesn't container any such elements, returns - std::make_pair(b.end(),b.end()). + A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - key_type const& + key_type const& - mapped_type& + mapped_type& - If the container does not already contain an elements with a key equivalent to - k, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type()) + If the container does not already contain an elements with a key equivalent to k, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type()) - A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to - k + A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to k - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -2162,17 +2081,15 @@ - key_type const& + key_type const& size_type - The index of the bucket which would contain an element with key - k. + The index of the bucket which would contain an element with key k. - The return value is less than - bucket_count() + The return value is less than bucket_count() @@ -2190,14 +2107,12 @@ const_local_iterator - - n shall be in the range + n shall be in the range [0, bucket_count()). - A local iterator pointing the first element in the bucket with index - n. + A local iterator pointing the first element in the bucket with index n. @@ -2215,14 +2130,12 @@ const_local_iterator - - n shall be in the range + n shall be in the range [0, bucket_count()). - A local iterator pointing the 'one past the end' element in the bucket with index - n. + A local iterator pointing the 'one past the end' element in the bucket with index n. @@ -2248,8 +2161,7 @@ float - Changes the container's maximum load factor, using - z as a hint. + Changes the container's maximum load factor, using z as a hint. @@ -2259,8 +2171,7 @@ void - Changes the number of buckets so that there at least - n buckets, and so that the load factor is less than the maximum load factor. + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. Invalidates iterators, and changes the order of elements @@ -2286,10 +2197,10 @@ - unordered_map<Key, T, Hash, Pred, Alloc>& + unordered_map<Key, T, Hash, Pred, Alloc>& - unordered_map<Key, T, Hash, Pred, Alloc>& + unordered_map<Key, T, Hash, Pred, Alloc>& void @@ -2297,9 +2208,7 @@ - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of - Hash or - Pred. + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. @@ -2354,17 +2263,14 @@ Hash - A unary function object type that acts a hash function for a - Key. It takes a single argument of type - Key and returns a value of type std::size_t. + A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. Pred - A binary function object that implements an equivalence relation on values of type - Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + A binary function object that implements an equivalence relation on values of type Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. @@ -2485,22 +2391,22 @@ size_type - implementation-defined + implementation-defined - hasher const& - hasher() + hasher const& + hasher() - key_equal const& - key_equal() + key_equal const& + key_equal() - allocator_type const& - allocator_type() + allocator_type const& + allocator_type() - size() == 0 + size() == 0 Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. @@ -2520,19 +2426,19 @@ size_type - implementation-defined + implementation-defined - hasher const& - hasher() + hasher const& + hasher() - key_equal const& - key_equal() + key_equal const& + key_equal() - allocator_type const& - allocator_type() + allocator_type const& + allocator_type() Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. @@ -2541,7 +2447,7 @@ - unordered_multimap const& + unordered_multimap const& The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. @@ -2552,9 +2458,13 @@ - unordered_multimap const& + unordered_multimap const& - unordered_multimap&The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + unordered_multimap& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + allocator_type @@ -2563,19 +2473,18 @@ bool - size() == 0 + size() == 0 size_type - std::distance(begin(), end()) + std::distance(begin(), end()) size_type - - size() of the largest possible container. + size() of the largest possible container. @@ -2610,13 +2519,12 @@ size_type - - std::distance(begin(), end()) + std::distance(begin(), end()) - value_type const& + value_type const& iterator @@ -2628,8 +2536,7 @@ - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -2643,7 +2550,7 @@ iterator - value_type const& + value_type const& iterator @@ -2652,7 +2559,7 @@ const_iterator - value_type const& + value_type const& const_iterator @@ -2667,8 +2574,7 @@ - If an exception is thrown by an operation other than a call to - hasher the function has no effect. + If an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -2695,8 +2601,7 @@ - When inserting a single element, if an exception is thrown by an operation other than a call to - hasher the function has no effect. + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. @@ -2718,34 +2623,31 @@ const_iterator - Erase the element pointed to by - position. + Erase the element pointed to by position. - The iterator following - position before the erasure. + The iterator following position before the erasure. - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + - key_type const& + key_type const& size_type - Erase all elements with key equivalent to - k. + Erase all elements with key equivalent to k. @@ -2753,9 +2655,7 @@ - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. @@ -2779,20 +2679,15 @@ const_iterator - Erases the elements in the range from - first to - last. + Erases the elements in the range from first to last. - The iterator following the erased elements - i.e. - last. + The iterator following the erased elements - i.e. last. - Only throws an exception, if it is thrown by a call to - hasher or - key_equal. + Only throws an exception, if it is thrown by a call to hasher or key_equal. They don't get called by the current implementation Boost.Unordered but other implementations may call them. @@ -2805,8 +2700,7 @@ - - size() == 0 + size() == 0 @@ -2816,13 +2710,11 @@ - unordered_multimap& + unordered_multimap& void - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of - key_equal or - hasher. + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. @@ -2843,51 +2735,46 @@ - key_type const& + key_type const& iterator - key_type const& + key_type const& iterator - An iterator pointing to an element with key equivalent to - k, or - b.end() if no such element exists. + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - key_type const& + key_type const& size_type - The number of elements with key equivalent to - k. + The number of elements with key equivalent to k. - key_type const& + key_type const& std::pair<iterator, iterator> - key_type const& + key_type const& std::pair<iterator, iterator> - A range with containing all elements with key equivalent to - k. If the container doesn't container any such elements, returns - std::make_pair(b.end(),b.end()). + A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). @@ -2925,17 +2812,15 @@ - key_type const& + key_type const& size_type - The index of the bucket which would contain an element with key - k. + The index of the bucket which would contain an element with key k. - The return value is less than - bucket_count() + The return value is less than bucket_count() @@ -2953,14 +2838,12 @@ const_local_iterator - - n shall be in the range + n shall be in the range [0, bucket_count()). - A local iterator pointing the first element in the bucket with index - n. + A local iterator pointing the first element in the bucket with index n. @@ -2978,14 +2861,12 @@ const_local_iterator - - n shall be in the range + n shall be in the range [0, bucket_count()). - A local iterator pointing the 'one past the end' element in the bucket with index - n. + A local iterator pointing the 'one past the end' element in the bucket with index n. @@ -3011,8 +2892,7 @@ float - Changes the container's maximum load factor, using - z as a hint. + Changes the container's maximum load factor, using z as a hint. @@ -3022,8 +2902,7 @@ void - Changes the number of buckets so that there at least - n buckets, and so that the load factor is less than the maximum load factor. + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. Invalidates iterators, and changes the order of elements @@ -3049,10 +2928,10 @@ - unordered_multimap<Key, T, Hash, Pred, Alloc>& + unordered_multimap<Key, T, Hash, Pred, Alloc>& - unordered_multimap<Key, T, Hash, Pred, Alloc>& + unordered_multimap<Key, T, Hash, Pred, Alloc>& void @@ -3060,9 +2939,7 @@ - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of - Hash or - Pred. + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. From 8f96a08523ed636a8c371650ce3a625c7a217c39 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 6 Aug 2006 20:46:06 +0000 Subject: [PATCH 050/175] Add some more implementation details. [SVN r3117] --- doc/rationale.qbk | 50 +++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/doc/rationale.qbk b/doc/rationale.qbk index 757effff..7e63a716 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -9,27 +9,31 @@ containers in the draft standard, so the interface was fixed. But there are still some implementation desicions to make. The priorities are conformance to the standard and portability. +The [@http://en.wikipedia.org/wiki/Hash_table wikipedia article on hash tables] +has a good summary of the implementation issues for hash tables in general. + [h2 Data Structure] By specifying an interface for accessing the buckets of the container the standard pretty much requires that the hash table uses chained addressing. -It would be conceivable to write a hash table that uses another method. -For example, one could use open addressing, -and use the lookup chain to act as a bucket but there are a few problems -with this. Local iterators would be veryinefficient and may not be able to -meet the complexity requirements. Indicating when an entry is the table is -empty or deleted would be impossible without allocating extra storage - -loosing one of the advantages of open addressing. And for containers with +It would be conceivable to write a hash table that uses another method. For +example, an it could use open addressing, and use the lookup chain to act as a +bucket but there are a some serious problems with this. The biggest one is that +the draft standard requires that pointers to elements aren't invalidated, so +the elements couldn't be stored in one array, but instead will need a layer of +indirection - loosing the efficiency and memory gains for small types. + +Local iterators would be very inefficient and may not be able to +meet the complexity requirements. And for containers with equivalent keys, making sure that they are adjacent would probably require a chain of some sort anyway. -But most damaging is perhaps the -restrictions on when iterators can be invalidated. Since open addressing -degrades badly when there are a high number of collisions the implemenation -might sometimes be unable to rehash when it is essential. To avoid such -problems an implementation would need to set its maximum load factor to a -fairly low value - but the standard requires that it is initially set to 1.0. +There are also the restrictions on when iterators can be invalidated. Since +open addressing degrades badly when there are a high number of collisions the +restrictions could prevent rehash when it's really needed. The maximum load +factor could be set to a fairly low value to work around this - but the +standard requires that it is initially set to 1.0. And, of course, since the standard is written with a eye towards chained addressing, users will be suprised if the performance doesn't reflect that. @@ -57,7 +61,7 @@ of 2. Using a prime number of buckets, and choosing a bucket by using the modulous of the hash functions's result will usually give a good result. The downside -is that the modulous operation is fairly expensive. +is that the required modulous operation is fairly expensive. Using a power of 2 allows for much quicker selection of the bucket to use, but at the expense of loosing the upper bits of the hash value. @@ -70,7 +74,7 @@ example see __wang__. Unfortunately, a transformation like Wang's requires knowledge of the number of bits in the hash value, so it isn't portable enough. This leaves more expensive methods, such as Knuth's Multiplicative Method (mentioned in Wang's article). These don't tend to work as well as taking the -modulous of a prime, and can take enough time to loose the +modulous of a prime, and the extra computation required might negate efficiency advantage of power of 2 hash tables. So, this implementation uses a prime number for the hash table size. @@ -87,14 +91,22 @@ Need to look into this one. In a fit of probably unwise enthusiasm, I implemented all the three versions with a macro (BOOST_UNORDERED_SWAP_METHOD) to pick which one is used. As -suggested by Howard Hinnant, I set option 3 as the default. I'll probably remove -the alternative implementations before review. +suggested by Howard Hinnant, I set option 3 as the default. I'll probably +remove the alternative implementations before review. + +There is currently a further issue - if the allocator's swap does throw there's +no guarantee what state the allocators will be in. The only solution seems to +be to double buffer the allocators. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#518 518. Are insert and erase stable for unordered_multiset and unordered_multimap?]] -In this implementation, erase is stable but insert is not. As long as a rehash -can change the order of the elements, insert can't be. +In this implementation, erase is stable. All inserts are stable, except for +inserting with a hint, which has slightly surprising behaviour. If the hint +points to the first element in the correct equal range it inserts at the end of +the range, for all other elements in the range it inserts immediately before +the element. I am very tempted to change insert with a hint to just ignore the +hint completely. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#528 528. TR1: issue 6.19 vs 6.3.4.3/2 (and 6.3.4.5/2)]] From a40fb486cc677ed932fc24aa40d657ec261293c3 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 6 Aug 2006 20:47:34 +0000 Subject: [PATCH 051/175] Change the copyright for the unordered container documentation, just my name since I wrote all the documentation (I think I misunderstood the semantics before). [SVN r3118] --- doc/unordered.qbk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/unordered.qbk b/doc/unordered.qbk index 6934e18f..31b5d921 100644 --- a/doc/unordered.qbk +++ b/doc/unordered.qbk @@ -1,7 +1,7 @@ [library Unordered Associative Containers [quickbook 1.3] - [authors [Maitin-Shepard, Jeremy B.], [James, Daniel]] - [copyright 2005 Daniel James] + [authors [James, Daniel]] + [copyright 2005 2006 Daniel James] [purpose std::tr1 compliant hash containers] [id unordered] [dirname unordered] From 5c32477723cf2eb50e01619eb4ae800db68ba0a6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 6 Aug 2006 20:52:33 +0000 Subject: [PATCH 052/175] Actually, on second thoughts, I think I might have been right before. I'll revert it for now. [SVN r3119] --- doc/unordered.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/unordered.qbk b/doc/unordered.qbk index 31b5d921..847154b1 100644 --- a/doc/unordered.qbk +++ b/doc/unordered.qbk @@ -1,6 +1,6 @@ [library Unordered Associative Containers [quickbook 1.3] - [authors [James, Daniel]] + [authors [Maitin-Shepard, Jeremy B.], [James, Daniel]] [copyright 2005 2006 Daniel James] [purpose std::tr1 compliant hash containers] [id unordered] From 905a2dd28a4a9ef1dac227bba994f2f2e218e0a5 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 31 Oct 2006 22:11:25 +0000 Subject: [PATCH 053/175] Simplify the hash table implementation somewhat and avoid an unnecessary copy for maps. [SVN r3339] --- include/boost/unordered/detail/allocator.hpp | 13 + .../unordered/detail/hash_table_impl.hpp | 453 ++++++++---------- 2 files changed, 207 insertions(+), 259 deletions(-) diff --git a/include/boost/unordered/detail/allocator.hpp b/include/boost/unordered/detail/allocator.hpp index 46b4f154..4974dc83 100644 --- a/include/boost/unordered/detail/allocator.hpp +++ b/include/boost/unordered/detail/allocator.hpp @@ -115,6 +115,7 @@ namespace boost { template struct allocator_constructor { + typedef typename Allocator::value_type value_type; typedef typename allocator_pointer::type pointer; Allocator& alloc_; @@ -138,12 +139,24 @@ namespace boost { template void construct(V const& v) { + BOOST_ASSERT(!ptr_ && !constructed_); + ptr_ = alloc_.allocate(1); + alloc_.construct(ptr_, value_type(v)); + constructed_ = true; + } + + void construct(value_type const& v) { BOOST_ASSERT(!ptr_ && !constructed_); ptr_ = alloc_.allocate(1); alloc_.construct(ptr_, v); constructed_ = true; } + pointer get() const + { + return ptr_; + } + // no throw pointer release() { diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index daf02c88..9d221635 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -54,19 +54,6 @@ namespace boost { typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type bucket_ptr; typedef BOOST_DEDUCED_TYPENAME allocator_reference::type reference; -#if defined(BOOST_UNORDERED_PARANOID) - // If the allocator has the expected pointer types I take some liberties. - typedef typename boost::mpl::and_< - boost::is_same, - boost::is_same - >::type is_pointer_allocator; - - typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_< - is_pointer_allocator, bucket_ptr, node_ptr>::type link_ptr; -#else - typedef bucket_ptr link_ptr; -#endif - // Hash Bucket // // all no throw @@ -75,7 +62,7 @@ namespace boost { { bucket& operator=(bucket const&); public: - link_ptr next_; + bucket_ptr next_; bucket() : next_() { @@ -107,7 +94,7 @@ namespace boost { BOOST_HASH_MSVC_RESET_PTR(group_prev_); } - link_ptr group_prev_; + bucket_ptr group_prev_; #endif }; @@ -122,27 +109,44 @@ namespace boost { // node_constructor // // Used to construct nodes in an exception safe manner. - // - // When not paranoid constructs the individual parts of the node seperately (avoiding an extra copy). - // When paranoid just use the more generic version. -#if !defined(BOOST_UNORDERED_PARANOID) + struct allocators + { + node_allocator node_alloc_; + bucket_allocator bucket_alloc_; + value_allocator value_alloc_; + node_base_allocator node_base_alloc_; + + allocators(node_allocator const& a) + : node_alloc_(a), bucket_alloc_(a), + value_alloc_(a), node_base_alloc_(a) + {} + + void destroy(bucket_ptr ptr) + { + node_ptr n(node_alloc_.address(static_cast(*ptr))); + value_alloc_.destroy(value_alloc_.address(n->value_)); + node_base_alloc_.destroy(node_base_alloc_.address(*n)); + node_alloc_.deallocate(n, 1); + } + }; + class node_constructor { - node_allocator& node_alloc_; - bucket_allocator& bucket_alloc_; + allocators& allocators_; node_base_allocator node_base_alloc_; value_allocator value_alloc_; node_ptr ptr_; - bool value_allocated_; - bool node_base_allocated_; + bool value_constructed_; + bool node_base_constructed_; public: - node_constructor(node_allocator& n, bucket_allocator& b) - : node_alloc_(n), bucket_alloc_(b), node_base_alloc_(n), value_alloc_(n), - ptr_(), value_allocated_(false), node_base_allocated_(false) + node_constructor(allocators& a) + : allocators_(a), node_base_alloc_(a.node_alloc_), + value_alloc_(a.node_alloc_), + ptr_(), value_constructed_(false), node_base_constructed_(false) { BOOST_HASH_MSVC_RESET_PTR(ptr_); } @@ -150,14 +154,14 @@ namespace boost { ~node_constructor() { if (ptr_) { - if (value_allocated_) + if (value_constructed_) value_alloc_.destroy( value_alloc_.address(ptr_->value_)); - if (node_base_allocated_) + if (node_base_constructed_) node_base_alloc_.destroy( node_base_alloc_.address(*ptr_)); - node_alloc_.deallocate(ptr_, 1); + allocators_.node_alloc_.deallocate(ptr_, 1); } } @@ -165,78 +169,75 @@ namespace boost { void construct(V const& v) { BOOST_ASSERT(!ptr_); - value_allocated_ = node_base_allocated_ = false; + value_constructed_ = node_base_constructed_ = false; - ptr_ = node_alloc_.allocate(1); + ptr_ = allocators_.node_alloc_.allocate(1); node_base_alloc_.construct(node_base_alloc_.address( *ptr_), node_base()); - node_base_allocated_ = true; + node_base_constructed_ = true; value_alloc_.construct(value_alloc_.address( ptr_->value_), v); - value_allocated_ = true; + value_constructed_ = true; + } + + node_ptr get() const + { + BOOST_ASSERT(ptr_); + return ptr_; } // no throw - link_ptr release() + bucket_ptr release() { node_ptr p = ptr_; unordered_detail::reset(ptr_); - return bucket_alloc_.address(*p); + return allocators_.bucket_alloc_.address(*p); } private: node_constructor(node_constructor const&); node_constructor& operator=(node_constructor const&); }; -#else - class node_constructor - : public allocator_constructor - { - public: - node_constructor(node_allocator& n, bucket_allocator&) - : allocator_constructor(n) {} - }; -#endif // Methods for navigating groups of elements with equal keys. #if BOOST_UNORDERED_HASH_EQUIVALENT - static link_ptr& prev_in_group(link_ptr p) { + static bucket_ptr& prev_in_group(bucket_ptr p) { return static_cast(*p).group_prev_; } // pre: Must be pointing to the first node in a group. - static link_ptr last_in_group(link_ptr p) { + static bucket_ptr last_in_group(bucket_ptr p) { BOOST_ASSERT(p && p != prev_in_group(p)->next_); return prev_in_group(p); } // pre: Must be pointing to the first node in a group. - static link_ptr& next_group(link_ptr p) { + static bucket_ptr& next_group(bucket_ptr p) { BOOST_ASSERT(p && p != prev_in_group(p)->next_); return prev_in_group(p)->next_; } #else - static link_ptr last_in_group(link_ptr p) { + static bucket_ptr last_in_group(bucket_ptr p) { return p; } - static link_ptr& next_group(link_ptr p) { + static bucket_ptr& next_group(bucket_ptr p) { BOOST_ASSERT(p); return p->next_; } #endif // pre: Must be pointing to a node - static node& get_node(link_ptr p) { + static node& get_node(bucket_ptr p) { BOOST_ASSERT(p); return static_cast(*p); } // pre: Must be pointing to a node - static reference get_value(link_ptr p) { + static reference get_value(bucket_ptr p) { BOOST_ASSERT(p); return static_cast(*p).value_; } @@ -244,7 +245,7 @@ namespace boost { class local_iterator_base { public: - link_ptr node_pointer_; + bucket_ptr node_pointer_; local_iterator_base() : node_pointer_() @@ -252,7 +253,7 @@ namespace boost { BOOST_HASH_MSVC_RESET_PTR(node_pointer_); } - explicit local_iterator_base(link_ptr n) + explicit local_iterator_base(bucket_ptr n) : node_pointer_(n) {} bool not_finished() const @@ -299,7 +300,7 @@ namespace boost { explicit iterator_base(bucket_ptr b) : bucket_(b), local_(b->next_) {} - iterator_base(bucket_ptr b, link_ptr n) + iterator_base(bucket_ptr b, bucket_ptr n) : bucket_(b), local_(n) {} iterator_base(bucket_ptr b, local_iterator_base const& it) @@ -334,29 +335,55 @@ namespace boost { // Member Variables - node_allocator node_alloc_; - bucket_allocator bucket_alloc_; - + allocators allocators_; bucket_ptr buckets_; size_type bucket_count_; bucket_ptr cached_begin_bucket_; size_type size_; - // Constructor/Deconstructor + // Constructors/Deconstructor HASH_TABLE_DATA(size_type n, node_allocator const& a) - : node_alloc_(a), bucket_alloc_(a), + : allocators_(a), buckets_(), bucket_count_(next_prime(n)), cached_begin_bucket_(), size_(0) { // The array constructor will clean up in the event of an // exception. allocator_array_constructor - constructor(bucket_alloc_); + constructor(allocators_.bucket_alloc_); + + // Creates an extra bucket to act as a sentinel. constructor.construct(bucket(), bucket_count_ + 1); cached_begin_bucket_ = constructor.get() + bucket_count_; + // Set up the sentinel. + cached_begin_bucket_->next_ = cached_begin_bucket_; + + // Only release the buckets once everything is successfully + // done. + buckets_ = constructor.release(); + } + + HASH_TABLE_DATA(HASH_TABLE_DATA const& h, size_type n) + : allocators_(h.allocators_), + buckets_(), bucket_count_(next_prime(n)), + cached_begin_bucket_(), size_(0) + { + // The array constructor will clean up in the event of an + // exception. + allocator_array_constructor + constructor(allocators_.bucket_alloc_); + + // Creates an extra bucket to act as a sentinel. + constructor.construct(bucket(), bucket_count_ + 1); + + cached_begin_bucket_ = constructor.get() + bucket_count_; + + // Set up the sentinel + cached_begin_bucket_->next_ = cached_begin_bucket_; + // Only release the buckets once everything is successfully // done. buckets_ = constructor.release(); @@ -366,8 +393,6 @@ namespace boost { ~HASH_TABLE_DATA() { if(buckets_) { - if(buckets_[bucket_count_].next_) remove_end_marker(); - bucket_ptr begin = cached_begin_bucket_; bucket_ptr end = buckets_ + bucket_count_; while(begin != end) { @@ -376,93 +401,12 @@ namespace boost { } for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2) - bucket_alloc_.destroy(buckets_ + i2); + allocators_.bucket_alloc_.destroy(buckets_ + i2); - bucket_alloc_.deallocate(buckets_, bucket_count_ + 1); + allocators_.bucket_alloc_.deallocate(buckets_, bucket_count_ + 1); } } - // This implementation uses a sentinel to mark the end of the - // buckets, so iterators won't fall of the end. This also acts - // as the end iterator. The problem is that these have to point to - // something. Normally I just point to the bucket itself but I have - // a (probably unfounded) worry that the minimum allocator - // requirements don't require that the pointer type for an allocator - // can point to a descendant type. So I have also support allocating - // a dummy node for that situation. - - struct normal_end_marker_impl - { - static void add(HASH_TABLE_DATA* data) { - data->buckets_[data->bucket_count_].next_ - = data->buckets_ + data->bucket_count_; - } - - static void remove(HASH_TABLE_DATA* data) { - reset(data->buckets_[data->bucket_count_].next_); - } - - static void move(HASH_TABLE_DATA* src, HASH_TABLE_DATA* dst) { - add(dst); - remove(src); - } - }; - - struct paranoid_end_marker_impl - { - static void add(HASH_TABLE_DATA* data) { - // This seems very wasteful, but I can't think of a better - // way to create an end node and work with all allocators. - data->buckets_[data->bucket_count_].next_ - = data->node_alloc_.allocate(1); - } - - static void remove(HASH_TABLE_DATA* data) { - data->node_alloc_.deallocate( - data->buckets_[data->bucket_count_].next_, 1); - } - - static void move(HASH_TABLE_DATA* src, HASH_TABLE_DATA* dst) { - dst->buckets_[dst->bucket_count_].next_ - = src->buckets_[src->bucket_count_].next_; - reset(src->buckets_[src->bucket_count_].next_); - } - }; - -#if !defined(BOOST_UNORDERED_PARANOID) - typedef normal_end_marker_impl end_marker_impl; -#else - typedef typename mpl::if_::type end_marker_impl; -#endif - - void add_end_marker() - { - BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && - !buckets_[bucket_count_].next_); - end_marker_impl::add(this); - } - - void move_end_marker(HASH_TABLE_DATA& src) - { - BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && - !buckets_[bucket_count_].next_); - BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(src.buckets_) && - BOOST_HASH_BORLAND_BOOL(src.buckets_[src.bucket_count_].next_)); - end_marker_impl::move(&src, this); - BOOST_ASSERT(!!buckets_[bucket_count_].next_); - BOOST_ASSERT(!src.buckets_[src.bucket_count_].next_); - } - - void remove_end_marker() - { - BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) && - BOOST_HASH_BORLAND_BOOL(buckets_[bucket_count_].next_)); - end_marker_impl::remove(this); - unordered_detail::reset(buckets_[bucket_count_].next_); - } - private: HASH_TABLE_DATA(HASH_TABLE_DATA const&); @@ -551,8 +495,8 @@ namespace boost { size_type group_count(local_iterator_base pos) const { size_type count = 0; - link_ptr it = pos.node_pointer_; - link_ptr first = it; + bucket_ptr it = pos.node_pointer_; + bucket_ptr first = it; do { ++count; it = prev_in_group(it); @@ -573,14 +517,14 @@ namespace boost { // no throw #if BOOST_UNORDERED_HASH_EQUIVALENT - link_ptr* get_for_erase(iterator_base r) const + bucket_ptr* get_for_erase(iterator_base r) const { - link_ptr pos = r.local_.node_pointer_; + bucket_ptr pos = r.local_.node_pointer_; // If the element isn't the first in its group, then // the link to it will be found in the previous element // in the group. - link_ptr* it = &prev_in_group(pos)->next_; + bucket_ptr* it = &prev_in_group(pos)->next_; if(*it == pos) return it; // The element is the first in its group, so just @@ -590,10 +534,10 @@ namespace boost { return it; } #else - link_ptr* get_for_erase(iterator_base r) const + bucket_ptr* get_for_erase(iterator_base r) const { - link_ptr pos = r.local_.node_pointer_; - link_ptr* it = &r.bucket_->next_; + bucket_ptr pos = r.local_.node_pointer_; + bucket_ptr* it = &r.bucket_->next_; while(*it != pos) it = &(*it)->next_; return it; } @@ -607,7 +551,7 @@ namespace boost { // no throw #if BOOST_UNORDERED_HASH_EQUIVALENT - void link_node(link_ptr n, local_iterator_base pos) + void link_node(bucket_ptr n, local_iterator_base pos) { node& node_ref = get_node(n); node& pos_ref = get_node(pos.node_pointer_); @@ -618,7 +562,7 @@ namespace boost { ++size_; } - void link_node(link_ptr n, bucket_ptr base) + void link_node(bucket_ptr n, bucket_ptr base) { node& node_ref = get_node(n); node_ref.next_ = base->next_; @@ -628,7 +572,7 @@ namespace boost { if(base < cached_begin_bucket_) cached_begin_bucket_ = base; } - void link_group(link_ptr n, bucket_ptr base, size_type count) + void link_group(bucket_ptr n, bucket_ptr base, size_type count) { node& node_ref = get_node(n); node& last_ref = get_node(node_ref.group_prev_); @@ -638,7 +582,7 @@ namespace boost { if(base < cached_begin_bucket_) cached_begin_bucket_ = base; } #else - void link_node(link_ptr n, bucket_ptr base) + void link_node(bucket_ptr n, bucket_ptr base) { n->next_ = base->next_; base->next_ = n; @@ -646,7 +590,7 @@ namespace boost { if(base < cached_begin_bucket_) cached_begin_bucket_ = base; } - void link_group(link_ptr n, bucket_ptr base, size_type) + void link_group(bucket_ptr n, bucket_ptr base, size_type) { link_node(n, base); } @@ -656,8 +600,8 @@ namespace boost { void unlink_node(iterator_base x) { node& to_delete = get_node(x.local_.node_pointer_); - link_ptr next = to_delete.next_; - link_ptr* pos = get_for_erase(x); + bucket_ptr next = to_delete.next_; + bucket_ptr* pos = get_for_erase(x); if(to_delete.group_prev_ == *pos) { // The deleted node is the sole node in the group, so @@ -673,7 +617,7 @@ namespace boost { // The deleted node is at the end of the group, so the // node in the group pointing to it is at the beginning // of the group. Find that to change its pointer. - link_ptr it = to_delete.group_prev_; + bucket_ptr it = to_delete.group_prev_; while(prev_in_group(it) != *pos) { it = prev_in_group(it); } @@ -683,23 +627,23 @@ namespace boost { --size_; } - size_type unlink_group(link_ptr* pos) + size_type unlink_group(bucket_ptr* pos) { size_type count = group_count(local_iterator_base(*pos)); size_ -= count; - link_ptr last = last_in_group(*pos); + bucket_ptr last = last_in_group(*pos); *pos = last->next_; return count; } #else void unlink_node(iterator_base x) { - link_ptr* pos = get_for_erase(x); + bucket_ptr* pos = get_for_erase(x); *pos = (*pos)->next_; --size_; } - size_type unlink_group(link_ptr* pos) + size_type unlink_group(bucket_ptr* pos) { *pos = (*pos)->next_; --size_; @@ -709,7 +653,7 @@ namespace boost { void unlink_nodes(iterator_base pos) { - link_ptr* it = get_for_erase(pos); + bucket_ptr* it = get_for_erase(pos); split_group(*it); unordered_detail::reset(*it); size_ -= node_count(pos.local_); @@ -721,7 +665,7 @@ namespace boost { local_iterator_base local_end = end.local_; size_ -= node_count(begin.local_, local_end); - link_ptr* it = get_for_erase(begin); + bucket_ptr* it = get_for_erase(begin); split_group(*it, local_end.node_pointer_); *it = local_end.node_pointer_; } @@ -733,61 +677,61 @@ namespace boost { local_iterator_base local_end = end.local_; split_group(local_end.node_pointer_); - link_ptr ptr(base->next_); + bucket_ptr ptr(base->next_); base->next_ = local_end.node_pointer_; size_ -= node_count(local_iterator_base(ptr), local_end); } #if BOOST_UNORDERED_HASH_EQUIVALENT - link_ptr split_group(link_ptr split) + // Break a ciruclar list into two, with split as the beginneing + // of the second group (if split is at the beginning then don't + // split). + bucket_ptr split_group(bucket_ptr split) { // If split is at the beginning of the group then there's // nothing to split. if(prev_in_group(split)->next_ != split) - return link_ptr(); + return bucket_ptr(); // Find the start of the group. - link_ptr start = split; + bucket_ptr start = split; do { start = prev_in_group(start); } while(prev_in_group(start)->next_ == start); - // Break the ciruclar list into two, one starting at - // 'it' (the beginning of the group) and one starting at - // 'split'. - link_ptr last = prev_in_group(start); + bucket_ptr last = prev_in_group(start); prev_in_group(start) = prev_in_group(split); prev_in_group(split) = last; return start; } - void split_group(link_ptr split1, link_ptr split2) + void split_group(bucket_ptr split1, bucket_ptr split2) { - link_ptr begin1 = split_group(split1); - link_ptr begin2 = split_group(split2); + bucket_ptr begin1 = split_group(split1); + bucket_ptr begin2 = split_group(split2); if(begin1 && split1 == begin2) { - link_ptr end1 = prev_in_group(begin1); + bucket_ptr end1 = prev_in_group(begin1); prev_in_group(begin1) = prev_in_group(begin2); prev_in_group(begin2) = end1; } } #else - void split_group(link_ptr) + void split_group(bucket_ptr) { } - void split_group(link_ptr, link_ptr) + void split_group(bucket_ptr, bucket_ptr) { } #endif // throws, strong exception-safety: - link_ptr construct_node(value_type const& v) + bucket_ptr construct_node(value_type const& v) { - node_constructor a(node_alloc_, bucket_alloc_); + node_constructor a(allocators_); a.construct(v); return a.release(); } @@ -801,7 +745,7 @@ namespace boost { iterator_base create_node(value_type const& v, bucket_ptr base) { // throws, strong exception-safety: - link_ptr n = construct_node(v); + bucket_ptr n = construct_node(v); // Rest is no throw link_node(n, base); @@ -812,7 +756,7 @@ namespace boost { iterator_base create_node(value_type const& v, iterator_base position) { // throws, strong exception-safety: - link_ptr n = construct_node(v); + bucket_ptr n = construct_node(v); // Rest is no throw link_node(n, position.local_); @@ -823,7 +767,7 @@ namespace boost { bucket_ptr base, local_iterator_base position) { // throws, strong exception-safety: - link_ptr n = construct_node(v); + bucket_ptr n = construct_node(v); // Rest is no throw if(position.not_finished()) @@ -858,40 +802,33 @@ namespace boost { // // no throw - void delete_node(link_ptr ptr) - { - node_ptr n(node_alloc_.address(static_cast(*ptr))); - node_alloc_.destroy(n); - node_alloc_.deallocate(n, 1); - } - - void delete_to_bucket_end(link_ptr ptr) + void delete_to_bucket_end(bucket_ptr ptr) { while(ptr) { - link_ptr pos = ptr; + bucket_ptr pos = ptr; ptr = ptr->next_; - delete_node(pos); + allocators_.destroy(pos); } } - void delete_nodes(link_ptr begin, link_ptr end) + void delete_nodes(bucket_ptr begin, bucket_ptr end) { while(begin != end) { - link_ptr pos = begin; + bucket_ptr pos = begin; begin = begin->next_; - delete_node(pos); + allocators_.destroy(pos); } } #if BOOST_UNORDERED_HASH_EQUIVALENT - void delete_group(link_ptr pos) + void delete_group(bucket_ptr pos) { delete_nodes(pos, prev_in_group(pos)->next_); } #else - void delete_group(link_ptr pos) + void delete_group(bucket_ptr pos) { - delete_node(pos); + allocators_.destroy(pos); } #endif @@ -903,7 +840,7 @@ namespace boost { void clear_bucket(bucket_ptr b) { - link_ptr ptr = b->next_; + bucket_ptr ptr = b->next_; unordered_detail::reset(b->next_); delete_to_bucket_end(ptr); } @@ -935,7 +872,7 @@ namespace boost { iterator_base next = r; next.increment(); unlink_node(r); - delete_node(r.local_.node_pointer_); + allocators_.destroy(r.local_.node_pointer_); // r has been invalidated but its bucket is still valid recompute_begin_bucket(r.bucket_, next.bucket_); return next; @@ -968,10 +905,11 @@ namespace boost { } if(r2 != end()) { + bucket_ptr first = r2.bucket_->next_; unlink_nodes(r2.bucket_, r2); - delete_nodes(r2.bucket_->next_, r2.local_.node_pointer_); + delete_nodes(first, r2.local_.node_pointer_); } - + // r1 has been invalidated but its bucket is still // valid. recompute_begin_bucket(r1.bucket_, r2.bucket_); @@ -1016,9 +954,9 @@ namespace boost { cached_begin_bucket_ = j; } - size_type erase_group(link_ptr* it, bucket_ptr bucket) + size_type erase_group(bucket_ptr* it, bucket_ptr bucket) { - link_ptr pos = *it; + bucket_ptr pos = *it; size_type count = unlink_group(it); delete_group(pos); @@ -1051,13 +989,12 @@ namespace boost { typedef HASH_TABLE_DATA data; typedef typename data::node_constructor node_constructor; - typedef typename data::link_ptr link_ptr; + typedef typename data::bucket_ptr bucket_ptr; public: typedef BOOST_DEDUCED_TYPENAME data::value_allocator value_allocator; typedef BOOST_DEDUCED_TYPENAME data::node_allocator node_allocator; - typedef BOOST_DEDUCED_TYPENAME data::bucket_ptr bucket_ptr; // Type definitions @@ -1120,7 +1057,6 @@ namespace boost { func_(&HASH_TABLE::func1_), // no throw mlf_(1.0f) // no throw { - this->add_end_marker(); calculate_max_load(); // no throw } @@ -1167,23 +1103,20 @@ namespace boost { func_(&HASH_TABLE::func1_), // no throw mlf_(1.0f) // no throw { - this->add_end_marker(); calculate_max_load(); // no throw // This can throw, but HASH_TABLE_DATA's destructor will clean up. insert(i, j); } - // Copy Construct HASH_TABLE(HASH_TABLE const& x) - : data(x.min_buckets_for_size(x.size()), x.node_alloc_), // throws + : data(x, x.min_buckets_for_size(x.size())), // throws func1_(x.current_functions()), // throws func2_(x.current_functions()), // throws func_(&HASH_TABLE::func1_), // no throw mlf_(x.mlf_) // no throw { - this->add_end_marker(); calculate_max_load(); // no throw // This can throw, but HASH_TABLE_DATA's destructor will clean @@ -1256,7 +1189,8 @@ namespace boost { functions_ptr new_func_this = copy_functions(x); // throws functions_ptr new_func_that = x.copy_functions(*this); // throws - if(this->node_alloc_ == x.node_alloc_) { + if(this->allocators_.node_alloc_ == x.allocators_.node_alloc_ && + this->allocators_.bucket_alloc_ == x.allocators_.bucket_alloc_) { this->data::swap(x); // no throw } else { @@ -1265,26 +1199,25 @@ namespace boost { "Swapping containers with different allocators."); #elif BOOST_UNORDERED_SWAP_METHOD == 2 // Create new buckets in separate HASH_TABLE_DATA objects - // which will clean up if any of this throws. - data new_this(x.min_buckets_for_size(x.size_), - this->node_alloc_); // throws - copy_buckets(x, new_this, this->*new_func_this); // throws + // which will clean up if anything throws an exception. + // (all can throw, but with no effect as these are new objects). + data new_this(*this, x.min_buckets_for_size(x.size_)); + copy_buckets(x, new_this, this->*new_func_this); - data new_that(min_buckets_for_size(this->size_), - x.node_alloc_); // throws - x.copy_buckets(*this, new_that, x.*new_func_that); // throws + data new_that(x, min_buckets_for_size(this->size_)); + x.copy_buckets(*this, new_that, x.*new_func_that); // Start updating the data here, no throw from now on. - new_this.move_end_marker(*this); // no throw - new_that.move_end_marker(x); // no throw - this->data::swap(new_this); // no throw - x.data::swap(new_that); // no throw + this->data::swap(new_this); + x.data::swap(new_that); #elif BOOST_UNORDERED_SWAP_METHOD == 3 - hash_swap(this->node_alloc_, - x.node_alloc_); // no throw, or is it? - hash_swap(this->bucket_alloc_, - x.bucket_alloc_); // no throw, or is it? - this->data::swap(x); // no throw + // Note: I'm not sure that these are guaranteed to be no + // throw. + hash_swap(this->allocators_.node_alloc_, + x.allocators_.node_alloc_); + hash_swap(this->allocators_.bucket_alloc_, + x.allocators_.bucket_alloc_); + this->data::swap(x); #else #error "Invalid swap method" #endif @@ -1330,7 +1263,7 @@ namespace boost { // no throw value_allocator get_allocator() const { - return this->node_alloc_; + return this->allocators_.node_alloc_; } // no throw @@ -1388,7 +1321,7 @@ namespace boost { size_type max_bucket_count() const { // -1 to account for the end marker. - return prev_prime(this->bucket_alloc_.max_size() - 1); + return prev_prime(this->allocators_.bucket_alloc_.max_size() - 1); } private: @@ -1520,7 +1453,7 @@ namespace boost { if (n == bucket_count()) // no throw return; - data new_buckets(n, this->node_alloc_); // throws, seperate + data new_buckets(*this, n); // throws, seperate move_buckets(*this, new_buckets, hash_function()); // basic/no throw new_buckets.swap(*this); // no throw @@ -1535,7 +1468,7 @@ namespace boost { static void move_buckets(data& src, data& dst, hasher const& hf) { BOOST_ASSERT(dst.size_ == 0); - BOOST_ASSERT(src.node_alloc_ == dst.node_alloc_); + BOOST_ASSERT(src.allocators_.node_alloc_ == dst.allocators_.node_alloc_); bucket_ptr end = src.buckets_ + src.bucket_count_; @@ -1551,15 +1484,11 @@ namespace boost { dst.index_from_hash( hf(extract_key(get_value(src_bucket->next_)))); - link_ptr n = src_bucket->next_; + bucket_ptr n = src_bucket->next_; size_type count = src.unlink_group(&src_bucket->next_); dst.link_group(n, dst_bucket, count); } } - - // Move the end marker from the source to destination. - // Now destination is valid, source is not. - dst.move_end_marker(src); } // basic excpetion safety. If an exception is thrown this will @@ -1609,7 +1538,7 @@ namespace boost { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). - node_constructor a(this->node_alloc_, this->bucket_alloc_); + node_constructor a(this->allocators_); a.construct(v); // reserve has basic exception safety if the hash function @@ -1619,7 +1548,7 @@ namespace boost { // Nothing after the point can throw. - link_ptr n = a.release(); + bucket_ptr n = a.release(); // I'm relying on local_iterator_base not being invalidated by // the rehash here. @@ -1646,7 +1575,7 @@ namespace boost { else { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). - node_constructor a(this->node_alloc_, this->bucket_alloc_); + node_constructor a(this->allocators_); a.construct(v); // reserve has basic exception safety if the hash function @@ -1656,7 +1585,7 @@ namespace boost { // Nothing after this point can throw - link_ptr n = a.release(); + bucket_ptr n = a.release(); this->link_node(n, it.local_); return iterator_base(base, n); @@ -1681,12 +1610,17 @@ namespace boost { reserve_extra(size() + distance); for (; i != j; ++i) { - key_type const& k = extract_key(*i); + node_constructor a(this->allocators_); + a.construct(*i); + + key_type const& k = extract_key(a.get()->value_); bucket_ptr bucket = get_bucket(k); local_iterator_base position = find_iterator(bucket, k); - // No effects in loop body until here, this is strong. - this->create_node(*i, bucket, position); + if(position.not_finished()) + link_node(a.release(), position); + else + link_node(a.release(), bucket); } } } @@ -1734,7 +1668,7 @@ namespace boost { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). - node_constructor a(this->node_alloc_, this->bucket_alloc_); + node_constructor a(this->allocators_); a.construct(value_type(k, mapped_type())); // reserve has basic exception safety if the hash function @@ -1744,7 +1678,7 @@ namespace boost { // Nothing after this point can throw. - link_ptr n = a.release(); + bucket_ptr n = a.release(); this->link_node(n, bucket); return *local_iterator_base(n); @@ -1774,7 +1708,7 @@ namespace boost { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). - node_constructor a(this->node_alloc_, this->bucket_alloc_); + node_constructor a(this->allocators_); a.construct(v); // reserve has basic exception safety if the hash function @@ -1784,7 +1718,7 @@ namespace boost { // Nothing after this point can throw. - link_ptr n = a.release(); + bucket_ptr n = a.release(); this->link_node(n, bucket); return std::pair( @@ -1835,11 +1769,10 @@ namespace boost { // safety since insert is only called once. for (; i != j; ++i) { // No side effects in this initial code - key_type const& k = extract_key(*i); - size_type hash_value = hash_function()(k); + size_type hash_value = hash_function()(extract_key(*i)); bucket_ptr bucket = this->buckets_ + this->index_from_hash(hash_value); - local_iterator_base pos = find_iterator(bucket, k); + local_iterator_base pos = find_iterator(bucket, extract_key(*i)); if (!pos.not_finished()) { // Doesn't already exist, add to bucket. @@ -1847,7 +1780,7 @@ namespace boost { // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). - node_constructor a(this->node_alloc_, this->bucket_alloc_); + node_constructor a(this->allocators_); value_type const& v = *i; a.construct(v); @@ -1879,7 +1812,7 @@ namespace boost { { // No side effects in initial section bucket_ptr bucket = get_bucket(k); - link_ptr* it = find_for_erase(bucket, k); + bucket_ptr* it = find_for_erase(bucket, k); // No throw. return *it ? this->erase_group(it, bucket) : 0; @@ -1959,9 +1892,9 @@ namespace boost { } // strong exception safety, no side effects - link_ptr* find_for_erase(bucket_ptr bucket, key_type const& k) const + bucket_ptr* find_for_erase(bucket_ptr bucket, key_type const& k) const { - link_ptr* it = &bucket->next_; + bucket_ptr* it = &bucket->next_; while(*it && !equal(k, this->get_value(*it))) it = &this->next_group(*it); @@ -2128,3 +2061,5 @@ namespace boost { #undef HASH_CONST_ITERATOR #undef HASH_LOCAL_ITERATOR #undef HASH_CONST_LOCAL_ITERATOR + + From 136e2fe3bafdcdb82b64221c05e473460c7347b9 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 31 Oct 2006 22:15:47 +0000 Subject: [PATCH 054/175] Documentation tweaks. [SVN r3340] --- doc/comparison.qbk | 6 ---- doc/diagrams/buckets.png | Bin 13856 -> 13867 bytes doc/rationale.qbk | 65 +++++++++++++++++++++++---------------- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/doc/comparison.qbk b/doc/comparison.qbk index 77c503b9..53e28bff 100644 --- a/doc/comparison.qbk +++ b/doc/comparison.qbk @@ -1,11 +1,5 @@ [section:comparison Comparison with Associative Containers] -TODO: This page probably contains too much information. Some of the comparisons -can probably be paired down (especially the complexity stuff) and some of the -extra details belong elsewhere. - -TODO: I've omitted some similarities - perhaps I should include them. - [table Interface differences. [[Associative Containers] [Unordered Associative Containers]] diff --git a/doc/diagrams/buckets.png b/doc/diagrams/buckets.png index 6d8ecdab9cfc4731a7839b0c954e63d395d34cc4..202673207ad8e51969c97d1782214a318e49f337 100644 GIT binary patch delta 54 zcmZ3GvpQ#k6~|^vj-#BL{|f%&WaOIsOZo~U*XFY_8##HoxK2;kH#asmHZtPk+T5=8 LoqKbw(PCx*NQV>b delta 45 zcmZ3Tvmj@K6~|_4jw76#g#L0){x5xb^A(v5oSdhp>zf-J8ygvIo~ZVndvmAJB4z-? CD-z5A diff --git a/doc/rationale.qbk b/doc/rationale.qbk index 7e63a716..d79cd93e 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -19,40 +19,47 @@ standard pretty much requires that the hash table uses chained addressing. It would be conceivable to write a hash table that uses another method. For example, an it could use open addressing, and use the lookup chain to act as a -bucket but there are a some serious problems with this. The biggest one is that -the draft standard requires that pointers to elements aren't invalidated, so -the elements couldn't be stored in one array, but instead will need a layer of -indirection - loosing the efficiency and memory gains for small types. +bucket but there are a some serious problems with this: -Local iterators would be very inefficient and may not be able to -meet the complexity requirements. And for containers with -equivalent keys, making sure that they are adjacent would probably require a -chain of some sort anyway. +* The draft standard requires that pointers to elements aren't invalidated, so + the elements can't be stored in one array, but will need a layer of + indirection instead - loosing the efficiency and most of the memory gain, + the main advantages of open addressing. -There are also the restrictions on when iterators can be invalidated. Since -open addressing degrades badly when there are a high number of collisions the -restrictions could prevent rehash when it's really needed. The maximum load -factor could be set to a fairly low value to work around this - but the -standard requires that it is initially set to 1.0. +* Local iterators would be very inefficient and may not be able to + meet the complexity requirements. + +* There are also the restrictions on when iterators can be invalidated. Since + open addressing degrades badly when there are a high number of collisions the + restrictions could prevent a rehash when it's really needed. The maximum load + factor could be set to a fairly low value to work around this - but the + standard requires that it is initially set to 1.0. -And, of course, since the standard is written with a eye towards chained -addressing, users will be suprised if the performance doesn't reflect that. +* And since the standard is written with a eye towards chained + addressing, users will be suprised if the performance doesn't reflect that. -So staying with chained addressing is inevitable. +So chained addressing is used. -For containers with unique keys I use a single-linked list to store the -buckets. There are other possible data structures which would allow for -some operations to be faster (such as erasing and iteration) but the gains -seem too small for the extra cost (in memory). The most commonly used -operations (insertion and lookup) would not be improved. +For containers with unique keys I store the buckets in a single-linked list. +There are other possible data structures (such as a double-linked list) +that allow for some operations to be faster (such as erasing and iteration) +but the possible gain seems small compared to the extra memory needed. +The most commonly used operations (insertion and lookup) would not be improved +at all. -But for containers with equivalent keys, a single-linked list can degrade badly +But for containers with equivalent keys a single-linked list can degrade badly when a large number of elements with equivalent keys are inserted. I think it's -reasonable to assume that users who chose to use `unordered_multiset` or -`unordered_multimap`, did so because they are likely to insert elements with +reasonable to assume that users who choose to use `unordered_multiset` or +`unordered_multimap` do so because they are likely to insert elements with equivalent keys. So I have used an alternative data structure that doesn't degrade, at the expense of an extra pointer per node. +This works by adding storing a circular linked list for each group of equivalent +nodes in reverse order. This allows quick navigation to the end of a group (since +the first element points to the last) and can be quickly updated when elements +are inserted or erased. The main disadvantage of this approach is some hairy code +for erasing elements. + [h2 Number of Buckets] There are two popular methods for choosing the number of buckets in a hash @@ -119,8 +126,12 @@ some member functions are overloaded by the same type. The proposed resolution is to add a new subsection to 17.4.4: [:An implementation shall not supply an overloaded function signature specified in any library clause if such a signature would be inherently ambiguous during overload resolution due to two library types referring to the same type.] -So I don't supply the `iterator` overloads - although this means that the -header and documentation are currently inconsistent. -This will be fixed before review submission. +So I don't supply the `iterator` overloads. + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#560 + 560. User-defined allocators without default constructor]] + +This implementation should work okay for an allocator without a default +constructor, although I don't currently test for this. [endsect] From ec310f7b80a86d49cbae74216b490aacacc8e215 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 31 Oct 2006 22:19:26 +0000 Subject: [PATCH 055/175] Update the unordered tests. Several changes including extra erase tests. The newer version of the containers have a hairy erase implementation, so need to test all the special cases. Also, a few extra tests here and there, avoid a couple of warnings and remove some old TODOs. [SVN r3341] --- test/container/compile_tests.hpp | 2 +- test/container/simple_tests.cpp | 7 - test/exception/erase_tests.cpp | 7 - test/exception/rehash_tests.cpp | 2 - test/helpers/generators.hpp | 61 +++------ test/helpers/input_iterator.hpp | 1 - test/helpers/invariants.hpp | 4 +- test/helpers/random_values.hpp | 8 +- test/helpers/strong.hpp | 8 -- test/helpers/tracker.hpp | 3 +- test/objects/exception.hpp | 38 +++--- test/objects/minimal.hpp | 27 +--- test/objects/test.hpp | 16 +-- test/unordered/Jamfile.v2 | 1 + test/unordered/assign_tests.cpp | 2 - test/unordered/constructor_tests.cpp | 43 ++++++ test/unordered/copy_tests.cpp | 2 - test/unordered/erase_equiv_tests.cpp | 195 +++++++++++++++++++++++++++ test/unordered/erase_tests.cpp | 2 - test/unordered/insert_tests.cpp | 21 ++- 20 files changed, 316 insertions(+), 134 deletions(-) create mode 100644 test/unordered/erase_equiv_tests.cpp diff --git a/test/container/compile_tests.hpp b/test/container/compile_tests.hpp index 70c02a13..264e038a 100644 --- a/test/container/compile_tests.hpp +++ b/test/container/compile_tests.hpp @@ -17,7 +17,7 @@ typedef long double comparison_type; template void sink(T const&) {} template -void container_test(X& r, T& value) +void container_test(X& r, T&) { typedef typename X::iterator iterator; typedef typename X::const_iterator const_iterator; diff --git a/test/container/simple_tests.cpp b/test/container/simple_tests.cpp index 265df168..91640148 100644 --- a/test/container/simple_tests.cpp +++ b/test/container/simple_tests.cpp @@ -43,7 +43,6 @@ void simple_test(X const& a) } { - // TODO: Also test with a random container... X b(a); X c; BOOST_TEST(equivalent(b)); @@ -59,8 +58,6 @@ void simple_test(X const& a) { X u; X& r = u; - // TODO: I can't actually see a requirement for that assignment - // returns a reference to itself (just that it returns a reference). BOOST_TEST(&(r = r) == &r); BOOST_TEST(r.empty()); BOOST_TEST(&(r = a) == &r); @@ -74,8 +71,6 @@ void simple_test(X const& a) (typename X::size_type) std::distance(a.begin(), a.end())); } - // TODO: Test max_size against allocator? - { BOOST_TEST(a.empty() == (a.size() == 0)); } @@ -85,8 +80,6 @@ void simple_test(X const& a) X u; BOOST_TEST(u.begin() == u.end()); } - - // TODO: Test construction with allocator? } int main() diff --git a/test/exception/erase_tests.cpp b/test/exception/erase_tests.cpp index 98cc5b1e..c1709a9d 100644 --- a/test/exception/erase_tests.cpp +++ b/test/exception/erase_tests.cpp @@ -27,8 +27,6 @@ struct erase_test_base : public test::exception_base void check(T const& x) const { std::string scope(test::scope); - // TODO: Instead of checking for 'operator==', I should check against - // a scope stack. BOOST_CHECK(scope.find("hash::") != std::string::npos || scope.find("equal_to::") != std::string::npos || scope == "operator==(object, object)"); @@ -52,11 +50,6 @@ struct erase_by_key_test1 : public erase_test_base } }; -// TODO: More tests... -// Better test by key. -// Test other erase signatures - generally they won't throw, but the standard -// does allow them to. And test clear as well. - RUN_EXCEPTION_TESTS( (erase_by_key_test1), CONTAINER_SEQ) diff --git a/test/exception/rehash_tests.cpp b/test/exception/rehash_tests.cpp index 9811c162..0634b44d 100644 --- a/test/exception/rehash_tests.cpp +++ b/test/exception/rehash_tests.cpp @@ -33,8 +33,6 @@ struct rehash_test_base : public test::exception_base void check(T const& x, strong_type const& strong) const { std::string scope(test::scope); - // TODO: Instead of checking for 'operator==', I should check against - // a scope stack. if(scope.find("hash::operator()") == std::string::npos && scope.find("equal_to::operator()") == std::string::npos && scope != "operator==(object, object)") diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp index 84350fa0..260a45b5 100644 --- a/test/helpers/generators.hpp +++ b/test/helpers/generators.hpp @@ -10,25 +10,31 @@ #include #include - -#if 0 +#include #include #include #include #include #include -#else -#include -#endif #include "./fwd.hpp" namespace test { -#if 0 typedef boost::hellekalek1995 integer_generator_type; typedef boost::lagged_fibonacci607 real_generator_type; -#endif + + template + struct generator; + + template std::pair generate( + std::pair const*) + { + static generator g1; + static generator g2; + + return std::pair(g1(), g2()); + } template struct generator @@ -42,30 +48,20 @@ namespace test inline int generate(int const*) { -#if 0 - integer_generator_type gen; - boost::uniform_int<> dist(0, 1000); + static integer_generator_type gen; + static boost::uniform_int<> dist(0, 1000); static boost::variate_generator > vg(gen, dist); return vg(); -#else - using namespace std; - return rand(); -#endif } inline char generate(char const*) { -#if 0 - integer_generator_type gen; - boost::uniform_int dist(32, 127); + static integer_generator_type gen; + static boost::uniform_int dist(32, 127); static boost::variate_generator > vg(gen, dist); return vg(); -#else - using namespace std; - return rand() % (128 - 32) + 32; -#endif } inline std::string generate(std::string const*) @@ -80,35 +76,16 @@ namespace test for(int i = 0; i < length; ++i) result += char_gen(); - //std::generate_n( - // std::back_inserter(result), - // rand() % 10, - // char_gen); - return result; } float generate(float const*) { -#if 0 - real_generator_type gen; - boost::uniform_real dist; + static real_generator_type gen; + static boost::uniform_real dist; static boost::variate_generator > vg(gen, dist); return vg(); -#else - using namespace std; - return (double) rand() / (double) RAND_MAX; -#endif - } - - template std::pair generate( - std::pair const*) - { - static generator g1; - static generator g2; - - return std::pair(g1(), g2()); } } diff --git a/test/helpers/input_iterator.hpp b/test/helpers/input_iterator.hpp index 3731a408..06a1eade 100644 --- a/test/helpers/input_iterator.hpp +++ b/test/helpers/input_iterator.hpp @@ -11,7 +11,6 @@ namespace test { - // TODO: Make this a stricter input iterator. template struct input_iterator_adaptor : boost::iterator_adaptor< diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index 50dcfd2b..177a94f2 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -49,8 +49,10 @@ namespace test if(test::has_unique_keys::value && count != 1) BOOST_ERROR("Non-unique key."); - if(x1.count(key) != count) + if(x1.count(key) != count) { BOOST_ERROR("Incorrect output of count."); + std::cerr< +#include #include #include "./generators.hpp" -#include "./metafunctions.hpp" namespace test { template struct random_values - : public std::vector::type> + : public std::list { random_values(int count) { - typedef typename non_const_value_type::type value_type; + typedef typename X::value_type value_type; static test::generator gen; - this->reserve(count); std::generate_n(std::back_inserter(*this), count, gen); } }; diff --git a/test/helpers/strong.hpp b/test/helpers/strong.hpp index c3fcfaa5..9636c3e1 100644 --- a/test/helpers/strong.hpp +++ b/test/helpers/strong.hpp @@ -22,14 +22,6 @@ namespace test values_type values_; public: void store(X const& x) { - // TODO: Well, I shouldn't be disabling exceptions here. Instead - // the test runner should provide a method to the test which - // disables exceptions and calls this. - // - // Actually, the test runner could also keep track of how many times - // store is called in a run. Because it knows that in the next run - // the first n-1 stores are unnecessary - since no exceptions will - // be thrown for them. DISABLE_EXCEPTIONS; values_.clear(); values_.reserve(x.size()); diff --git a/test/helpers/tracker.hpp b/test/helpers/tracker.hpp index 217724b2..54b25db5 100644 --- a/test/helpers/tracker.hpp +++ b/test/helpers/tracker.hpp @@ -138,8 +138,7 @@ namespace test }; template - typename equals_to_compare::type create_compare( - Equals const& equals) + typename equals_to_compare::type create_compare(Equals const&) { typename equals_to_compare::type x; return x; diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index fe7e31a6..7b3911f6 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -17,12 +17,6 @@ #include #include "../helpers/fwd.hpp" -// TODO: -// a) This can only be included in compile unit. -// b) This stuff should be somewhere else. -// but I'm feeling too lazy right now (although sadly not lazy enough to -// avoid reinventing yet another wheel). - #define RUN_EXCEPTION_TESTS(test_seq, param_seq) \ BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_EXCEPTION_TESTS_OP, (test_seq)(param_seq)) @@ -213,6 +207,11 @@ namespace exception } } + ~object() { + tag1_ = -1; + tag2_ = -1; + } + object& operator=(object const& x) { SCOPE(object::operator=(object)) { @@ -346,7 +345,7 @@ namespace exception return *this; } - std::size_t operator()(object const& x1, object const& x2) const { + bool operator()(object const& x1, object const& x2) const { SCOPE(equal_to::operator()(object, object)) { EPOINT("Mock equal_to function."); } @@ -376,10 +375,6 @@ namespace exception } }; - // TODO: Need to track that same allocator is used to allocate, construct, - // deconstruct and destroy objects. Also, need to check that constructed - // objects are deconstructed (Boost.Test should take care of memory leaks - // for us). template class allocator { @@ -394,14 +389,14 @@ namespace exception template struct rebind { typedef allocator other; }; - explicit allocator(int t = 0) + explicit allocator(int = 0) { SCOPE(allocator::allocator()) { EPOINT("Mock allocator default constructor."); } } - template allocator(allocator const& x) + template allocator(allocator const&) { SCOPE(allocator::allocator()) { EPOINT("Mock allocator template copy constructor."); @@ -424,8 +419,11 @@ namespace exception return *this; } + // If address throws, then it can't be used in erase or the + // destructor, which is very limiting. I need to check up on + // this. + pointer address(reference r) { - // TODO: Is this no throw? Major problems if it isn't. //SCOPE(allocator::address(reference)) { // EPOINT("Mock allocator address function."); //} @@ -433,7 +431,6 @@ namespace exception } const_pointer address(const_reference r) { - // TODO: Is this no throw? Major problems if it isn't. //SCOPE(allocator::address(const_reference)) { // EPOINT("Mock allocator const address function."); //} @@ -469,7 +466,7 @@ namespace exception //return pointer(static_cast(::operator new(n * sizeof(T)))); } - void deallocate(pointer p, size_type n) + void deallocate(pointer p, size_type) { //::operator delete((void*) p); if(p) { @@ -495,13 +492,12 @@ namespace exception } }; + // It's pretty much impossible to write a compliant swap when these + // two can throw. So they don't. + template - inline bool operator==(allocator const& x, allocator const& y) + inline bool operator==(allocator const&, allocator const&) { - // TODO: I can't meet the exception requirements for swap if this - // throws. Does the standard specify that allocator comparisons can't - // throw? - // //SCOPE(operator==(allocator, allocator)) { // EPOINT("Mock allocator equality operator."); //} diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index 587b81fe..ee1d7aa2 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -47,19 +47,12 @@ namespace minimal { public: static hash create() { return hash(); } - // TODO: hash has to be default constructible for the default - // parameters. Maybe use an alternative version for testing - // other member functions. - // - // Or maybe it's required to be default constructible? - // The Container requirements include a default constructor. hash() {} hash(hash const&) {} - // TODO: Required to be assignable? hash& operator=(hash const&) { return *this; } ~hash() {} - std::size_t operator()(T const& x) const { return 0; } + std::size_t operator()(T const&) const { return 0; } }; template @@ -67,15 +60,8 @@ namespace minimal { public: static equal_to create() { return equal_to(); } - // TODO: equal_to has to be default constructible for the default - // parameters. Maybe use an alternative version for testing - // other member functions. - // - // Or maybe it's required to be default constructible? - // The Container requirements include a default constructor. equal_to() {} equal_to(equal_to const&) {} - // TODO: Required to be assignable? equal_to& operator=(equal_to const&) { return *this; } ~equal_to() {} @@ -173,9 +159,6 @@ namespace minimal bool operator>=(const_pointer const& x) const { return ptr_ >= x.ptr_; } }; - // TODO: Issue 560 suggests that an allocator doesn't have to have - // a default constructor. - // http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#560 template class allocator { @@ -207,7 +190,7 @@ namespace minimal return pointer(static_cast(::operator new(n * sizeof(T)))); } - void deallocate(pointer p, size_type n) + void deallocate(pointer p, size_type) { ::operator delete((void*) p.ptr_); } @@ -225,19 +208,19 @@ namespace minimal }; template - inline bool operator==(allocator const& x, allocator const& y) + inline bool operator==(allocator const&, allocator const&) { return true; } template - inline bool operator!=(allocator const& x, allocator const& y) + inline bool operator!=(allocator const&, allocator const&) { return false; } template - void swap(allocator& x, allocator& y) + void swap(allocator&, allocator&) { } } diff --git a/test/objects/test.hpp b/test/objects/test.hpp index ba61a68c..9f4702c0 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -32,6 +32,11 @@ namespace test public: explicit object(int t1 = 0, int t2 = 0) : tag1_(t1), tag2_(t2) {} + ~object() { + tag1_ = -1; + tag2_ = -1; + } + friend bool operator==(object const& x1, object const& x2) { return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; } @@ -222,7 +227,7 @@ namespace test void track_allocate(void *ptr, std::size_t n, std::size_t size, int tag) { if(n == 0) { - // TODO: This is unspecified - not undefined, so what to do? + BOOST_ERROR("Allocating 0 length array."); } else { ++count_allocations; @@ -238,7 +243,6 @@ namespace test if(pos == allocated_memory.end()) { BOOST_ERROR("Deallocating unknown pointer."); } else { - // TODO: Not exception safe. BOOST_TEST(pos->first.start == ptr); BOOST_TEST(pos->first.end == (char*) ptr + n * size); BOOST_TEST(pos->second.tag_ == tag); @@ -249,21 +253,18 @@ namespace test if(count_allocations > 0) --count_allocations; } - void track_construct(void* ptr, std::size_t size, int tag) + void track_construct(void* ptr, std::size_t /*size*/, int tag) { std::map::iterator pos = allocated_memory.find(memory_area(ptr, ptr)); if(pos == allocated_memory.end()) BOOST_ERROR("Constructing unknown pointer."); BOOST_TEST(pos->second.tag_ == tag); - //TODO: Track the number of allocations, and make sure the number - // of constructions doesn't exceed it. If you're feeling keen, - // perhaps track the individual objects in the array. ++count_constructions; ++pos->second.constructed_; } - void track_destroy(void* ptr, std::size_t size, int tag) + void track_destroy(void* ptr, std::size_t /*size*/, int tag) { std::map::iterator pos = allocated_memory.find(memory_area(ptr, ptr)); @@ -302,7 +303,6 @@ namespace test allocator(allocator const& x) : tag_(x.tag_) { detail::allocator_ref(); } ~allocator() { detail::allocator_unref(); } - // TODO: Shall I check these? pointer address(reference r) { return pointer(&r); } const_pointer address(const_reference r) { return const_pointer(&r); } diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 68d54582..ffa20a42 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -19,6 +19,7 @@ test-suite unordered-tests [ run assign_tests.cpp ] [ run insert_tests.cpp ] [ run erase_tests.cpp ] + [ run erase_equiv_tests.cpp ] [ run find_tests.cpp ] [ run bucket_tests.cpp ] [ run load_factor_tests.cpp ] diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index 434e1c3e..ca36910a 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -61,7 +61,6 @@ void assign_tests2(T* = 0) std::cerr<<"assign_tests2.1\n"; { - // TODO: Need to generate duplicates... test::random_values v(1000); T x1(v.begin(), v.end(), 0, hf1, eq1); T x2(0, hf2, eq2); @@ -73,7 +72,6 @@ void assign_tests2(T* = 0) std::cerr<<"assign_tests2.2\n"; { - // TODO: Need to generate duplicates... test::random_values v1(100), v2(100); T x1(v1.begin(), v1.end(), 0, hf1, eq1, al1); T x2(v2.begin(), v2.end(), 0, hf2, eq2, al2); diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 48d7be23..2fb27911 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -11,6 +11,7 @@ #include "../helpers/tracker.hpp" #include "../helpers/equivalent.hpp" #include "../helpers/input_iterator.hpp" +#include "../helpers/invariants.hpp" #include @@ -29,6 +30,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); } std::cerr<<"Construct 2\n"; @@ -39,6 +41,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); } std::cerr<<"Construct 3\n"; @@ -49,6 +52,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); } std::cerr<<"Construct 4\n"; @@ -58,6 +62,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); } std::cerr<<"Construct 5\n"; @@ -69,6 +74,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); test::check_container(x, v); + test::check_equivalent_keys(x); } std::cerr<<"Construct 6\n"; @@ -80,6 +86,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); test::check_container(x, v); + test::check_equivalent_keys(x); } std::cerr<<"Construct 7\n"; @@ -91,6 +98,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); test::check_container(x, v); + test::check_equivalent_keys(x); } std::cerr<<"Construct 8\n"; @@ -101,6 +109,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); test::check_container(x, v); + test::check_equivalent_keys(x); } std::cerr<<"Construct 9\n"; @@ -111,6 +120,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); } std::cerr<<"Construct 10\n"; @@ -122,6 +132,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); test::check_container(x, v); + test::check_equivalent_keys(x); } } @@ -145,6 +156,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq1)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); } std::cerr<<"Construct 2\n"; @@ -155,6 +167,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); } std::cerr<<"Construct 3\n"; @@ -165,6 +178,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(test::equivalent(x.key_eq(), eq1)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); test::check_container(x, v); + test::check_equivalent_keys(x); } std::cerr<<"Construct 4\n"; @@ -176,6 +190,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); test::check_container(x, v); + test::check_equivalent_keys(x); } @@ -186,6 +201,8 @@ void constructor_tests2(T* = 0) T y(x.begin(), x.end(), 0, hf1, eq1, al2); test::check_container(x, v); test::check_container(y, x); + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); } std::cerr<<"Construct 6\n"; @@ -195,6 +212,8 @@ void constructor_tests2(T* = 0) T y(x.begin(), x.end(), 0, hf, eq); test::check_container(x, v); test::check_container(y, x); + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); } std::cerr<<"Construct 7\n"; @@ -204,6 +223,8 @@ void constructor_tests2(T* = 0) T y(x.begin(), x.end(), 0, hf2, eq2); test::check_container(x, v); test::check_container(y, x); + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); } std::cerr<<"Construct 8 - from input iterator\n"; @@ -213,9 +234,26 @@ void constructor_tests2(T* = 0) T y(test::input_iterator(x.begin()), test::input_iterator(x.end()), 0, hf2, eq2); test::check_container(x, v); test::check_container(y, x); + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); } } +template +void map_constructor_test(T* = 0) +{ + std::cerr<<"map_constructor_test\n"; + + typedef std::list > list; + test::random_values v(1000); + list l(v.begin(), v.end()); + + T x(l.begin(), l.end()); + + test::check_container(x, v); + test::check_equivalent_keys(x); +} + int main() { std::cerr<<"Test1 unordered_set\n"; @@ -245,5 +283,10 @@ int main() std::cerr<<"Test2 unordered_multimap\n"; constructor_tests2((boost::unordered_multimap >*) 0); + std::cerr<<"Map Test unordered_map\n"; + map_constructor_test((boost::unordered_map >*) 0); + std::cerr<<"Map Test unordered_multimap\n"; + map_constructor_test((boost::unordered_multimap >*) 0); + return boost::report_errors(); } diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index 6111c3ba..2a0671f4 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -67,7 +67,6 @@ void copy_construct_tests2(T* ptr = 0) typename T::allocator_type al(1); { - // TODO: I could check how many buckets y has, it should be lower (QOI issue). T x(10000, hf, eq, al); T y(x); BOOST_TEST(y.empty()); @@ -79,7 +78,6 @@ void copy_construct_tests2(T* ptr = 0) } { - // TODO: Invariant checks are especially important here. test::random_values v(1000); T x(v.begin(), v.end(), 0, hf, eq, al); diff --git a/test/unordered/erase_equiv_tests.cpp b/test/unordered/erase_equiv_tests.cpp new file mode 100644 index 00000000..780ffae4 --- /dev/null +++ b/test/unordered/erase_equiv_tests.cpp @@ -0,0 +1,195 @@ + +// Copyright 2006 Daniel James. +// Distributed under 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) + +// The code for erasing elements from containers with equivalent keys is very +// hairy with several tricky edge cases - so explicitly test each one. + +#include +#include +#include +#include +#include +#include +#include +#include "../objects/test.hpp" + +struct write_pair_type +{ + template + void operator()(std::pair const& x) const + { + std::cout<<"("< +void write_container(Container const& x) +{ + std::for_each(x.begin(), x.end(), write_pair); + std::cout<<"\n"; +} + +// Make everything collide - for testing erase in a single bucket. +struct collision_hash +{ + int operator()(int) const { return 0; } +}; + +// For testing erase in 2 buckets. +struct collision2_hash +{ + int operator()(int x) const { return x & 1; } +}; + +typedef boost::unordered_multimap, + test::allocator > > collide_map; +typedef boost::unordered_multimap, + test::allocator > > collide_map2; +typedef collide_map::value_type pair; +typedef std::list list; + + +void empty_range_tests() +{ + collide_map x; + x.erase(x.begin(), x.end()); + x.erase(x.begin(), x.begin()); + x.erase(x.end(), x.end()); +} + +void single_item_tests() +{ + list init; + init.push_back(pair(1,1)); + + collide_map x(init.begin(), init.end()); + x.erase(x.begin(), x.begin()); + BOOST_TEST(x.count(1) == 1 && x.size() == 1); + x.erase(x.end(), x.end()); + BOOST_TEST(x.count(1) == 1 && x.size() == 1); + x.erase(x.begin(), x.end()); + BOOST_TEST(x.count(1) == 0 && x.size() == 0); +} + +void two_equivalent_item_tests() +{ + list init; + init.push_back(pair(1,1)); + init.push_back(pair(1,2)); + + { + collide_map x(init.begin(), init.end()); + x.erase(x.begin(), x.end()); + BOOST_TEST(x.count(1) == 0 && x.size() == 0); + } + + { + collide_map x(init.begin(), init.end()); + int value = boost::next(x.begin())->second; + x.erase(x.begin(), boost::next(x.begin())); + BOOST_TEST(x.count(1) == 1 && x.size() == 1 && + x.begin()->first == 1 && x.begin()->second == value); + } + + { + collide_map x(init.begin(), init.end()); + int value = x.begin()->second; + x.erase(boost::next(x.begin()), x.end()); + BOOST_TEST(x.count(1) == 1 && x.size() == 1 && + x.begin()->first == 1 && x.begin()->second == value); + } +} + +// At this point I got bored and wrote more automated tests... + +template +bool compare(Range1 const& x, Range2 const& y) +{ + list a(x.begin(), x.end()); + list b(y.begin(), y.end()); + a.sort(); + b.sort(); + return a == b; +} + +template +bool general_erase_range_test(Container& x, int start, int end) +{ + list l(x.begin(), x.end()); + l.erase(boost::next(l.begin(), start), boost::next(l.begin(), end)); + x.erase(boost::next(x.begin(), start), boost::next(x.begin(), end)); + return compare(l, x); +} + +template +void erase_subrange_tests(Container const& x) +{ + for(std::size_t length = 0; length < x.size(); ++length) { + for(std::size_t position = 0; position < x.size() - length; ++position) { + Container y(x); + list init(y.begin(), y.end()); + if(!general_erase_range_test(y, position, position + length)) { + BOOST_ERROR("general_erase_range_test failed."); + std::cout<<"Erase: ["< +void x_by_y_erase_range_tests(Container*, int values, int duplicates) +{ + Container y; + + for(int i = 0; i < values; ++i) { + for(int j = 0; j < duplicates; ++j) { + y.insert(pair(i, j)); + } + } + + std::cout<<"Values: "< +void exhaustive_erase_tests(Container* x, int num_values, + int num_duplicated) +{ + for(int i = 0; i < num_values; ++i) { + for(int j = 0; j < num_duplicated; ++j) { + x_by_y_erase_range_tests(x, i, j); + } + } +} + +void exhaustive_collide_tests() +{ + std::cout<<"exhaustive_collide_tests:\n"; + collide_map m; + exhaustive_erase_tests((collide_map*) 0, 4, 4); + std::cout<<"\n"; +} + +void exhaustive_collide2_tests() +{ + std::cout<<"exhaustive_collide2_tests:\n"; + exhaustive_erase_tests((collide_map2*) 0, 8, 4); + std::cout<<"\n"; +} + +int main() +{ + empty_range_tests(); + single_item_tests(); + two_equivalent_item_tests(); + exhaustive_collide_tests(); + exhaustive_collide2_tests(); + + return boost::report_errors(); +} diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index d6cb1408..274471f9 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -105,8 +105,6 @@ void erase_tests1(Container* = 0) BOOST_TEST(x.erase(x.begin(), x.end()) == x.begin()); } - // TODO: More range erase tests. - std::cerr<<"clear().\n"; { test::random_values v(500); diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index c3664d34..687866b8 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -196,8 +196,8 @@ void insert_tests2(X* = 0) test::random_values v(1000); x.insert(v.begin(), v.end()); - test::check_container(x, v); + test::check_container(x, v); test::check_equivalent_keys(x); } @@ -218,6 +218,8 @@ void insert_tests2(X* = 0) template void map_tests(X* = 0) { + std::cerr<<"map tests.\n"; + X x; test::ordered tracker = test::create_ordered(x); @@ -240,6 +242,20 @@ void map_tests(X* = 0) test::check_equivalent_keys(x); } +template +void associative_insert_range_test(X* = 0) +{ + std::cerr<<"associative_insert_range_test\n"; + + typedef std::list > list; + test::random_values v(1000); + list l(v.begin(), v.end()); + + X x; x.insert(l.begin(), l.end()); + + test::check_equivalent_keys(x); +} + int main() { unique_insert_tests1((boost::unordered_set*) 0); @@ -265,5 +281,8 @@ int main() map_tests((boost::unordered_map*) 0); map_tests((boost::unordered_map >*) 0); + associative_insert_range_test((boost::unordered_map >*) 0); + associative_insert_range_test((boost::unordered_multimap >*) 0); + return boost::report_errors(); } From 8d8f9e1942c03945d38a32bef432637ec90222aa Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 3 Dec 2006 23:08:17 +0000 Subject: [PATCH 056/175] Add a test that unnecessary copies aren't made of objects inserted. Make the exception tests track the allocator usage. Speed up the exception tests a bit. [SVN r3490] --- test/exception/Jamfile.v2 | 3 +- test/exception/erase_tests.cpp | 2 +- test/exception/swap_tests.cpp | 2 +- test/objects/exception.hpp | 194 ++++++++++++++++++++-- test/objects/test.hpp | 15 +- test/unordered/Jamfile.v2 | 1 + test/unordered/swap_tests.cpp | 10 +- test/unordered/unnecessary_copy_tests.cpp | 49 ++++++ 8 files changed, 251 insertions(+), 25 deletions(-) create mode 100644 test/unordered/unnecessary_copy_tests.cpp diff --git a/test/exception/Jamfile.v2 b/test/exception/Jamfile.v2 index 7f32840e..e099c772 100644 --- a/test/exception/Jamfile.v2 +++ b/test/exception/Jamfile.v2 @@ -5,7 +5,8 @@ import testing ; -alias framework : /boost/test//boost_unit_test_framework/speed ; +#alias framework : /boost/test//boost_unit_test_framework/speed ; +alias framework : /boost/test//boost_unit_test_framework ; project unordered-test/exception-tests : requirements diff --git a/test/exception/erase_tests.cpp b/test/exception/erase_tests.cpp index c1709a9d..ef1fd2f4 100644 --- a/test/exception/erase_tests.cpp +++ b/test/exception/erase_tests.cpp @@ -27,7 +27,7 @@ struct erase_test_base : public test::exception_base void check(T const& x) const { std::string scope(test::scope); - BOOST_CHECK(scope.find("hash::") != std::string::npos || + HASH_CHECK(scope.find("hash::") != std::string::npos || scope.find("equal_to::") != std::string::npos || scope == "operator==(object, object)"); diff --git a/test/exception/swap_tests.cpp b/test/exception/swap_tests.cpp index 66747e08..5fe64763 100644 --- a/test/exception/swap_tests.cpp +++ b/test/exception/swap_tests.cpp @@ -24,7 +24,7 @@ struct self_swap_base : public test::exception_base std::string scope(test::scope); #if BOOST_UNORDERED_SWAP_METHOD != 2 - BOOST_CHECK( + HASH_CHECK( scope == "hash::operator(hash)" || scope == "hash::operator=(hash)" || scope == "equal_to::operator(equal_to)" || diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index 7b3911f6..ff00bbe8 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -16,6 +16,7 @@ #include #include #include "../helpers/fwd.hpp" +#include #define RUN_EXCEPTION_TESTS(test_seq, param_seq) \ BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_EXCEPTION_TESTS_OP, (test_seq)(param_seq)) @@ -37,7 +38,6 @@ } #define SCOPE(scope_name) \ - BOOST_ITEST_SCOPE(scope_name); \ for(::test::scope_guard unordered_test_guard( \ BOOST_STRINGIZE(scope_name)); \ !unordered_test_guard.dismissed(); \ @@ -53,6 +53,8 @@ #define DISABLE_EXCEPTIONS \ ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false) +#define HASH_CHECK(test) if(!(test)) BOOST_ERROR(BOOST_STRINGIZE(test)) + namespace test { static char const* scope = ""; bool exceptions_enabled = false; @@ -175,6 +177,161 @@ namespace test { namespace exception { + namespace detail + { + // This annoymous namespace won't cause ODR violations as I won't + // be linking multiple translation units together. I'll probably + // move this into a cpp file before a full release, but for now it's + // the most convenient way. + namespace + { + template + struct malloc_allocator + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef malloc_allocator other; }; + + malloc_allocator() {} + template malloc_allocator(malloc_allocator const& x) {} + malloc_allocator(malloc_allocator const& x) {} + + pointer address(reference r) { return &r; } + const_pointer address(const_reference r) { return &r; } + + pointer allocate(size_type n) { + return static_cast(malloc(n * sizeof(T))); + } + + pointer allocate(size_type n, const_pointer u) { return allocate(n); } + void deallocate(pointer p, size_type n) { free(p); } + void construct(pointer p, T const& t) { new(p) T(t); } + void destroy(pointer p) { p->~T(); } + + size_type max_size() const { + return (std::numeric_limits::max)(); + } + + bool operator==(malloc_allocator const& x) const { return true; } + bool operator!=(malloc_allocator const& x) const { return false; } + }; + + struct memory_area { + void const* start; + void const* end; + + memory_area(void const* s, void const* e) + : start(s), end(e) + { + } + + // This is a bit dodgy as it defines overlapping + // areas as 'equal', so this isn't a total ordering. + // But it is for non-overlapping memory regions - which + // is what'll be stored. + // + // All searches will be for areas entirely contained by + // a member of the set - so it should find the area that contains + // the region that is searched for. + bool operator<(memory_area const& other) const { + return end < other.start; + } + }; + + struct memory_track { + explicit memory_track(int tag = -1) : + tag_(tag) {} + + int tag_; + }; + + typedef std::map, + malloc_allocator > > + allocated_memory_type; + allocated_memory_type allocated_memory; + unsigned int count_allocators = 0; + unsigned int count_allocations = 0; + unsigned int count_constructions = 0; + } + + void allocator_ref() + { + if(count_allocators == 0) { + count_allocations = 0; + count_constructions = 0; + allocated_memory.clear(); + } + ++count_allocators; + } + + void allocator_unref() + { + HASH_CHECK(count_allocators > 0); + if(count_allocators > 0) { + --count_allocators; + if(count_allocators == 0) { + bool no_allocations_left = (count_allocations == 0); + bool no_constructions_left = (count_constructions == 0); + bool allocated_memory_empty = allocated_memory.empty(); + + // Clearing the data before the checks terminate the tests. + count_allocations = 0; + count_constructions = 0; + allocated_memory.clear(); + + HASH_CHECK(no_allocations_left); + HASH_CHECK(no_constructions_left); + HASH_CHECK(allocated_memory_empty); + } + } + } + + void track_allocate(void *ptr, std::size_t n, std::size_t size, int tag) + { + if(n == 0) { + BOOST_ERROR("Allocating 0 length array."); + } + else { + ++count_allocations; + allocated_memory[memory_area(ptr, (char*) ptr + n * size)] = + memory_track(tag); + } + } + + void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag) + { + allocated_memory_type::iterator pos + = allocated_memory.find(memory_area(ptr, ptr)); + if(pos == allocated_memory.end()) { + BOOST_ERROR("Deallocating unknown pointer."); + } else { + HASH_CHECK(pos->first.start == ptr); + HASH_CHECK(pos->first.end == (char*) ptr + n * size); + HASH_CHECK(pos->second.tag_ == tag); + allocated_memory.erase(pos); + } + HASH_CHECK(count_allocations > 0); + if(count_allocations > 0) --count_allocations; + } + + void track_construct(void* ptr, std::size_t /*size*/, int tag) + { + ++count_constructions; + } + + void track_destroy(void* ptr, std::size_t /*size*/, int tag) + { + HASH_CHECK(count_constructions > 0); + if(count_constructions > 0) --count_constructions; + } + } + class object; class hash; class equal_to; @@ -379,6 +536,7 @@ namespace exception class allocator { public: + int tag_; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef T* pointer; @@ -389,32 +547,38 @@ namespace exception template struct rebind { typedef allocator other; }; - explicit allocator(int = 0) + explicit allocator(int t = 0) : tag_(t) { SCOPE(allocator::allocator()) { EPOINT("Mock allocator default constructor."); } + detail::allocator_ref(); } - template allocator(allocator const&) + template allocator(allocator const& x) : tag_(x.tag_) { SCOPE(allocator::allocator()) { EPOINT("Mock allocator template copy constructor."); } + detail::allocator_ref(); } - allocator(allocator const&) + allocator(allocator const& x) : tag_(x.tag_) { SCOPE(allocator::allocator()) { EPOINT("Mock allocator copy constructor."); } + detail::allocator_ref(); } - ~allocator() {} + ~allocator() { + detail::allocator_unref(); + } - allocator& operator=(allocator const&) { + allocator& operator=(allocator const& x) { SCOPE(allocator::allocator()) { EPOINT("Mock allocator assignment operator."); + tag_ = x.tag_; } return *this; } @@ -446,6 +610,7 @@ namespace exception ptr = (T*) malloc(n * sizeof(T)); if(!ptr) throw std::bad_alloc(); } + detail::track_allocate((void*) ptr, n, sizeof(T), tag_); return pointer(ptr); //return pointer(static_cast(::operator new(n * sizeof(T)))); @@ -461,15 +626,17 @@ namespace exception ptr = (T*) malloc(n * sizeof(T)); if(!ptr) throw std::bad_alloc(); } + detail::track_allocate((void*) ptr, n, sizeof(T), tag_); return pointer(ptr); //return pointer(static_cast(::operator new(n * sizeof(T)))); } - void deallocate(pointer p, size_type) + void deallocate(pointer p, size_type n) { //::operator delete((void*) p); if(p) { + detail::track_deallocate((void*) p, n, sizeof(T), tag_); using namespace std; free(p); } @@ -480,9 +647,13 @@ namespace exception EPOINT("Mock allocator construct function."); new(p) T(t); } + detail::track_construct((void*) p, sizeof(T), tag_); } - void destroy(pointer p) { p->~T(); } + void destroy(pointer p) { + detail::track_destroy((void*) p, sizeof(T), tag_); + p->~T(); + } size_type max_size() const { SCOPE(allocator::construct(pointer, T)) { @@ -496,12 +667,12 @@ namespace exception // two can throw. So they don't. template - inline bool operator==(allocator const&, allocator const&) + inline bool operator==(allocator const& x, allocator const& y) { //SCOPE(operator==(allocator, allocator)) { // EPOINT("Mock allocator equality operator."); //} - return true; + return x.tag_ == y.tag_; } template @@ -510,9 +681,10 @@ namespace exception //SCOPE(operator!=(allocator, allocator)) { // EPOINT("Mock allocator inequality operator."); //} - return false; + return x.tag_ != y.tag_; } } } #endif + diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 9f4702c0..1803b2ac 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -154,6 +154,10 @@ namespace test namespace detail { + // This annoymous namespace won't cause ODR violations as I won't + // be linking multiple translation units together. I'll probably + // move this into a cpp file before a full release, but for now it's + // the most convenient way. namespace { struct memory_area { void const* start; @@ -186,7 +190,8 @@ namespace test int tag_; }; - std::map allocated_memory; + typedef std::map allocated_memory_type; + allocated_memory_type allocated_memory; unsigned int count_allocators = 0; unsigned int count_allocations = 0; unsigned int count_constructions = 0; @@ -238,7 +243,7 @@ namespace test void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag) { - std::map::iterator pos + allocated_memory_type::iterator pos = allocated_memory.find(memory_area(ptr, ptr)); if(pos == allocated_memory.end()) { BOOST_ERROR("Deallocating unknown pointer."); @@ -255,7 +260,7 @@ namespace test void track_construct(void* ptr, std::size_t /*size*/, int tag) { - std::map::iterator pos + allocated_memory_type::iterator pos = allocated_memory.find(memory_area(ptr, ptr)); if(pos == allocated_memory.end()) BOOST_ERROR("Constructing unknown pointer."); @@ -266,7 +271,7 @@ namespace test void track_destroy(void* ptr, std::size_t /*size*/, int tag) { - std::map::iterator pos + allocated_memory_type::iterator pos = allocated_memory.find(memory_area(ptr, ptr)); if(pos == allocated_memory.end()) BOOST_ERROR("Destroying unknown pointer."); @@ -349,7 +354,7 @@ namespace test return tag_ != x.tag_; } }; - + template bool equivalent_impl(allocator const& x, allocator const& y, test::derived_type) { return x == y; diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index ffa20a42..f69c158a 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -18,6 +18,7 @@ test-suite unordered-tests [ run copy_tests.cpp ] [ run assign_tests.cpp ] [ run insert_tests.cpp ] + [ run unnecessary_copy_tests.cpp ] [ run erase_tests.cpp ] [ run erase_equiv_tests.cpp ] [ run find_tests.cpp ] diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp index 9473c7a0..35d70eb2 100644 --- a/test/unordered/swap_tests.cpp +++ b/test/unordered/swap_tests.cpp @@ -74,11 +74,10 @@ void swap_tests2(X* ptr = 0) X x(v.begin(), v.end(), 0, hasher(1), key_equal(1)); X y(0, hasher(2), key_equal(2)); swap_test_impl(x, y); - swap_test_impl(x, y); } { - test::random_values vx(1000), vy(1000); + test::random_values vx(100), vy(50); X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1)); X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2)); swap_test_impl(x, y); @@ -87,7 +86,7 @@ void swap_tests2(X* ptr = 0) #if BOOST_UNORDERED_SWAP_METHOD == 1 { - test::random_values vx(1000), vy(1000); + test::random_values vx(100), vy(50); X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); try { @@ -97,15 +96,14 @@ void swap_tests2(X* ptr = 0) } #else { - test::random_values vx(1000), vy(1000); + test::random_values vx(50), vy(100); X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); swap_test_impl(x, y); - swap_test_impl(x, y); } { - test::random_values vx(1000), vy(1000); + test::random_values vx(100), vy(100); X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1), allocator_type(1)); X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2), allocator_type(2)); swap_test_impl(x, y); diff --git a/test/unordered/unnecessary_copy_tests.cpp b/test/unordered/unnecessary_copy_tests.cpp new file mode 100644 index 00000000..3991d1f2 --- /dev/null +++ b/test/unordered/unnecessary_copy_tests.cpp @@ -0,0 +1,49 @@ + +// Copyright 2006 Daniel James. +// Distributed under 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) + +#include +#include +#include + +struct count_copies +{ + static int count; + count_copies() { ++count; } + count_copies(count_copies const& x) { ++count; } +private: + count_copies& operator=(count_copies const&); +}; + +std::size_t hash_value(count_copies const& x) { + return 0; +} + +bool operator==(count_copies const& x, count_copies const& y) { + return true; +} + +int count_copies::count; + +template +void unnecessary_copy_test(T*) +{ + count_copies::count = 0; + T x; + typename T::value_type a; + BOOST_TEST(count_copies::count == 1); + x.insert(a); + BOOST_TEST(count_copies::count == 2); +} + + +int main() +{ + unnecessary_copy_test((boost::unordered_set*) 0); + unnecessary_copy_test((boost::unordered_multiset*) 0); + unnecessary_copy_test((boost::unordered_map*) 0); + unnecessary_copy_test((boost::unordered_multimap*) 0); + + return boost::report_errors(); +} From 70797d159a203330aac72a7839c0b83784dfbc34 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 3 Dec 2006 23:11:18 +0000 Subject: [PATCH 057/175] New version of the hash table implementation. Hopefully a little clearer with less messing around with allocators and pointers. [SVN r3491] --- .../unordered/detail/hash_table_impl.hpp | 547 +++++++++--------- 1 file changed, 287 insertions(+), 260 deletions(-) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 9d221635..55bd5db7 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -33,7 +33,7 @@ namespace boost { { public: class node; - class node_base; + class node_links; class bucket; typedef std::size_t size_type; @@ -43,8 +43,8 @@ namespace boost { boost::unordered_detail::rebind_wrap::type node_allocator; typedef BOOST_DEDUCED_TYPENAME - boost::unordered_detail::rebind_wrap::type - node_base_allocator; + boost::unordered_detail::rebind_wrap::type + node_links_allocator; typedef BOOST_DEDUCED_TYPENAME boost::unordered_detail::rebind_wrap::type bucket_allocator; @@ -62,22 +62,22 @@ namespace boost { { bucket& operator=(bucket const&); public: - bucket_ptr next_; + node_ptr first_; - bucket() : next_() + bucket() : first_() { - BOOST_HASH_MSVC_RESET_PTR(next_); + BOOST_HASH_MSVC_RESET_PTR(first_); } - bucket(bucket const& x) : next_(x.next_) + bucket(bucket const& x) : first_(x.first_) { // Only copy construct when allocating. - BOOST_ASSERT(!x.next_); + BOOST_ASSERT(!x.first_); } bool empty() const { - return !this->next_; + return !this->first_; } }; @@ -85,24 +85,31 @@ namespace boost { // // all no throw - class node_base : public bucket + class node_links { -#if BOOST_UNORDERED_HASH_EQUIVALENT public: - node_base() : group_prev_() +#if BOOST_UNORDERED_HASH_EQUIVALENT + node_links() : next_(), group_prev_() { + BOOST_HASH_MSVC_RESET_PTR(next_); BOOST_HASH_MSVC_RESET_PTR(group_prev_); } - bucket_ptr group_prev_; + node_ptr next_; + node_ptr group_prev_; +#else + node_links() : next_() + { + BOOST_HASH_MSVC_RESET_PTR(next_); + } + + node_ptr next_; #endif }; - class node : public node_base + struct node { - public: - node(value_type const& v) : node_base(), value_(v) {} - + node_links links_; value_type value_; }; @@ -115,85 +122,95 @@ namespace boost { node_allocator node_alloc_; bucket_allocator bucket_alloc_; value_allocator value_alloc_; - node_base_allocator node_base_alloc_; + node_links_allocator node_links_alloc_; - allocators(node_allocator const& a) + allocators(value_allocator const& a) : node_alloc_(a), bucket_alloc_(a), - value_alloc_(a), node_base_alloc_(a) + value_alloc_(a), node_links_alloc_(a) {} - void destroy(bucket_ptr ptr) + void destroy(node_ptr n) { - node_ptr n(node_alloc_.address(static_cast(*ptr))); value_alloc_.destroy(value_alloc_.address(n->value_)); - node_base_alloc_.destroy(node_base_alloc_.address(*n)); + node_links_alloc_.destroy(node_links_alloc_.address(n->links_)); node_alloc_.deallocate(n, 1); } + + void swap(allocators& x) + { + hash_swap(node_alloc_, x.node_alloc_); + hash_swap(bucket_alloc_, x.bucket_alloc_); + hash_swap(value_alloc_, x.value_alloc_); + hash_swap(node_links_alloc_, x.node_links_alloc_); + } + + bool operator==(allocators const& x) + { + return value_alloc_ == x.value_alloc_; + } }; class node_constructor { allocators& allocators_; - node_base_allocator node_base_alloc_; - value_allocator value_alloc_; - node_ptr ptr_; + node_ptr node_; bool value_constructed_; - bool node_base_constructed_; + bool node_links_constructed_; public: node_constructor(allocators& a) - : allocators_(a), node_base_alloc_(a.node_alloc_), - value_alloc_(a.node_alloc_), - ptr_(), value_constructed_(false), node_base_constructed_(false) + : allocators_(a), + node_(), value_constructed_(false), node_links_constructed_(false) { - BOOST_HASH_MSVC_RESET_PTR(ptr_); + BOOST_HASH_MSVC_RESET_PTR(node_); } ~node_constructor() { - if (ptr_) { + if (node_) { if (value_constructed_) - value_alloc_.destroy( - value_alloc_.address(ptr_->value_)); - if (node_base_constructed_) - node_base_alloc_.destroy( - node_base_alloc_.address(*ptr_)); + allocators_.value_alloc_.destroy( + allocators_.value_alloc_.address(node_->value_)); + if (node_links_constructed_) + allocators_.node_links_alloc_.destroy( + allocators_.node_links_alloc_.address(node_->links_)); - allocators_.node_alloc_.deallocate(ptr_, 1); + allocators_.node_alloc_.deallocate(node_, 1); } } template void construct(V const& v) { - BOOST_ASSERT(!ptr_); - value_constructed_ = node_base_constructed_ = false; + BOOST_ASSERT(!node_); + value_constructed_ = node_links_constructed_ = false; - ptr_ = allocators_.node_alloc_.allocate(1); + node_ = allocators_.node_alloc_.allocate(1); - node_base_alloc_.construct(node_base_alloc_.address( - *ptr_), node_base()); - node_base_constructed_ = true; + allocators_.node_links_alloc_.construct( + allocators_.node_links_alloc_.address(node_->links_), + node_links()); + node_links_constructed_ = true; - value_alloc_.construct(value_alloc_.address( - ptr_->value_), v); + allocators_.value_alloc_.construct( + allocators_.value_alloc_.address(node_->value_), v); value_constructed_ = true; } node_ptr get() const { - BOOST_ASSERT(ptr_); - return ptr_; + BOOST_ASSERT(node_); + return node_; } // no throw - bucket_ptr release() + node_ptr release() { - node_ptr p = ptr_; - unordered_detail::reset(ptr_); - return allocators_.bucket_alloc_.address(*p); + node_ptr n = node_; + unordered_detail::reset(node_); + return n; } private: @@ -204,87 +221,75 @@ namespace boost { // Methods for navigating groups of elements with equal keys. #if BOOST_UNORDERED_HASH_EQUIVALENT - static bucket_ptr& prev_in_group(bucket_ptr p) { - return static_cast(*p).group_prev_; + static node_ptr& prev_in_group(node_ptr n) { + return n->links_.group_prev_; } // pre: Must be pointing to the first node in a group. - static bucket_ptr last_in_group(bucket_ptr p) { - BOOST_ASSERT(p && p != prev_in_group(p)->next_); - return prev_in_group(p); + static node_ptr last_in_group(node_ptr n) { + BOOST_ASSERT(n && n != prev_in_group(n)->links_.next_); + return prev_in_group(n); } // pre: Must be pointing to the first node in a group. - static bucket_ptr& next_group(bucket_ptr p) { - BOOST_ASSERT(p && p != prev_in_group(p)->next_); - return prev_in_group(p)->next_; + static node_ptr& next_group(node_ptr n) { + BOOST_ASSERT(n && n != prev_in_group(n)->links_.next_); + return prev_in_group(n)->links_.next_; } #else - static bucket_ptr last_in_group(bucket_ptr p) { - return p; + static node_ptr last_in_group(node_ptr n) { + return n; } - static bucket_ptr& next_group(bucket_ptr p) { - BOOST_ASSERT(p); - return p->next_; + static node_ptr& next_group(node_ptr n) { + BOOST_ASSERT(n); + return n->links_.next_; } #endif - // pre: Must be pointing to a node - static node& get_node(bucket_ptr p) { - BOOST_ASSERT(p); - return static_cast(*p); - } - - // pre: Must be pointing to a node - static reference get_value(bucket_ptr p) { - BOOST_ASSERT(p); - return static_cast(*p).value_; - } - class local_iterator_base { public: - bucket_ptr node_pointer_; + node_ptr node_; local_iterator_base() - : node_pointer_() + : node_() { - BOOST_HASH_MSVC_RESET_PTR(node_pointer_); + BOOST_HASH_MSVC_RESET_PTR(node_); } - explicit local_iterator_base(bucket_ptr n) - : node_pointer_(n) {} + explicit local_iterator_base(node_ptr n) + : node_(n) {} bool not_finished() const { - return node_pointer_ ? true : false; + return node_ ? true : false; } bool operator==(local_iterator_base const& x) const { - return node_pointer_ == x.node_pointer_; + return node_ == x.node_; } bool operator!=(local_iterator_base const& x) const { - return node_pointer_ != x.node_pointer_; + return node_ != x.node_; } reference operator*() const { - return get_value(node_pointer_); + return node_->value_; } void increment() { - BOOST_ASSERT(node_pointer_); - node_pointer_ = node_pointer_->next_; + BOOST_ASSERT(node_); + node_ = node_->links_.next_; } void next_group() { - node_pointer_ = HASH_TABLE_DATA::next_group(node_pointer_); + node_ = HASH_TABLE_DATA::next_group(node_); } }; @@ -298,9 +303,9 @@ namespace boost { : bucket_(), local_() {} explicit iterator_base(bucket_ptr b) - : bucket_(b), local_(b->next_) {} + : bucket_(b), local_(b->first_) {} - iterator_base(bucket_ptr b, bucket_ptr n) + iterator_base(bucket_ptr b, node_ptr n) : bucket_(b), local_(n) {} iterator_base(bucket_ptr b, local_iterator_base const& it) @@ -326,9 +331,16 @@ namespace boost { BOOST_ASSERT(bucket_); local_.increment(); - while (!local_.node_pointer_) { - ++bucket_; - local_ = local_iterator_base(bucket_->next_); + if(!local_.node_) { + do { + ++bucket_; + } while(!bucket_->first_); + + // If at the end of the buckets... + if(bucket_->first_ == (bucket_ + 1)->first_) + unordered_detail::reset(local_); + else + local_ = local_iterator_base(bucket_->first_); } } }; @@ -343,7 +355,7 @@ namespace boost { // Constructors/Deconstructor - HASH_TABLE_DATA(size_type n, node_allocator const& a) + HASH_TABLE_DATA(size_type n, value_allocator const& a) : allocators_(a), buckets_(), bucket_count_(next_prime(n)), cached_begin_bucket_(), size_(0) @@ -353,21 +365,18 @@ namespace boost { allocator_array_constructor constructor(allocators_.bucket_alloc_); - // Creates an extra bucket to act as a sentinel. - constructor.construct(bucket(), bucket_count_ + 1); + // Creates two extra buckets to act as sentinels. + constructor.construct(bucket(), bucket_count_ + 2); cached_begin_bucket_ = constructor.get() + bucket_count_; - // Set up the sentinel. - cached_begin_bucket_->next_ = cached_begin_bucket_; - // Only release the buckets once everything is successfully // done. buckets_ = constructor.release(); } - HASH_TABLE_DATA(HASH_TABLE_DATA const& h, size_type n) - : allocators_(h.allocators_), + HASH_TABLE_DATA(HASH_TABLE_DATA const& x, size_type n) + : allocators_(x.allocators_), buckets_(), bucket_count_(next_prime(n)), cached_begin_bucket_(), size_(0) { @@ -376,14 +385,11 @@ namespace boost { allocator_array_constructor constructor(allocators_.bucket_alloc_); - // Creates an extra bucket to act as a sentinel. - constructor.construct(bucket(), bucket_count_ + 1); + // Creates two extra buckets to act as sentinels. + constructor.construct(bucket(), bucket_count_ + 2); cached_begin_bucket_ = constructor.get() + bucket_count_; - // Set up the sentinel - cached_begin_bucket_->next_ = cached_begin_bucket_; - // Only release the buckets once everything is successfully // done. buckets_ = constructor.release(); @@ -400,10 +406,11 @@ namespace boost { ++begin; } - for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2) + // Destroy two extra buckets for the sentinels. + for(size_type i2 = 0; i2 < bucket_count_ + 2; ++i2) allocators_.bucket_alloc_.destroy(buckets_ + i2); - allocators_.bucket_alloc_.deallocate(buckets_, bucket_count_ + 1); + allocators_.bucket_alloc_.deallocate(buckets_, bucket_count_ + 2); } } @@ -444,12 +451,12 @@ namespace boost { iterator_base end() const { - return iterator_base(buckets_ + bucket_count_); + return iterator_base(buckets_ + bucket_count_, local_iterator_base()); } local_iterator_base begin(size_type n) const { - return local_iterator_base(buckets_[n].next_); + return local_iterator_base(buckets_[n].first_); } local_iterator_base end(size_type) const @@ -459,7 +466,7 @@ namespace boost { local_iterator_base begin(bucket_ptr b) const { - return local_iterator_base(b->next_); + return local_iterator_base(b->first_); } // Bucket Size @@ -492,11 +499,11 @@ namespace boost { } #if BOOST_UNORDERED_HASH_EQUIVALENT - size_type group_count(local_iterator_base pos) const + size_type group_count(local_iterator_base first_node) const { size_type count = 0; - bucket_ptr it = pos.node_pointer_; - bucket_ptr first = it; + node_ptr it = first_node.node_; + node_ptr first = it; do { ++count; it = prev_in_group(it); @@ -517,28 +524,28 @@ namespace boost { // no throw #if BOOST_UNORDERED_HASH_EQUIVALENT - bucket_ptr* get_for_erase(iterator_base r) const + node_ptr* get_for_erase(iterator_base r) const { - bucket_ptr pos = r.local_.node_pointer_; + node_ptr n = r.local_.node_; // If the element isn't the first in its group, then // the link to it will be found in the previous element // in the group. - bucket_ptr* it = &prev_in_group(pos)->next_; - if(*it == pos) return it; + node_ptr* it = &prev_in_group(n)->links_.next_; + if(*it == n) return it; - // The element is the first in its group, so just - // need to check the first elements. - it = &r.bucket_->next_; - while(*it != pos) it = &HASH_TABLE_DATA::next_group(*it); + // The element is the first in its group, so iterate + // throught the groups, checking against the first element. + it = &r.bucket_->first_; + while(*it != n) it = &HASH_TABLE_DATA::next_group(*it); return it; } #else - bucket_ptr* get_for_erase(iterator_base r) const + node_ptr* get_for_erase(iterator_base r) const { - bucket_ptr pos = r.local_.node_pointer_; - bucket_ptr* it = &r.bucket_->next_; - while(*it != pos) it = &(*it)->next_; + node_ptr n = r.local_.node_; + node_ptr* it = &r.bucket_->first_; + while(*it != n) it = &(*it)->links_.next_; return it; } #endif @@ -551,59 +558,68 @@ namespace boost { // no throw #if BOOST_UNORDERED_HASH_EQUIVALENT - void link_node(bucket_ptr n, local_iterator_base pos) + void link_node(node_ptr n, local_iterator_base pos) { - node& node_ref = get_node(n); - node& pos_ref = get_node(pos.node_pointer_); - node_ref.next_ = pos_ref.group_prev_->next_; - node_ref.group_prev_ = pos_ref.group_prev_; - pos_ref.group_prev_->next_ = n; - pos_ref.group_prev_ = n; + node_links& links = pos.node_->links_; + + n->links_.next_ = links.group_prev_->links_.next_; + n->links_.group_prev_ = links.group_prev_; + links.group_prev_->links_.next_ = n; + links.group_prev_ = n; ++size_; } - void link_node(bucket_ptr n, bucket_ptr base) + void link_node(node_ptr n, bucket_ptr base) { - node& node_ref = get_node(n); - node_ref.next_ = base->next_; - node_ref.group_prev_ = n; - base->next_ = n; + n->links_.next_ = base->first_; + n->links_.group_prev_ = n; + base->first_ = n; ++size_; - if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + if(base < cached_begin_bucket_) { + cached_begin_bucket_ = base; + buckets_[bucket_count_].first_ = cached_begin_bucket_->first_; + buckets_[bucket_count_+1].first_ = cached_begin_bucket_->first_; + } } - void link_group(bucket_ptr n, bucket_ptr base, size_type count) + void link_group(node_ptr n, bucket_ptr base, size_type count) { - node& node_ref = get_node(n); - node& last_ref = get_node(node_ref.group_prev_); - last_ref.next_ = base->next_; - base->next_ = n; + n->links_.group_prev_->links_.next_ = base->first_; + base->first_ = n; size_ += count; - if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + if(base < cached_begin_bucket_) { + cached_begin_bucket_ = base; + buckets_[bucket_count_].first_ = cached_begin_bucket_->first_; + buckets_[bucket_count_+1].first_ = cached_begin_bucket_->first_; + } } #else - void link_node(bucket_ptr n, bucket_ptr base) + void link_node(node_ptr n, bucket_ptr base) { - n->next_ = base->next_; - base->next_ = n; + n->links_.next_ = base->first_; + base->first_ = n; ++size_; - if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + if(base < cached_begin_bucket_) { + cached_begin_bucket_ = base; + buckets_[bucket_count_].first_ = cached_begin_bucket_->first_; + buckets_[bucket_count_+1].first_ = cached_begin_bucket_->first_; + } } - void link_group(bucket_ptr n, bucket_ptr base, size_type) + void link_group(node_ptr n, bucket_ptr base, size_type) { link_node(n, base); } #endif #if BOOST_UNORDERED_HASH_EQUIVALENT - void unlink_node(iterator_base x) + void unlink_node(iterator_base it) { - node& to_delete = get_node(x.local_.node_pointer_); - bucket_ptr next = to_delete.next_; - bucket_ptr* pos = get_for_erase(x); + node_ptr* pos = get_for_erase(it); + node_ptr n = *pos; + node_ptr next = n->links_.next_; - if(to_delete.group_prev_ == *pos) { + if(n->links_.group_prev_ == n) { // The deleted node is the sole node in the group, so // no need to unlink it from a goup. } @@ -611,52 +627,52 @@ namespace boost { { // The deleted node is not at the end of the group, so // change the link from the next node. - prev_in_group(next) = to_delete.group_prev_; + prev_in_group(next) = n->links_.group_prev_; } else { // The deleted node is at the end of the group, so the // node in the group pointing to it is at the beginning // of the group. Find that to change its pointer. - bucket_ptr it = to_delete.group_prev_; + node_ptr it = n->links_.group_prev_; while(prev_in_group(it) != *pos) { it = prev_in_group(it); } - prev_in_group(it) = to_delete.group_prev_; + prev_in_group(it) = n->links_.group_prev_; } - *pos = (*pos)->next_; + *pos = next; --size_; } - size_type unlink_group(bucket_ptr* pos) + size_type unlink_group(node_ptr* pos) { size_type count = group_count(local_iterator_base(*pos)); size_ -= count; - bucket_ptr last = last_in_group(*pos); - *pos = last->next_; + node_ptr last = last_in_group(*pos); + *pos = last->links_.next_; return count; } #else - void unlink_node(iterator_base x) + void unlink_node(iterator_base n) { - bucket_ptr* pos = get_for_erase(x); - *pos = (*pos)->next_; + node_ptr* pos = get_for_erase(n); + *pos = (*pos)->links_.next_; --size_; } - size_type unlink_group(bucket_ptr* pos) + size_type unlink_group(node_ptr* pos) { - *pos = (*pos)->next_; + *pos = (*pos)->links_.next_; --size_; return 1; } #endif - void unlink_nodes(iterator_base pos) + void unlink_nodes(iterator_base n) { - bucket_ptr* it = get_for_erase(pos); + node_ptr* it = get_for_erase(n); split_group(*it); unordered_detail::reset(*it); - size_ -= node_count(pos.local_); + size_ -= node_count(n.local_); } void unlink_nodes(iterator_base begin, iterator_base end) @@ -665,9 +681,9 @@ namespace boost { local_iterator_base local_end = end.local_; size_ -= node_count(begin.local_, local_end); - bucket_ptr* it = get_for_erase(begin); - split_group(*it, local_end.node_pointer_); - *it = local_end.node_pointer_; + node_ptr* it = get_for_erase(begin); + split_group(*it, local_end.node_); + *it = local_end.node_; } void unlink_nodes(bucket_ptr base, iterator_base end) @@ -675,10 +691,10 @@ namespace boost { BOOST_ASSERT(base == end.bucket_); local_iterator_base local_end = end.local_; - split_group(local_end.node_pointer_); + split_group(local_end.node_); - bucket_ptr ptr(base->next_); - base->next_ = local_end.node_pointer_; + node_ptr ptr(base->first_); + base->first_ = local_end.node_; size_ -= node_count(local_iterator_base(ptr), local_end); } @@ -687,49 +703,49 @@ namespace boost { // Break a ciruclar list into two, with split as the beginneing // of the second group (if split is at the beginning then don't // split). - bucket_ptr split_group(bucket_ptr split) + node_ptr split_group(node_ptr split) { // If split is at the beginning of the group then there's // nothing to split. - if(prev_in_group(split)->next_ != split) - return bucket_ptr(); + if(prev_in_group(split)->links_.next_ != split) + return node_ptr(); // Find the start of the group. - bucket_ptr start = split; + node_ptr start = split; do { start = prev_in_group(start); - } while(prev_in_group(start)->next_ == start); + } while(prev_in_group(start)->links_.next_ == start); - bucket_ptr last = prev_in_group(start); + node_ptr last = prev_in_group(start); prev_in_group(start) = prev_in_group(split); prev_in_group(split) = last; return start; } - void split_group(bucket_ptr split1, bucket_ptr split2) + void split_group(node_ptr split1, node_ptr split2) { - bucket_ptr begin1 = split_group(split1); - bucket_ptr begin2 = split_group(split2); + node_ptr begin1 = split_group(split1); + node_ptr begin2 = split_group(split2); if(begin1 && split1 == begin2) { - bucket_ptr end1 = prev_in_group(begin1); + node_ptr end1 = prev_in_group(begin1); prev_in_group(begin1) = prev_in_group(begin2); prev_in_group(begin2) = end1; } } #else - void split_group(bucket_ptr) + void split_group(node_ptr) { } - void split_group(bucket_ptr, bucket_ptr) + void split_group(node_ptr, node_ptr) { } #endif // throws, strong exception-safety: - bucket_ptr construct_node(value_type const& v) + node_ptr construct_node(value_type const& v) { node_constructor a(allocators_); a.construct(v); @@ -745,7 +761,7 @@ namespace boost { iterator_base create_node(value_type const& v, bucket_ptr base) { // throws, strong exception-safety: - bucket_ptr n = construct_node(v); + node_ptr n = construct_node(v); // Rest is no throw link_node(n, base); @@ -756,7 +772,7 @@ namespace boost { iterator_base create_node(value_type const& v, iterator_base position) { // throws, strong exception-safety: - bucket_ptr n = construct_node(v); + node_ptr n = construct_node(v); // Rest is no throw link_node(n, position.local_); @@ -767,7 +783,7 @@ namespace boost { bucket_ptr base, local_iterator_base position) { // throws, strong exception-safety: - bucket_ptr n = construct_node(v); + node_ptr n = construct_node(v); // Rest is no throw if(position.not_finished()) @@ -797,38 +813,38 @@ namespace boost { // Delete Node // - // Remove a node, or a range of nodes, from a bucket, and destory + // Remove a node, or a range of nodes, from a bucket, and destroy // them. // // no throw - void delete_to_bucket_end(bucket_ptr ptr) + void delete_to_bucket_end(node_ptr begin) { - while(ptr) { - bucket_ptr pos = ptr; - ptr = ptr->next_; - allocators_.destroy(pos); + while(begin) { + node_ptr node = begin; + begin = begin->links_.next_; + allocators_.destroy(node); } } - void delete_nodes(bucket_ptr begin, bucket_ptr end) + void delete_nodes(node_ptr begin, node_ptr end) { while(begin != end) { - bucket_ptr pos = begin; - begin = begin->next_; - allocators_.destroy(pos); + node_ptr node = begin; + begin = begin->links_.next_; + allocators_.destroy(node); } } #if BOOST_UNORDERED_HASH_EQUIVALENT - void delete_group(bucket_ptr pos) + void delete_group(node_ptr first_node) { - delete_nodes(pos, prev_in_group(pos)->next_); + delete_nodes(first_node, prev_in_group(first_node)->links_.next_); } #else - void delete_group(bucket_ptr pos) + void delete_group(node_ptr node) { - allocators_.destroy(pos); + allocators_.destroy(node); } #endif @@ -840,9 +856,9 @@ namespace boost { void clear_bucket(bucket_ptr b) { - bucket_ptr ptr = b->next_; - unordered_detail::reset(b->next_); - delete_to_bucket_end(ptr); + node_ptr first_node = b->first_; + unordered_detail::reset(b->first_); + delete_to_bucket_end(first_node); } void clear() @@ -872,7 +888,7 @@ namespace boost { iterator_base next = r; next.increment(); unlink_node(r); - allocators_.destroy(r.local_.node_pointer_); + allocators_.destroy(r.local_.node_); // r has been invalidated but its bucket is still valid recompute_begin_bucket(r.bucket_, next.bucket_); return next; @@ -886,7 +902,7 @@ namespace boost { if (r1.bucket_ == r2.bucket_) { unlink_nodes(r1, r2); - delete_nodes(r1.local_.node_pointer_, r2.local_.node_pointer_); + delete_nodes(r1.local_.node_, r2.local_.node_); // No need to call recompute_begin_bucket because // the nodes are only deleted from one bucket, which @@ -897,17 +913,17 @@ namespace boost { BOOST_ASSERT(r1.bucket_ < r2.bucket_); unlink_nodes(r1); - delete_to_bucket_end(r1.local_.node_pointer_); + delete_to_bucket_end(r1.local_.node_); for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i) { - size_ -= node_count(local_iterator_base(i->next_)); + size_ -= node_count(local_iterator_base(i->first_)); clear_bucket(i); } if(r2 != end()) { - bucket_ptr first = r2.bucket_->next_; + node_ptr first = r2.bucket_->first_; unlink_nodes(r2.bucket_, r2); - delete_nodes(first, r2.local_.node_pointer_); + delete_nodes(first, r2.local_.node_); } // r1 has been invalidated but its bucket is still @@ -926,18 +942,20 @@ namespace boost { // // no throw - void recompute_begin_bucket(bucket_ptr ptr) + void recompute_begin_bucket(bucket_ptr b) { - BOOST_ASSERT(!(ptr < cached_begin_bucket_)); + BOOST_ASSERT(!(b < cached_begin_bucket_)); - if(ptr == cached_begin_bucket_) - { - if (size_ != 0) { - while (cached_begin_bucket_->empty()) - ++cached_begin_bucket_; - } else { - cached_begin_bucket_ = buckets_ + bucket_count_; - } + if (size_ == 0) { + cached_begin_bucket_ = buckets_ + bucket_count_; + } + else if(b == cached_begin_bucket_ && b->empty()) { + do { + ++cached_begin_bucket_; + } while (cached_begin_bucket_->empty()); + + buckets_[bucket_count_].first_ = cached_begin_bucket_->first_; + buckets_[bucket_count_+1].first_ = cached_begin_bucket_->first_; } } @@ -945,22 +963,28 @@ namespace boost { // // no throw - void recompute_begin_bucket(bucket_ptr i, bucket_ptr j) + void recompute_begin_bucket(bucket_ptr b1, bucket_ptr b2) { - BOOST_ASSERT(!(i < cached_begin_bucket_) && !(j < i)); - BOOST_ASSERT(j == buckets_ + bucket_count_ || !j->empty()); + BOOST_ASSERT(!(b1 < cached_begin_bucket_) && !(b2 < b1)); + BOOST_ASSERT(b2 == buckets_ + bucket_count_ || !b2->empty()); - if(i == cached_begin_bucket_ && i->empty()) - cached_begin_bucket_ = j; + if(size_ == 0) { + cached_begin_bucket_ = b2; + } else if(b1 == cached_begin_bucket_ && b1->empty()) { + cached_begin_bucket_ = b2; + + buckets_[bucket_count_].first_ = cached_begin_bucket_->first_; + buckets_[bucket_count_+1].first_ = cached_begin_bucket_->first_; + } } - size_type erase_group(bucket_ptr* it, bucket_ptr bucket) + size_type erase_group(node_ptr* pos, bucket_ptr b) { - bucket_ptr pos = *it; - size_type count = unlink_group(it); - delete_group(pos); + node_ptr first_node = *pos; + size_type count = unlink_group(pos); + delete_group(first_node); - this->recompute_begin_bucket(bucket); + this->recompute_begin_bucket(b); return count; } @@ -990,6 +1014,7 @@ namespace boost { typedef typename data::node_constructor node_constructor; typedef typename data::bucket_ptr bucket_ptr; + typedef typename data::node_ptr node_ptr; public: @@ -1047,6 +1072,9 @@ namespace boost { public: // Constructors + // + // In the constructors, if anything throws an exception, + // HASH_TABLE_DATA's destructor will clean up. HASH_TABLE(size_type n, hasher const& hf, key_equal const& eq, @@ -1086,11 +1114,11 @@ namespace boost { }; template - size_type initial_size(I i, I j, size_type x) + size_type initial_size(I i, I j, size_type n) { BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type iterator_traversal_tag; - return initial_size(i, j, x, iterator_traversal_tag); + return initial_size(i, j, n, iterator_traversal_tag); }; template @@ -1189,8 +1217,7 @@ namespace boost { functions_ptr new_func_this = copy_functions(x); // throws functions_ptr new_func_that = x.copy_functions(*this); // throws - if(this->allocators_.node_alloc_ == x.allocators_.node_alloc_ && - this->allocators_.bucket_alloc_ == x.allocators_.bucket_alloc_) { + if(this->allocators_ == x.allocators_) { this->data::swap(x); // no throw } else { @@ -1211,12 +1238,9 @@ namespace boost { this->data::swap(new_this); x.data::swap(new_that); #elif BOOST_UNORDERED_SWAP_METHOD == 3 - // Note: I'm not sure that these are guaranteed to be no + // Note: I'm not sure that allocator swap is guaranteed to be no // throw. - hash_swap(this->allocators_.node_alloc_, - x.allocators_.node_alloc_); - hash_swap(this->allocators_.bucket_alloc_, - x.allocators_.bucket_alloc_); + this->allocators_.swap(x.allocators_); this->data::swap(x); #else #error "Invalid swap method" @@ -1263,7 +1287,7 @@ namespace boost { // no throw value_allocator get_allocator() const { - return this->allocators_.node_alloc_; + return this->allocators_.value_alloc_; } // no throw @@ -1468,24 +1492,26 @@ namespace boost { static void move_buckets(data& src, data& dst, hasher const& hf) { BOOST_ASSERT(dst.size_ == 0); - BOOST_ASSERT(src.allocators_.node_alloc_ == dst.allocators_.node_alloc_); + + // I'm assuming that this can't throw... + BOOST_ASSERT(src.allocators_ == dst.allocators_); bucket_ptr end = src.buckets_ + src.bucket_count_; for(; src.cached_begin_bucket_ != end; ++src.cached_begin_bucket_) { bucket_ptr src_bucket = src.cached_begin_bucket_; - while(src_bucket->next_) { + while(src_bucket->first_) { // Move the first group of equivalent nodes in // src_bucket to dst. // This next line throws iff the hash function throws. bucket_ptr dst_bucket = dst.buckets_ + dst.index_from_hash( - hf(extract_key(get_value(src_bucket->next_)))); + hf(extract_key(src_bucket->first_->value_))); - bucket_ptr n = src_bucket->next_; - size_type count = src.unlink_group(&src_bucket->next_); + node_ptr n = src_bucket->first_; + size_type count = src.unlink_group(&src_bucket->first_); dst.link_group(n, dst_bucket, count); } } @@ -1548,7 +1574,7 @@ namespace boost { // Nothing after the point can throw. - bucket_ptr n = a.release(); + node_ptr n = a.release(); // I'm relying on local_iterator_base not being invalidated by // the rehash here. @@ -1585,7 +1611,7 @@ namespace boost { // Nothing after this point can throw - bucket_ptr n = a.release(); + node_ptr n = a.release(); this->link_node(n, it.local_); return iterator_base(base, n); @@ -1678,7 +1704,7 @@ namespace boost { // Nothing after this point can throw. - bucket_ptr n = a.release(); + node_ptr n = a.release(); this->link_node(n, bucket); return *local_iterator_base(n); @@ -1718,7 +1744,7 @@ namespace boost { // Nothing after this point can throw. - bucket_ptr n = a.release(); + node_ptr n = a.release(); this->link_node(n, bucket); return std::pair( @@ -1812,7 +1838,7 @@ namespace boost { { // No side effects in initial section bucket_ptr bucket = get_bucket(k); - bucket_ptr* it = find_for_erase(bucket, k); + node_ptr* it = find_for_erase(bucket, k); // No throw. return *it ? this->erase_group(it, bucket) : 0; @@ -1856,7 +1882,7 @@ namespace boost { local_iterator_base it = find_iterator(bucket, k); if (it.not_finished()) { iterator_base first(iterator_base(bucket, it)); - iterator_base second(iterator_base(bucket, last_in_group(it.node_pointer_))); + iterator_base second(iterator_base(bucket, last_in_group(it.node_))); second.increment(); return std::pair(first, second); } @@ -1892,10 +1918,10 @@ namespace boost { } // strong exception safety, no side effects - bucket_ptr* find_for_erase(bucket_ptr bucket, key_type const& k) const + node_ptr* find_for_erase(bucket_ptr bucket, key_type const& k) const { - bucket_ptr* it = &bucket->next_; - while(*it && !equal(k, this->get_value(*it))) + node_ptr* it = &bucket->first_; + while(*it && !equal(k, (*it)->value_)) it = &this->next_group(*it); return it; @@ -2063,3 +2089,4 @@ namespace boost { #undef HASH_CONST_LOCAL_ITERATOR + From 9e51e23cead5547f6d9295dddb470ec5de827f33 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 10 Jan 2007 00:03:26 +0000 Subject: [PATCH 058/175] Remove some of the old doxygen stuff from the unordered container header files as I'm not using doxygen. [SVN r3618] --- include/boost/unordered_map.hpp | 79 ++++++++++----------------------- include/boost/unordered_set.hpp | 52 +++++++++------------- 2 files changed, 45 insertions(+), 86 deletions(-) diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp index 810474b9..fb09b46f 100644 --- a/include/boost/unordered_map.hpp +++ b/include/boost/unordered_map.hpp @@ -21,11 +21,6 @@ namespace boost { - //! An unordered associative container that associates unique keys with another value. - /*! For full details see chapter 23 of the draft C++ standard. - * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf - */ - template , @@ -33,27 +28,20 @@ namespace boost class Alloc = std::allocator > > class unordered_map { - // Named for the benefit of Doxygen. typedef boost::unordered_detail::hash_types_unique_keys< std::pair, Key, Hash, Pred, Alloc - > implementation_defined; + > implementation; - typename implementation_defined::hash_table base; + typename implementation::hash_table base; public: + // types - /*! Key must be Assignable and CopyConstructible. - */ + typedef Key key_type; typedef std::pair value_type; typedef T mapped_type; - /*! Hash is a unary function object type such for hf of type hasher - * hf(x) has type std::size_t. - */ typedef Hash hasher; - /*! Pred is a binary predicate that takes two arguments of type Key. - * Pred is an equivalence realtion - */ typedef Pred key_equal; typedef Alloc allocator_type; @@ -62,29 +50,16 @@ namespace boost typedef typename allocator_type::reference reference; typedef typename allocator_type::const_reference const_reference; - typedef typename implementation_defined::size_type size_type; - typedef typename implementation_defined::difference_type difference_type; + typedef typename implementation::size_type size_type; + typedef typename implementation::difference_type difference_type; - typedef typename implementation_defined::iterator iterator; - typedef typename implementation_defined::const_iterator const_iterator; - - /*! A local_iterator object may be used to iterate through a single - * bucket, but may not be used to iterate across buckets. - */ - typedef typename implementation_defined::local_iterator local_iterator; - - /*! A const_local_iterator object may be used to iterate through a single - * bucket, but may not be used to iterate across buckets. - */ - typedef typename implementation_defined::const_local_iterator - const_local_iterator; + typedef typename implementation::iterator iterator; + typedef typename implementation::const_iterator const_iterator; + typedef typename implementation::local_iterator local_iterator; + typedef typename implementation::const_local_iterator const_local_iterator; // construct/destroy/copy - /*! Constructs an empty container with at least n buckets, using hf as - * the hash function and eq as the key equality predicate. a is used - * as the allocator. - */ explicit unordered_map( size_type n = boost::unordered_detail::default_initial_bucket_count, const hasher &hf = hasher(), @@ -94,10 +69,6 @@ namespace boost { } - /*! Constructs an empty container with at least n buckets, using hf as - * the hash function and eq as the key equality predicate, and inserts - * elements from [i,j) into it. a is used as the allocator. - */ template unordered_map(InputIterator f, InputIterator l) : base(f, l, boost::unordered_detail::default_initial_bucket_count, @@ -117,7 +88,7 @@ namespace boost private: - typename implementation_defined::iterator_base const& + typename implementation::iterator_base const& get(const_iterator const& it) { return boost::unordered_detail::iterator_access::get(it); @@ -333,7 +304,7 @@ namespace boost { return const_local_iterator(base.begin(n)); } - + const_local_iterator cend(size_type n) const { return const_local_iterator(base.end(n)); @@ -370,11 +341,6 @@ namespace boost m1.swap(m2); } - //! An unordered associative container that associates equivalent keys with another value. - /*! For full details see chapter 23 of the draft C++ standard. - * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf - */ - template , @@ -382,15 +348,16 @@ namespace boost class Alloc = std::allocator > > class unordered_multimap { - // Named for the benefit of Doxygen. typedef boost::unordered_detail::hash_types_equivalent_keys< std::pair, Key, Hash, Pred, Alloc - > implementation_defined; + > implementation; - typename implementation_defined::hash_table base; + typename implementation::hash_table base; public: + // types + typedef Key key_type; typedef std::pair value_type; typedef T mapped_type; @@ -403,13 +370,13 @@ namespace boost typedef typename allocator_type::reference reference; typedef typename allocator_type::const_reference const_reference; - typedef typename implementation_defined::size_type size_type; - typedef typename implementation_defined::difference_type difference_type; + typedef typename implementation::size_type size_type; + typedef typename implementation::difference_type difference_type; - typedef typename implementation_defined::iterator iterator; - typedef typename implementation_defined::const_iterator const_iterator; - typedef typename implementation_defined::local_iterator local_iterator; - typedef typename implementation_defined::const_local_iterator const_local_iterator; + typedef typename implementation::iterator iterator; + typedef typename implementation::const_iterator const_iterator; + typedef typename implementation::local_iterator local_iterator; + typedef typename implementation::const_local_iterator const_local_iterator; // construct/destroy/copy @@ -441,7 +408,7 @@ namespace boost private: - typename implementation_defined::iterator_base const& + typename implementation::iterator_base const& get(const_iterator const& it) { return boost::unordered_detail::iterator_access::get(it); diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp index f0db4d3d..ad925560 100644 --- a/include/boost/unordered_set.hpp +++ b/include/boost/unordered_set.hpp @@ -21,26 +21,22 @@ namespace boost { - //! An unordered associative container that stores unique values. - /*! For full details see chapter 23 of the draft C++ standard. - * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf - */ - template , class Pred = std::equal_to, class Alloc = std::allocator > class unordered_set { - // Named for the benefit of Doxygen. typedef boost::unordered_detail::hash_types_unique_keys< Value, Value, Hash, Pred, Alloc - > implementation_defined; + > implementation; - typename implementation_defined::hash_table base; + typename implementation::hash_table base; public: + // types + typedef Value key_type; typedef Value value_type; typedef Hash hasher; @@ -52,13 +48,13 @@ namespace boost typedef typename allocator_type::reference reference; typedef typename allocator_type::const_reference const_reference; - typedef typename implementation_defined::size_type size_type; - typedef typename implementation_defined::difference_type difference_type; + typedef typename implementation::size_type size_type; + typedef typename implementation::difference_type difference_type; - typedef typename implementation_defined::const_iterator iterator; - typedef typename implementation_defined::const_iterator const_iterator; - typedef typename implementation_defined::const_local_iterator local_iterator; - typedef typename implementation_defined::const_local_iterator const_local_iterator; + typedef typename implementation::const_iterator iterator; + typedef typename implementation::const_iterator const_iterator; + typedef typename implementation::const_local_iterator local_iterator; + typedef typename implementation::const_local_iterator const_local_iterator; // construct/destroy/copy @@ -89,7 +85,7 @@ namespace boost private: - typename implementation_defined::iterator_base const& + typename implementation::iterator_base const& get(const_iterator const& it) { return boost::unordered_detail::iterator_access::get(it); @@ -310,26 +306,22 @@ namespace boost m1.swap(m2); } - //! An unordered associative container that stores equivalent values. - /*! For full details see chapter 23 of the draft C++ standard. - * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf - */ - template , class Pred = std::equal_to, class Alloc = std::allocator > class unordered_multiset { - // Named for the benefit of Doxygen. typedef boost::unordered_detail::hash_types_equivalent_keys< Value, Value, Hash, Pred, Alloc - > implementation_defined; + > implementation; - typename implementation_defined::hash_table base; + typename implementation::hash_table base; public: + //types + typedef Value key_type; typedef Value value_type; typedef Hash hasher; @@ -341,13 +333,13 @@ namespace boost typedef typename allocator_type::reference reference; typedef typename allocator_type::const_reference const_reference; - typedef typename implementation_defined::size_type size_type; - typedef typename implementation_defined::difference_type difference_type; + typedef typename implementation::size_type size_type; + typedef typename implementation::difference_type difference_type; - typedef typename implementation_defined::const_iterator iterator; - typedef typename implementation_defined::const_iterator const_iterator; - typedef typename implementation_defined::const_local_iterator local_iterator; - typedef typename implementation_defined::const_local_iterator const_local_iterator; + typedef typename implementation::const_iterator iterator; + typedef typename implementation::const_iterator const_iterator; + typedef typename implementation::const_local_iterator local_iterator; + typedef typename implementation::const_local_iterator const_local_iterator; // construct/destroy/copy @@ -378,7 +370,7 @@ namespace boost private: - typename implementation_defined::iterator_base const& + typename implementation::iterator_base const& get(const_iterator const& it) { return boost::unordered_detail::iterator_access::get(it); From add94f8385f67205c2895ee1c96d8149ec1fc616 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 10 Jan 2007 00:04:00 +0000 Subject: [PATCH 059/175] Remove some superfluous semi-colons from the unordered container implementation. [SVN r3619] --- include/boost/unordered/detail/hash_table_impl.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 55bd5db7..044584a4 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1104,14 +1104,14 @@ namespace boost { { // max load factor isn't set yet, but when it is, it'll be 1.0. return (std::max)(static_cast(std::distance(i, j)) + 1, n); - }; + } template size_type initial_size(I, I, size_type n, boost::incrementable_traversal_tag) { return n; - }; + } template size_type initial_size(I i, I j, size_type n) @@ -1119,7 +1119,7 @@ namespace boost { BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type iterator_traversal_tag; return initial_size(i, j, n, iterator_traversal_tag); - }; + } template HASH_TABLE(I i, I j, size_type n, @@ -1784,7 +1784,7 @@ namespace boost { BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type iterator_traversal_tag; return insert_size(i, j, iterator_traversal_tag); - }; + } // if hash function throws, or inserting > 1 element, basic exception safety // strong otherwise From f979c07d2e212fe3b39418676bcb42602e3ae026 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 10 Jan 2007 00:06:55 +0000 Subject: [PATCH 060/175] Check that exceptions thrown when swapping meet the exception requirements. [SVN r3620] --- test/exception/swap_tests.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/exception/swap_tests.cpp b/test/exception/swap_tests.cpp index 5fe64763..2cc0f381 100644 --- a/test/exception/swap_tests.cpp +++ b/test/exception/swap_tests.cpp @@ -72,6 +72,16 @@ struct swap_base : public test::exception_base } catch (std::runtime_error) {} } void check(data_type const& d) const { + std::string scope(test::scope); + +#if BOOST_UNORDERED_SWAP_METHOD != 2 + HASH_CHECK( + scope == "hash::operator(hash)" || + scope == "hash::operator=(hash)" || + scope == "equal_to::operator(equal_to)" || + scope == "equal_to::operator=(equal_to)"); +#endif + test::check_equivalent_keys(d.x); test::check_equivalent_keys(d.y); } From 7468336d3af1c8765f9791b2ae65a81655ea52d6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 10 Jan 2007 00:07:33 +0000 Subject: [PATCH 061/175] Making use of the new quickbook import feature. [SVN r3621] --- doc/hash_equality.qbk | 37 +++-------------- doc/src_code/insensitive.cpp | 78 ++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 32 deletions(-) create mode 100644 doc/src_code/insensitive.cpp diff --git a/doc/hash_equality.qbk b/doc/hash_equality.qbk index 9cc1ef7a..158b32ea 100644 --- a/doc/hash_equality.qbk +++ b/doc/hash_equality.qbk @@ -30,39 +30,12 @@ Alternatively, you might wish to use a different equality function. If so, make sure you use a hash function that matches it. For example, a case-insensitive dictionary: - struct iequal_to - : std::binary_function - { - bool operator()(std::string const& x, - std::string const& y) const - { - return boost::algorithm::iequals(x, y); - } - }; +[import src_code/insensitive.cpp] +[case_insensitive_functions] +[case_insensitive_dictionary] - struct ihash - : std::unary_function - { - bool operator()(std::string const& x) const - { - std::size_t seed = 0; - - for(std::string::const_iterator it = x.begin(); - it != x.end(); ++it) - { - boost::hash_combine(seed, std::tolower(*it)); - } - - return seed; - } - }; - - struct word_info { - // ... - }; - - boost::unordered_map - idictionary; +A more generic version of this example is available at: +[@../../libs/unordered/examples/case_insensitive.hpp /libs/unordered/examples/case_insensitive.hpp] [h2 Custom Types] diff --git a/doc/src_code/insensitive.cpp b/doc/src_code/insensitive.cpp new file mode 100644 index 00000000..c79d41de --- /dev/null +++ b/doc/src_code/insensitive.cpp @@ -0,0 +1,78 @@ + +// Copyright 2006 Daniel James. +// Distributed under 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) + +#include +#include +#include + +//[case_insensitive_functions + struct iequal_to + : std::binary_function + { + bool operator()(std::string const& x, + std::string const& y) const + { + return boost::algorithm::iequals(x, y); + } + }; + + struct ihash + : std::unary_function + { + bool operator()(std::string const& x) const + { + std::size_t seed = 0; + + for(std::string::const_iterator it = x.begin(); + it != x.end(); ++it) + { + boost::hash_combine(seed, std::toupper(*it)); + } + + return seed; + } + }; + + struct word_info; +//] + + struct word_info { + int tag; + explicit word_info(int t = 0) : tag(t) {} + }; + +int main() { +//[case_insensitive_dictionary + boost::unordered_map + idictionary; +//] + + BOOST_TEST(idictionary.empty()); + + idictionary["one"] = word_info(1); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE") == idictionary.find("one")); + + idictionary.insert(std::make_pair("ONE", word_info(2))); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE")->first == "one" && + idictionary.find("ONE")->second.tag == 1); + + idictionary["One"] = word_info(3); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE")->first == "one" && + idictionary.find("ONE")->second.tag == 3); + + idictionary["two"] = word_info(4); + BOOST_TEST(idictionary.size() == 2); + BOOST_TEST(idictionary.find("two") != idictionary.end() && + idictionary.find("TWO")->first == "two" && + idictionary.find("Two")->second.tag == 4); + + return boost::report_errors(); +} From 12f1e037a8c6887f0a39e761ebd716ffbb6e19c7 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 10 Jan 2007 00:08:41 +0000 Subject: [PATCH 062/175] Add an beefed up example of using a custom predicate and hash function to make unordered_map case insensitive. [SVN r3622] --- examples/case_insensitive.hpp | 60 ++++++++++++++++++++++ examples/case_insensitive_test.cpp | 82 ++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 examples/case_insensitive.hpp create mode 100644 examples/case_insensitive_test.cpp diff --git a/examples/case_insensitive.hpp b/examples/case_insensitive.hpp new file mode 100644 index 00000000..3931a725 --- /dev/null +++ b/examples/case_insensitive.hpp @@ -0,0 +1,60 @@ + +// Copyright 2006 Daniel James. +// Distributed under 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) + +// This file implements a locale aware case insenstive equality predicate and +// hash function. Unfortunately it still falls short of full +// internationalization as it only deals with a single character at a time +// (some languages have tricky cases where the characters in an upper case +// string don't have a one-to-one correspondence with the lower case version of +// the text, eg. ) + +#if !defined(BOOST_HASH_EXAMPLES_CASE_INSENSITIVE_HEADER) +#define BOOST_HASH_EXAMPLES_CASE_INSENSITIVE_HEADER + +#include +#include + +namespace hash_examples +{ + struct iequal_to + : std::binary_function + { + iequal_to() {} + explicit iequal_to(std::locale const& l) : locale_(l) {} + + template + bool operator()(String1 const& x1, String2 const& x2) const + { + return boost::algorithm::iequals(x1, x2); + } + private: + std::locale locale_; + }; + + struct ihash + : std::unary_function + { + ihash() {} + explicit ihash(std::locale const& l) : locale_(l) {} + + template + bool operator()(String const& x) const + { + std::size_t seed = 0; + + for(typename String::const_iterator it = x.begin(); + it != x.end(); ++it) + { + boost::hash_combine(seed, std::toupper(*it, locale_)); + } + + return seed; + } + private: + std::locale locale_; + }; +} + +#endif diff --git a/examples/case_insensitive_test.cpp b/examples/case_insensitive_test.cpp new file mode 100644 index 00000000..66674bd9 --- /dev/null +++ b/examples/case_insensitive_test.cpp @@ -0,0 +1,82 @@ + +// Copyright 2006 Daniel James. +// Distributed under 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) + +#include "./case_insensitive.hpp" +#include +#include + +struct word_info { + int tag; + explicit word_info(int t = 0) : tag(t) {} +}; + +void test1() { + boost::unordered_map idictionary; + + BOOST_TEST(idictionary.empty()); + + idictionary["one"] = word_info(1); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE") == idictionary.find("one")); + + idictionary.insert(std::make_pair("ONE", word_info(2))); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE")->first == "one" && + idictionary.find("ONE")->second.tag == 1); + + idictionary["One"] = word_info(3); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE")->first == "one" && + idictionary.find("ONE")->second.tag == 3); + + idictionary["two"] = word_info(4); + BOOST_TEST(idictionary.size() == 2); + BOOST_TEST(idictionary.find("two") != idictionary.end() && + idictionary.find("TWO")->first == "two" && + idictionary.find("Two")->second.tag == 4); + + +} + +void test2() { + boost::unordered_map idictionary; + + BOOST_TEST(idictionary.empty()); + + idictionary[L"one"] = word_info(1); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find(L"ONE") != idictionary.end() && + idictionary.find(L"ONE") == idictionary.find(L"one")); + + idictionary.insert(std::make_pair(L"ONE", word_info(2))); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find(L"ONE") != idictionary.end() && + idictionary.find(L"ONE")->first == L"one" && + idictionary.find(L"ONE")->second.tag == 1); + + idictionary[L"One"] = word_info(3); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find(L"ONE") != idictionary.end() && + idictionary.find(L"ONE")->first == L"one" && + idictionary.find(L"ONE")->second.tag == 3); + + idictionary[L"two"] = word_info(4); + BOOST_TEST(idictionary.size() == 2); + BOOST_TEST(idictionary.find(L"two") != idictionary.end() && + idictionary.find(L"TWO")->first == L"two" && + idictionary.find(L"Two")->second.tag == 4); +} + +int main() { + test1(); + test2(); + + return boost::report_errors(); +} From 8d31c94d9af0b699f4a3d6943da10784af8740d2 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 25 Feb 2007 21:09:38 +0000 Subject: [PATCH 063/175] Fixed for intel strict mode. [SVN r3736] --- include/boost/unordered/detail/hash_table_impl.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 044584a4..246bbc4a 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1644,9 +1644,9 @@ namespace boost { local_iterator_base position = find_iterator(bucket, k); if(position.not_finished()) - link_node(a.release(), position); + this->link_node(a.release(), position); else - link_node(a.release(), bucket); + this->link_node(a.release(), bucket); } } } @@ -1856,7 +1856,7 @@ namespace boost { size_type count(key_type const& k) const { local_iterator_base it = find_iterator(k); // throws, strong - return it.not_finished() ? group_count(it) : 0; + return it.not_finished() ? this->group_count(it) : 0; } // find @@ -1882,7 +1882,7 @@ namespace boost { local_iterator_base it = find_iterator(bucket, k); if (it.not_finished()) { iterator_base first(iterator_base(bucket, it)); - iterator_base second(iterator_base(bucket, last_in_group(it.node_))); + iterator_base second(iterator_base(bucket, this->last_in_group(it.node_))); second.increment(); return std::pair(first, second); } From df1e1598a5c9a4753a05f009e3886f84ec9efbb0 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 18 Mar 2007 20:00:59 +0000 Subject: [PATCH 064/175] Import latest changes to the unordered containers. Includes: Copyright update. Switch back to the version where the sentinel points to itself. Remove alternative versions of swap. Workaround a borland bug or two. More consistent use of class/swap/template. Avoid a few warnings. Add a no-throw swap to the allocator for exception testing. [SVN r3793] --- doc/buckets.qbk | 6 +- doc/comparison.qbk | 9 +- doc/hash_equality.qbk | 4 + doc/intro.qbk | 4 + doc/rationale.qbk | 11 +- doc/ref.xml | 6 + doc/src_code/insensitive.cpp | 2 +- doc/unordered.qbk | 6 +- examples/case_insensitive.hpp | 2 +- examples/case_insensitive_test.cpp | 2 +- include/boost/unordered/detail/allocator.hpp | 2 +- include/boost/unordered/detail/hash_table.hpp | 11 +- .../unordered/detail/hash_table_impl.hpp | 498 ++++++++---------- include/boost/unordered_map.hpp | 2 +- include/boost/unordered_set.hpp | 2 +- test/Jamfile.v2 | 2 +- test/container/compile_tests.hpp | 2 +- test/container/link_test_1.cpp | 2 +- test/container/link_test_2.cpp | 2 +- test/container/map_compile.cpp | 2 +- test/container/set_compile.cpp | 2 +- test/container/simple_tests.cpp | 2 +- test/exception/Jamfile.v2 | 8 +- test/exception/assign_tests.cpp | 2 +- test/exception/constructor_tests.cpp | 2 +- test/exception/containers.hpp | 2 +- test/exception/copy_tests.cpp | 2 +- test/exception/erase_tests.cpp | 2 +- test/exception/insert_tests.cpp | 2 +- test/exception/rehash_tests.cpp | 2 +- test/exception/swap_tests.cpp | 2 +- test/helpers/check_return_type.hpp | 2 +- test/helpers/equivalent.hpp | 2 +- test/helpers/fwd.hpp | 2 +- test/helpers/generators.hpp | 2 +- test/helpers/helpers.hpp | 2 +- test/helpers/input_iterator.hpp | 2 +- test/helpers/invariants.hpp | 2 +- test/helpers/metafunctions.hpp | 2 +- test/helpers/random_values.hpp | 2 +- test/helpers/strong.hpp | 2 +- test/helpers/tracker.hpp | 2 +- test/objects/exception.hpp | 7 +- test/objects/fwd.hpp | 2 +- test/objects/minimal.hpp | 10 +- test/objects/test.hpp | 2 +- test/unordered/Jamfile.v2 | 6 +- test/unordered/assign_tests.cpp | 2 +- test/unordered/bucket_tests.cpp | 2 +- test/unordered/compile_tests.cpp | 8 +- test/unordered/constructor_tests.cpp | 2 +- test/unordered/copy_tests.cpp | 2 +- test/unordered/equivalent_keys_tests.cpp | 2 +- test/unordered/erase_equiv_tests.cpp | 2 +- test/unordered/erase_tests.cpp | 2 +- test/unordered/find_tests.cpp | 2 +- test/unordered/insert_tests.cpp | 2 +- test/unordered/load_factor_tests.cpp | 2 +- test/unordered/rehash_tests.cpp | 2 +- test/unordered/swap_tests.cpp | 2 +- test/unordered/unnecessary_copy_tests.cpp | 2 +- 61 files changed, 334 insertions(+), 354 deletions(-) diff --git a/doc/buckets.qbk b/doc/buckets.qbk index 099ca7ff..4fbc6282 100644 --- a/doc/buckets.qbk +++ b/doc/buckets.qbk @@ -1,3 +1,7 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + [section:buckets The Data Structure] The containers are made up of a number of 'buckets', each of which can contain @@ -6,7 +10,7 @@ boost::unordered_set unordered_set] with 7 buckets containing 5 elements, `A`, `B`, `C`, `D` and `E` (this is just for illustration, in practise containers will have more buckets). -[$../diagrams/buckets.png] +[$../../libs/unordered/doc/diagrams/buckets.png] In order to decide which bucket to place an element in, the container applies `Hash` to the element's key (for `unordered_set` and `unordered_multiset` the diff --git a/doc/comparison.qbk b/doc/comparison.qbk index 53e28bff..e36e3064 100644 --- a/doc/comparison.qbk +++ b/doc/comparison.qbk @@ -1,3 +1,7 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + [section:comparison Comparison with Associative Containers] [table Interface differences. @@ -10,7 +14,10 @@ ] [ [`Compare` exposed by member typedef `key_compare`, accessed by member function `key_comp()`] - [`Hash` exposed by member typedef `hasher`, accessed by member function `hash_function()`.\n`Pred` by member typedef `key_equal` and member function `key_eq()`.] + [ + `Hash` exposed by member typedef `hasher`, accessed by member function `hash_function()`. + + `Pred` by member typedef `key_equal` and member function `key_eq()`.] ] [ [Member typedef `value_compare` supplies an ordering comparison for member elements, accessed by member function `value_comp()`.] diff --git a/doc/hash_equality.qbk b/doc/hash_equality.qbk index 158b32ea..e2844a07 100644 --- a/doc/hash_equality.qbk +++ b/doc/hash_equality.qbk @@ -1,3 +1,7 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + [section:hash_equality Equality Predicates and Hash Functions] [/TODO: A better introduction to hash functions?] diff --git a/doc/intro.qbk b/doc/intro.qbk index 5c44afad..d6e290ff 100644 --- a/doc/intro.qbk +++ b/doc/intro.qbk @@ -1,3 +1,7 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + [def __tr1__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf C++ Standard Library Technical Report]] diff --git a/doc/rationale.qbk b/doc/rationale.qbk index d79cd93e..ec8f3d30 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -1,3 +1,7 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + [def __wang__ [@http://www.concentric.net/~Ttwang/tech/inthash.htm Thomas Wang's article on integer hash functions]] @@ -96,14 +100,11 @@ Need to look into this one. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#431 431. Swapping containers with unequal allocators]] -In a fit of probably unwise enthusiasm, I implemented all the three versions -with a macro (BOOST_UNORDERED_SWAP_METHOD) to pick which one is used. As -suggested by Howard Hinnant, I set option 3 as the default. I'll probably -remove the alternative implementations before review. +I'm following Howard Hinnant's advice and implement option 3. There is currently a further issue - if the allocator's swap does throw there's no guarantee what state the allocators will be in. The only solution seems to -be to double buffer the allocators. +be to double buffer the allocators. But I'm assuming that it won't throw for now. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#518 518. Are insert and erase stable for unordered_multiset and unordered_multimap?]] diff --git a/doc/ref.xml b/doc/ref.xml index e0125e17..b834598f 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -1,3 +1,9 @@ + + - - - - -
- - - - - - - An unordered associative container that stores unique values. - - - For the normative reference see chapter 23 of - the working draft of the C++ standard [n2009]. - - - - Template Parameters - - - - - - Value - - Value must be Assignable and CopyConstructible - - - - - Hash - - A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. - - - - - Pred - - A binary function object that implements an equivalence relation on values of type Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. - - - - - Alloc - - An allocator whose value type is the same as the container's value type. - - - - - - - The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. - - The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. - - - - Value - - - Value - - - Hash - - - Pred - - - Alloc - - - typename allocator_type::pointer - - - typename allocator_type::const_pointer - - - typename allocator_type::reference - - lvalue of - value_type. - - - - - typename allocator_type::const_reference - - const lvalue of - value_type. - - - - - implementation-defined - unsigned integral type - - size_type can represent any non-negative value of - difference_type. - - - - - implementation-defined - signed integral type - Is identical to the difference type of - iterator and - const_iterator. - - - - - implementation-defined - - A constant iterator whose value type is - value_type. - - - Any iterator category except output iterator. - - Convertible to - const_iterator. - - - - - - implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - - - - - - implementation-defined - An iterator with the same value type, difference type and pointer and reference type as - iterator. - - A local_iterator object can be used to iterate through a single bucket. - - - - implementation-defined - A constant iterator with the same value type, difference type and pointer and reference type as - const_iterator. - - A const_local_iterator object can be used to iterate through a single bucket. - - - - - size_type - implementation-defined - - - hasher const& - hasher() - - - key_equal const& - key_equal() - - - allocator_type const& - allocator_type() - - - size() == 0 - - - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. - - - - - - - InputIterator - - - InputIterator - - - size_type - implementation-defined - - - hasher const& - hasher() - - - key_equal const& - key_equal() - - - allocator_type const& - allocator_type() - - - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. - - - - - - unordered_set const& - - - The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. - - - - - - - - unordered_set const& - - unordered_set& - - The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. - - - - - allocator_type - - - - bool - - size() == 0 - - - - size_type - - std::distance(begin(), end()) - - - - size_type - size() of the largest possible container. - - - - - - iterator - const_iterator - An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. - - - - - iterator - - - const_iterator - - An iterator which refers to the past-the-end value for the container. - - - - const_iterator - A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. - - - - const_iterator - A constant iterator which refers to the past-the-end value for the container. - - - - - - size_type - std::distance(begin(), end()) - - - - - value_type const& - - std::pair<iterator, bool> - - Inserts obj in the container if and only if there is no element in the container with an equivalent value. - - - - The bool component of the return type is true if an insert took place. - - If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - iterator - - - value_type const& - - iterator - - - - const_iterator - - - value_type const& - - const_iterator - - - Inserts obj in the container if and only if there is no element in the container with an equivalent value. - - hint is a suggestion to where the element should be inserted. - - - - If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - - - - - - - InputIterator - - - InputIterator - - void - - Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent value. - - - - When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - iterator - - iterator - - - - const_iterator - - const_iterator - - - Erase the element pointed to by position. - - - - The iterator following position before the erasure. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - - - - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - - - - - - - key_type const& - - size_type - - - Erase all elements with key equivalent to k. - - - - The number of elements erased. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - - - - - - iterator - - - iterator - - iterator - - - - const_iterator - - - const_iterator - - const_iterator - - - Erases the elements in the range from first to last. - - - - The iterator following the erased elements - i.e. last. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - - - - - void - - Erases all elements in the container. - - - - size() == 0 - - - - Never throws an exception. - - - - - - unordered_set& - - void - - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. - - - - - - - hasher - The container's hash function. - - - - key_equal - The container's key equality predicate. - - - - - - - - key_type const& - - iterator - - - - key_type const& - - iterator - - - An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - - - - - - key_type const& - - size_type - - The number of elements with key equivalent to k. - - - - - - - key_type const& - - std::pair<iterator, iterator> - - - - key_type const& - - std::pair<iterator, iterator> - - - A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - - - - - - - size_type - - The number of buckets. - - - - - size_type - - An upper bound on the number of buckets. - - - - - - size_type - - size_type - - - n < bucket_count() - - - - The number of elements in bucket - n. - - - - - - key_type const& - - size_type - - The index of the bucket which would contain an element with key k. - - - - The return value is less than bucket_count() - - - - - - - size_type - - local_iterator - - - - size_type - - const_local_iterator - - - n shall be in the range - [0, bucket_count()). - - - - A local iterator pointing the first element in the bucket with index n. - - - - - - - size_type - - local_iterator - - - - size_type - - const_local_iterator - - - n shall be in the range - [0, bucket_count()). - - - - A local iterator pointing the 'one past the end' element in the bucket with index n. - - - - - - - float - - The average number of elements per bucket. - - - - - float - - Returns the current maximum load factor. - - - - - - float - - float - - Changes the container's maximum load factor, using z as a hint. - - - - - - size_type - - void - - Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. - - Invalidates iterators, and changes the order of elements - - - - The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. - - - - - - - - - unordered_set<Key, T, Hash, Pred, Alloc>& - - - unordered_set<Key, T, Hash, Pred, Alloc>& - - void - - x.swap(y) - - - - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. - - - - - - - - - An unordered associative container that stores values. The same key can be stored multiple times. - - - For the normative reference see chapter 23 of - the working draft of the C++ standard [n2009]. - - - - Template Parameters - - - - - - Value - - Value must be Assignable and CopyConstructible - - - - - Hash - - A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. - - - - - Pred - - A binary function object that implements an equivalence relation on values of type Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. - - - - - Alloc - - An allocator whose value type is the same as the container's value type. - - - - - - - The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. - - The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. - - - - Value - - - Value - - - Hash - - - Pred - - - Alloc - - - typename allocator_type::pointer - - - typename allocator_type::const_pointer - - - typename allocator_type::reference - - lvalue of - value_type. - - - - - typename allocator_type::const_reference - - const lvalue of - value_type. - - - - - implementation-defined - unsigned integral type - - size_type can represent any non-negative value of - difference_type. - - - - - implementation-defined - signed integral type - Is identical to the difference type of - iterator and - const_iterator. - - - - - implementation-defined - - A constant iterator whose value type is - value_type. - - - Any iterator category except output iterator. - - Convertible to - const_iterator. - - - - - - implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - - - - - - implementation-defined - An iterator with the same value type, difference type and pointer and reference type as - iterator. - - A local_iterator object can be used to iterate through a single bucket. - - - - implementation-defined - A constant iterator with the same value type, difference type and pointer and reference type as - const_iterator. - - A const_local_iterator object can be used to iterate through a single bucket. - - - - - size_type - implementation-defined - - - hasher const& - hasher() - - - key_equal const& - key_equal() - - - allocator_type const& - allocator_type() - - - size() == 0 - - - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. - - - - - - - InputIterator - - - InputIterator - - - size_type - implementation-defined - - - hasher const& - hasher() - - - key_equal const& - key_equal() - - - allocator_type const& - allocator_type() - - - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. - - - - - - unordered_multiset const& - - - The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. - - - - - - - - unordered_multiset const& - - unordered_multiset& - - The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. - - - - - allocator_type - - - - bool - - size() == 0 - - - - size_type - - std::distance(begin(), end()) - - - - size_type - size() of the largest possible container. - - - - - - iterator - const_iterator - An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. - - - - - iterator - - - const_iterator - - An iterator which refers to the past-the-end value for the container. - - - - const_iterator - A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. - - - - const_iterator - A constant iterator which refers to the past-the-end value for the container. - - - - - - size_type - std::distance(begin(), end()) - - - - - value_type const& - - iterator - - Inserts obj in the container. - - - - An iterator pointing to the inserted element. - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - iterator - - - value_type const& - - iterator - - - - const_iterator - - - value_type const& - - const_iterator - - - Inserts obj in the container. - - hint is a suggestion to where the element should be inserted. - - - - An iterator pointing to the inserted element. - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - InputIterator - - - InputIterator - - void - - Inserts a range of elements into the container. - - - - When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - iterator - - iterator - - - - const_iterator - - const_iterator - - - Erase the element pointed to by position. - - - - The iterator following position before the erasure. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - - - - - - - - - key_type const& - - size_type - - - Erase all elements with key equivalent to k. - - - - The number of elements erased. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - - - - - - iterator - - - iterator - - iterator - - - - const_iterator - - - const_iterator - - const_iterator - - - Erases the elements in the range from first to last. - - - - The iterator following the erased elements - i.e. last. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - - - - - void - - Erases all elements in the container. - - - - size() == 0 - - - - Never throws an exception. - - - - - - unordered_multiset& - - void - - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. - - - - - - - hasher - The container's hash function. - - - - key_equal - The container's key equality predicate. - - - - - - - - key_type const& - - iterator - - - - key_type const& - - iterator - - - An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - - - - - - key_type const& - - size_type - - The number of elements with key equivalent to k. - - - - - - - key_type const& - - std::pair<iterator, iterator> - - - - key_type const& - - std::pair<iterator, iterator> - - - A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - - - - - - - size_type - - The number of buckets. - - - - - size_type - - An upper bound on the number of buckets. - - - - - - size_type - - size_type - - - n < bucket_count() - - - - The number of elements in bucket - n. - - - - - - key_type const& - - size_type - - The index of the bucket which would contain an element with key k. - - - - The return value is less than bucket_count() - - - - - - - size_type - - local_iterator - - - - size_type - - const_local_iterator - - - n shall be in the range - [0, bucket_count()). - - - - A local iterator pointing the first element in the bucket with index n. - - - - - - - size_type - - local_iterator - - - - size_type - - const_local_iterator - - - n shall be in the range - [0, bucket_count()). - - - - A local iterator pointing the 'one past the end' element in the bucket with index n. - - - - - - - float - - The average number of elements per bucket. - - - - - float - - Returns the current maximum load factor. - - - - - - float - - float - - Changes the container's maximum load factor, using z as a hint. - - - - - - size_type - - void - - Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. - - Invalidates iterators, and changes the order of elements - - - - The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. - - - - - - - - - unordered_multiset<Key, T, Hash, Pred, Alloc>& - - - unordered_multiset<Key, T, Hash, Pred, Alloc>& - - void - - x.swap(y) - - - - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. - - - - -
- - -
- - - - - - - An unordered associative container that associates unique keys with another value. - - - For the normative reference see chapter 23 of - the working draft of the C++ standard [n2009]. - - - - Template Parameters - - - - - - Key - - Key must be Assignable and CopyConstructible. - - - - - T - - T must be CopyConstructible - - - - - Hash - - A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. - - - - - Pred - - A binary function object that implements an equivalence relation on values of type Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. - - - - - Alloc - - An allocator whose value type is the same as the container's value type. - - - - - - - The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. - - The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. - - - - Key - - - std::pair<Key const, Value> - - - T - - - Hash - - - Pred - - - Alloc - - - typename allocator_type::pointer - - - typename allocator_type::const_pointer - - - typename allocator_type::reference - - lvalue of - value_type. - - - - - typename allocator_type::const_reference - - const lvalue of - value_type. - - - - - implementation-defined - unsigned integral type - - size_type can represent any non-negative value of - difference_type. - - - - - implementation-defined - signed integral type - Is identical to the difference type of - iterator and - const_iterator. - - - - - implementation-defined - - A iterator whose value type is - value_type. - - - Any iterator category except output iterator. - - Convertible to - const_iterator. - - - - - - implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - - - - - - implementation-defined - An iterator with the same value type, difference type and pointer and reference type as - iterator. - - A local_iterator object can be used to iterate through a single bucket. - - - - implementation-defined - A constant iterator with the same value type, difference type and pointer and reference type as - const_iterator. - - A const_local_iterator object can be used to iterate through a single bucket. - - - - - size_type - implementation-defined - - - hasher const& - hasher() - - - key_equal const& - key_equal() - - - allocator_type const& - allocator_type() - - - size() == 0 - - - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. - - - - - - - InputIterator - - - InputIterator - - - size_type - implementation-defined - - - hasher const& - hasher() - - - key_equal const& - key_equal() - - - allocator_type const& - allocator_type() - - - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. - - - - - - unordered_map const& - - - The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. - - - - - - - - unordered_map const& - - unordered_map& - - The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. - - - - - allocator_type - - - - bool - - size() == 0 - - - - size_type - - std::distance(begin(), end()) - - - - size_type - size() of the largest possible container. - - - - - - iterator - const_iterator - An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. - - - - - iterator - - - const_iterator - - An iterator which refers to the past-the-end value for the container. - - - - const_iterator - A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. - - - - const_iterator - A constant iterator which refers to the past-the-end value for the container. - - - - - - size_type - std::distance(begin(), end()) - - - - - value_type const& - - std::pair<iterator, bool> - - Inserts obj in the container if and only if there is no element in the container with an equivalent key. - - - - The bool component of the return type is true if an insert took place. - - If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - iterator - - - value_type const& - - iterator - - - - const_iterator - - - value_type const& - - const_iterator - - - Inserts obj in the container if and only if there is no element in the container with an equivalent key. - - hint is a suggestion to where the element should be inserted. - - - - If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - - - - - - - InputIterator - - - InputIterator - - void - - Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key. - - - - When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - iterator - - iterator - - - - const_iterator - - const_iterator - - - Erase the element pointed to by position. - - - - The iterator following position before the erasure. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - - - - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - - - - - - - key_type const& - - size_type - - - Erase all elements with key equivalent to k. - - - - The number of elements erased. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - - - - - - iterator - - - iterator - - iterator - - - - const_iterator - - - const_iterator - - const_iterator - - - Erases the elements in the range from first to last. - - - - The iterator following the erased elements - i.e. last. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - - - - - void - - Erases all elements in the container. - - - - size() == 0 - - - - Never throws an exception. - - - - - - unordered_map& - - void - - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. - - - - - - - hasher - The container's hash function. - - - - key_equal - The container's key equality predicate. - - - - - - - - key_type const& - - iterator - - - - key_type const& - - iterator - - - An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - - - - - - key_type const& - - size_type - - The number of elements with key equivalent to k. - - - - - - - key_type const& - - std::pair<iterator, iterator> - - - - key_type const& - - std::pair<iterator, iterator> - - - A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - - - - - - key_type const& - - mapped_type& - - If the container does not already contain an elements with a key equivalent to k, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type()) - - - - A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to k - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - size_type - - The number of buckets. - - - - - size_type - - An upper bound on the number of buckets. - - - - - - size_type - - size_type - - - n < bucket_count() - - - - The number of elements in bucket - n. - - - - - - key_type const& - - size_type - - The index of the bucket which would contain an element with key k. - - - - The return value is less than bucket_count() - - - - - - - size_type - - local_iterator - - - - size_type - - const_local_iterator - - - n shall be in the range - [0, bucket_count()). - - - - A local iterator pointing the first element in the bucket with index n. - - - - - - - size_type - - local_iterator - - - - size_type - - const_local_iterator - - - n shall be in the range - [0, bucket_count()). - - - - A local iterator pointing the 'one past the end' element in the bucket with index n. - - - - - - - float - - The average number of elements per bucket. - - - - - float - - Returns the current maximum load factor. - - - - - - float - - float - - Changes the container's maximum load factor, using z as a hint. - - - - - - size_type - - void - - Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. - - Invalidates iterators, and changes the order of elements - - - - The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. - - - - - - - - - unordered_map<Key, T, Hash, Pred, Alloc>& - - - unordered_map<Key, T, Hash, Pred, Alloc>& - - void - - x.swap(y) - - - - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. - - - - - - - - - An unordered associative container that associates keys with another value. The same key can be stored multiple times. - - - For the normative reference see chapter 23 of - the working draft of the C++ standard [n2009]. - - - - Template Parameters - - - - - - Key - - Key must be Assignable and CopyConstructible. - - - - - T - - T must be CopyConstructible - - - - - Hash - - A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. - - - - - Pred - - A binary function object that implements an equivalence relation on values of type Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. - - - - - Alloc - - An allocator whose value type is the same as the container's value type. - - - - - - - The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. - - The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. - - - - Key - - - std::pair<Key const, Value> - - - T - - - Hash - - - Pred - - - Alloc - - - typename allocator_type::pointer - - - typename allocator_type::const_pointer - - - typename allocator_type::reference - - lvalue of - value_type. - - - - - typename allocator_type::const_reference - - const lvalue of - value_type. - - - - - implementation-defined - unsigned integral type - - size_type can represent any non-negative value of - difference_type. - - - - - implementation-defined - signed integral type - Is identical to the difference type of - iterator and - const_iterator. - - - - - implementation-defined - - A iterator whose value type is - value_type. - - - Any iterator category except output iterator. - - Convertible to - const_iterator. - - - - - - implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - - - - - - implementation-defined - An iterator with the same value type, difference type and pointer and reference type as - iterator. - - A local_iterator object can be used to iterate through a single bucket. - - - - implementation-defined - A constant iterator with the same value type, difference type and pointer and reference type as - const_iterator. - - A const_local_iterator object can be used to iterate through a single bucket. - - - - - size_type - implementation-defined - - - hasher const& - hasher() - - - key_equal const& - key_equal() - - - allocator_type const& - allocator_type() - - - size() == 0 - - - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. - - - - - - - InputIterator - - - InputIterator - - - size_type - implementation-defined - - - hasher const& - hasher() - - - key_equal const& - key_equal() - - - allocator_type const& - allocator_type() - - - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. - - - - - - unordered_multimap const& - - - The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. - - - - - - - - unordered_multimap const& - - unordered_multimap& - - The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. - - - - - allocator_type - - - - bool - - size() == 0 - - - - size_type - - std::distance(begin(), end()) - - - - size_type - size() of the largest possible container. - - - - - - iterator - const_iterator - An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. - - - - - iterator - - - const_iterator - - An iterator which refers to the past-the-end value for the container. - - - - const_iterator - A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. - - - - const_iterator - A constant iterator which refers to the past-the-end value for the container. - - - - - - size_type - std::distance(begin(), end()) - - - - - value_type const& - - iterator - - Inserts obj in the container. - - - - An iterator pointing to the inserted element. - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - iterator - - - value_type const& - - iterator - - - - const_iterator - - - value_type const& - - const_iterator - - - Inserts obj in the container. - - hint is a suggestion to where the element should be inserted. - - - - An iterator pointing to the inserted element. - - - - If an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - InputIterator - - - InputIterator - - void - - Inserts a range of elements into the container. - - - - When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - - - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - - - - iterator - - iterator - - - - const_iterator - - const_iterator - - - Erase the element pointed to by position. - - - - The iterator following position before the erasure. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - - - - - - - - - key_type const& - - size_type - - - Erase all elements with key equivalent to k. - - - - The number of elements erased. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - - - - - - iterator - - - iterator - - iterator - - - - const_iterator - - - const_iterator - - const_iterator - - - Erases the elements in the range from first to last. - - - - The iterator following the erased elements - i.e. last. - - - - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - - - - - void - - Erases all elements in the container. - - - - size() == 0 - - - - Never throws an exception. - - - - - - unordered_multimap& - - void - - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. - - - - - - - hasher - The container's hash function. - - - - key_equal - The container's key equality predicate. - - - - - - - - key_type const& - - iterator - - - - key_type const& - - iterator - - - An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - - - - - - key_type const& - - size_type - - The number of elements with key equivalent to k. - - - - - - - key_type const& - - std::pair<iterator, iterator> - - - - key_type const& - - std::pair<iterator, iterator> - - - A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - - - - - - - size_type - - The number of buckets. - - - - - size_type - - An upper bound on the number of buckets. - - - - - - size_type - - size_type - - - n < bucket_count() - - - - The number of elements in bucket - n. - - - - - - key_type const& - - size_type - - The index of the bucket which would contain an element with key k. - - - - The return value is less than bucket_count() - - - - - - - size_type - - local_iterator - - - - size_type - - const_local_iterator - - - n shall be in the range - [0, bucket_count()). - - - - A local iterator pointing the first element in the bucket with index n. - - - - - - - size_type - - local_iterator - - - - size_type - - const_local_iterator - - - n shall be in the range - [0, bucket_count()). - - - - A local iterator pointing the 'one past the end' element in the bucket with index n. - - - - - - - float - - The average number of elements per bucket. - - - - - float - - Returns the current maximum load factor. - - - - - - float - - float - - Changes the container's maximum load factor, using z as a hint. - - - - - - size_type - - void - - Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. - - Invalidates iterators, and changes the order of elements - - - - The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. - - - - - - - - - unordered_multimap<Key, T, Hash, Pred, Alloc>& - - - unordered_multimap<Key, T, Hash, Pred, Alloc>& - - void - - x.swap(y) - - - - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. - - - - -
-
diff --git a/doc/src/boost.xml b/doc/src/boost.xml new file mode 100644 index 00000000..457d5047 --- /dev/null +++ b/doc/src/boost.xml @@ -0,0 +1,9 @@ + + + + The Boost C++ Unordered Containers Library Documentation + + + + diff --git a/doc/src_code/insensitive.cpp b/doc/src_code/insensitive.cpp deleted file mode 100644 index a9e4b7b0..00000000 --- a/doc/src_code/insensitive.cpp +++ /dev/null @@ -1,78 +0,0 @@ - -// Copyright 2006-2007 Daniel James. -// Distributed under 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) - -#include -#include -#include - -//[case_insensitive_functions - struct iequal_to - : std::binary_function - { - bool operator()(std::string const& x, - std::string const& y) const - { - return boost::algorithm::iequals(x, y); - } - }; - - struct ihash - : std::unary_function - { - bool operator()(std::string const& x) const - { - std::size_t seed = 0; - - for(std::string::const_iterator it = x.begin(); - it != x.end(); ++it) - { - boost::hash_combine(seed, std::toupper(*it)); - } - - return seed; - } - }; - - struct word_info; -//] - - struct word_info { - int tag; - explicit word_info(int t = 0) : tag(t) {} - }; - -int main() { -//[case_insensitive_dictionary - boost::unordered_map - idictionary; -//] - - BOOST_TEST(idictionary.empty()); - - idictionary["one"] = word_info(1); - BOOST_TEST(idictionary.size() == 1); - BOOST_TEST(idictionary.find("ONE") != idictionary.end() && - idictionary.find("ONE") == idictionary.find("one")); - - idictionary.insert(std::make_pair("ONE", word_info(2))); - BOOST_TEST(idictionary.size() == 1); - BOOST_TEST(idictionary.find("ONE") != idictionary.end() && - idictionary.find("ONE")->first == "one" && - idictionary.find("ONE")->second.tag == 1); - - idictionary["One"] = word_info(3); - BOOST_TEST(idictionary.size() == 1); - BOOST_TEST(idictionary.find("ONE") != idictionary.end() && - idictionary.find("ONE")->first == "one" && - idictionary.find("ONE")->second.tag == 3); - - idictionary["two"] = word_info(4); - BOOST_TEST(idictionary.size() == 2); - BOOST_TEST(idictionary.find("two") != idictionary.end() && - idictionary.find("TWO")->first == "two" && - idictionary.find("Two")->second.tag == 4); - - return boost::report_errors(); -} diff --git a/doc/unordered.qbk b/doc/unordered.qbk deleted file mode 100644 index f2a5b22d..00000000 --- a/doc/unordered.qbk +++ /dev/null @@ -1,30 +0,0 @@ -[/ Copyright 2006-2007 Daniel James. - / Distributed under 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) ] - -[library Unordered Associative Containers - [quickbook 1.3] - [authors [Maitin-Shepard, Jeremy B.], [James, Daniel]] - [copyright 2005 2007 Daniel James] - [purpose std::tr1 compliant hash containers] - [id unordered] - [dirname unordered] - [license - Distributed under 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] - ] -] - -[section Warning] -This documentation is a work in progress, and is often incomplete, incoherent -and, worst of all, incorrect. Don't take anything in it seriously. -[endsect] - -[include:unordered intro.qbk] -[include:unordered buckets.qbk] -[include:unordered hash_equality.qbk] -[include:unordered comparison.qbk] -[include:unordered rationale.qbk] - -[xinclude ref.xml] diff --git a/index.html b/index.html new file mode 100644 index 00000000..ad4049d4 --- /dev/null +++ b/index.html @@ -0,0 +1,9 @@ + + + + + +Automatic redirection failed, please go to +
doc/html/index.html + + diff --git a/project-root.jam b/project-root.jam new file mode 100644 index 00000000..2fe494f5 --- /dev/null +++ b/project-root.jam @@ -0,0 +1,58 @@ +# +# Copyright (c) 2006 João Abecasis +# +# Distributed under 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) +# + +## +## IMPORTANT NOTE: This file MUST NOT be copied over a boost installation +## + +path-constant top : . ; + +import modules ; +import path ; + +local boost-root = [ modules.peek : BOOST_ROOT ] ; +local math-header-include = $(top)/../.. ; + +if ! $(boost-root) +{ + local boost-search-dirs = [ modules.peek : BOOST_BUILD_PATH ] ; + + for local dir in $(boost-search-dirs) + { + if [ path.glob $(dir)/../../../ : boost/version.hpp ] + { + boost-root += $(dir)/../../../ ; + } + } + + if $(boost-root) + { + boost-root = [ path.make $(boost-root[1]) ] ; + } + else + { + ECHO "Warning: couldn't find BOOST_ROOT in" $(boost-root) ; + } +} + +use-project /boost/unit_test : $(boost-root)/libs/test/build ; + +project unordered + : requirements + $(boost-root) + : # build everything in ./bin.v2 + build-dir bin.v2 + ; + + + + + + + + From e73b90fa86d2d38cf2a06e0ab7993197cfbc756c Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 19 May 2007 20:04:14 +0000 Subject: [PATCH 071/175] ** empty log message ** [SVN r4139] --- test/objects/minimal.hpp | 2 +- test/unordered/constructor_tests.cpp | 5 +++-- test/unordered/erase_equiv_tests.cpp | 12 ++++++++---- test/unordered/insert_tests.cpp | 5 +++-- test/unordered/unnecessary_copy_tests.cpp | 8 ++++++++ 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index 1d8d8452..31d26d5d 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -79,7 +79,7 @@ namespace minimal T* ptr_; - ptr(T* ptr) : ptr_(ptr) {} + ptr(T* x) : ptr_(x) {} public: ptr() : ptr_(0) {} diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 18fd0194..34839530 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -244,9 +244,10 @@ void map_constructor_test(T* = 0) { std::cerr<<"map_constructor_test\n"; - typedef std::list > list; + typedef std::list > list; test::random_values v(1000); - list l(v.begin(), v.end()); + list l; + std::copy(v.begin(), v.end(), std::back_inserter(l)); T x(l.begin(), l.end()); diff --git a/test/unordered/erase_equiv_tests.cpp b/test/unordered/erase_equiv_tests.cpp index a36e6d80..d8f11e1a 100644 --- a/test/unordered/erase_equiv_tests.cpp +++ b/test/unordered/erase_equiv_tests.cpp @@ -109,8 +109,10 @@ void two_equivalent_item_tests() template bool compare(Range1 const& x, Range2 const& y) { - list a(x.begin(), x.end()); - list b(y.begin(), y.end()); + list a; + list b; + std::copy(x.begin(), x.end(), std::back_inserter(a)); + std::copy(y.begin(), y.end(), std::back_inserter(b)); a.sort(); b.sort(); return a == b; @@ -119,7 +121,8 @@ bool compare(Range1 const& x, Range2 const& y) template bool general_erase_range_test(Container& x, int start, int end) { - list l(x.begin(), x.end()); + list l; + std::copy(x.begin(), x.end(), std::back_inserter(l)); l.erase(boost::next(l.begin(), start), boost::next(l.begin(), end)); x.erase(boost::next(x.begin(), start), boost::next(x.begin(), end)); return compare(l, x); @@ -131,7 +134,8 @@ void erase_subrange_tests(Container const& x) for(std::size_t length = 0; length < x.size(); ++length) { for(std::size_t position = 0; position < x.size() - length; ++position) { Container y(x); - list init(y.begin(), y.end()); + list init; + std::copy(y.begin(), y.end(), std::back_inserter(init)); if(!general_erase_range_test(y, position, position + length)) { BOOST_ERROR("general_erase_range_test failed."); std::cout<<"Erase: ["< > list; + typedef std::list > list; test::random_values v(1000); - list l(v.begin(), v.end()); + list l; + std::copy(v.begin(), v.end(), std::back_inserter(l)); X x; x.insert(l.begin(), l.end()); diff --git a/test/unordered/unnecessary_copy_tests.cpp b/test/unordered/unnecessary_copy_tests.cpp index 395a87fe..19255ba2 100644 --- a/test/unordered/unnecessary_copy_tests.cpp +++ b/test/unordered/unnecessary_copy_tests.cpp @@ -16,10 +16,18 @@ private: count_copies& operator=(count_copies const&); }; +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +namespace boost { +#endif + std::size_t hash_value(count_copies const& x) { return 0; } +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +} +#endif + bool operator==(count_copies const& x, count_copies const& y) { return true; } From 81787f114ff6dce927d7f6e35e07bc1585f9575a Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 19 May 2007 20:16:07 +0000 Subject: [PATCH 072/175] Rename release => release.sh to make it clear that it's a shell script. [SVN r4140] --- release.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 release.sh diff --git a/release.sh b/release.sh new file mode 100755 index 00000000..923557aa --- /dev/null +++ b/release.sh @@ -0,0 +1,26 @@ +TARBALL_DIR=../tarballs +UNORDERED_DST=$TARBALL_DIR/unordered + +mkdir -p $TARBALL_DIR +rm $TARBALL_DIR/unordered.tar.gz +rm -rf $TARBALL_DIR/unordered + +svn export . $UNORDERED_DST + +cd $UNORDERED_DST/doc +bjam +cd - + +mkdir $UNORDERED_DST/doc/html/images/ +cp $BOOST_ROOT/doc/html/*.css $UNORDERED_DST/doc/html/ +cp $BOOST_ROOT/doc/html/images/*.png $UNORDERED_DST/doc/html/images/ + +rm -r $UNORDERED_DST/bin.v2 +rm $UNORDERED_DST/release + +cd $TARBALL_DIR +tar -czf unordered.tar.gz unordered +cd - + +rm -r $UNORDERED_DST + From 434a0c8021f317558e837ac712beb0d3e9e0aa0c Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 19 May 2007 20:17:29 +0000 Subject: [PATCH 073/175] And change release.sh so it deletes the copy of itself with the new name. [SVN r4141] --- release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.sh b/release.sh index 923557aa..2f2e3d1c 100755 --- a/release.sh +++ b/release.sh @@ -16,7 +16,7 @@ cp $BOOST_ROOT/doc/html/*.css $UNORDERED_DST/doc/html/ cp $BOOST_ROOT/doc/html/images/*.png $UNORDERED_DST/doc/html/images/ rm -r $UNORDERED_DST/bin.v2 -rm $UNORDERED_DST/release +rm $UNORDERED_DST/release.sh cd $TARBALL_DIR tar -czf unordered.tar.gz unordered From bd1477fa257400adfa7b015fe518a64d67068a27 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 20 May 2007 16:48:52 +0000 Subject: [PATCH 074/175] Add a couple of notes about the updated issues/proposals in the post-Oxford mailing (more to come...) [SVN r4145] --- doc/rationale.qbk | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 doc/rationale.qbk diff --git a/doc/rationale.qbk b/doc/rationale.qbk new file mode 100644 index 00000000..dbc6e089 --- /dev/null +++ b/doc/rationale.qbk @@ -0,0 +1,147 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[def __wang__ + [@http://www.concentric.net/~Ttwang/tech/inthash.htm + Thomas Wang's article on integer hash functions]] + +[section:rationale Implementation Rationale] + +The intent of this library is to implement the unordered +containers in the draft standard, so the interface was fixed. But there are +still some implementation desicions to make. The priorities are +conformance to the standard and portability. + +The [@http://en.wikipedia.org/wiki/Hash_table wikipedia article on hash tables] +has a good summary of the implementation issues for hash tables in general. + +[h2 Data Structure] + +By specifying an interface for accessing the buckets of the container the +standard pretty much requires that the hash table uses chained addressing. + +It would be conceivable to write a hash table that uses another method. For +example, an it could use open addressing, and use the lookup chain to act as a +bucket but there are a some serious problems with this: + +* The draft standard requires that pointers to elements aren't invalidated, so + the elements can't be stored in one array, but will need a layer of + indirection instead - loosing the efficiency and most of the memory gain, + the main advantages of open addressing. + +* Local iterators would be very inefficient and may not be able to + meet the complexity requirements. + +* There are also the restrictions on when iterators can be invalidated. Since + open addressing degrades badly when there are a high number of collisions the + restrictions could prevent a rehash when it's really needed. The maximum load + factor could be set to a fairly low value to work around this - but the + standard requires that it is initially set to 1.0. + +* And since the standard is written with a eye towards chained + addressing, users will be suprised if the performance doesn't reflect that. + +So chained addressing is used. + +For containers with unique keys I store the buckets in a single-linked list. +There are other possible data structures (such as a double-linked list) +that allow for some operations to be faster (such as erasing and iteration) +but the possible gain seems small compared to the extra memory needed. +The most commonly used operations (insertion and lookup) would not be improved +at all. + +But for containers with equivalent keys a single-linked list can degrade badly +when a large number of elements with equivalent keys are inserted. I think it's +reasonable to assume that users who choose to use `unordered_multiset` or +`unordered_multimap` do so because they are likely to insert elements with +equivalent keys. So I have used an alternative data structure that doesn't +degrade, at the expense of an extra pointer per node. + +This works by adding storing a circular linked list for each group of equivalent +nodes in reverse order. This allows quick navigation to the end of a group (since +the first element points to the last) and can be quickly updated when elements +are inserted or erased. The main disadvantage of this approach is some hairy code +for erasing elements. + +[h2 Number of Buckets] + +There are two popular methods for choosing the number of buckets in a hash +table. One is to have a prime number of buckets, another is to use a power +of 2. + +Using a prime number of buckets, and choosing a bucket by using the modulous +of the hash functions's result will usually give a good result. The downside +is that the required modulous operation is fairly expensive. + +Using a power of 2 allows for much quicker selection of the bucket +to use, but at the expense of loosing the upper bits of the hash value. +For some specially designed hash functions it is possible to do this and +still get a good result but as the containers can take arbitrary hash +functions this can't be relied on. + +To avoid this a transformation could be applied to the hash function, for an +example see __wang__. Unfortunately, a transformation like Wang's requires +knowledge of the number of bits in the hash value, so it isn't portable enough. +This leaves more expensive methods, such as Knuth's Multiplicative Method +(mentioned in Wang's article). These don't tend to work as well as taking the +modulous of a prime, and the extra computation required might negate +efficiency advantage of power of 2 hash tables. + +So, this implementation uses a prime number for the hash table size. + +[h2 Active Issues and Proposals] + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2257.html + Removing unused allocator functions]] + +This proposal suggests removing the `construct`, `destroy` and `address` +member functions - all of which Boost.Unordered calls. It's near trivial +to replace the calls with the appropriate code - and will simplify the +implementation. + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#431 + 431. Swapping containers with unequal allocators]] + +I followed Howard Hinnant's advice and implemented option 3. + +There is currently a further issue - if the allocator's swap does throw there's +no guarantee what state the allocators will be in. The only solution seems to +be to double buffer the allocators. But I'm assuming that it won't throw for now. + +Update: the comittee have now decided that swap should do a fast swap if the +allocator is Swappable and a slow swap using copy construction otherwise. In +the future I develop support for concepts and do this, but what should I do for +the current implementation and, in the future, compilers without concepts? I +should probably change it to a slow swap. + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#518 + 518. Are insert and erase stable for unordered_multiset and unordered_multimap?]] + +In this implementation, erase is stable. All inserts are stable, except for +inserting with a hint, which has slightly surprising behaviour. If the hint +points to the first element in the correct equal range it inserts at the end of +the range, for all other elements in the range it inserts immediately before +the element. I am very tempted to change insert with a hint to just ignore the +hint completely. + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#528 + 528. TR1: issue 6.19 vs 6.3.4.3/2 (and 6.3.4.5/2)]] + +In the current implementation, for `unordered_set` and +`unordered_multiset`, `iterator` and `const_iterator` have the same type and +`local_iterator` and `const_local_iterator` also have the same type. This makes +it impossible to implement the header exactly as described in the synopsis, as +some member functions are overloaded by the same type. + +The proposed resolution is to add a new subsection to 17.4.4: +[:An implementation shall not supply an overloaded function signature specified in any library clause if such a signature would be inherently ambiguous during overload resolution due to two library types referring to the same type.] +So I don't supply the `iterator` overloads. + +[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#560 + 560. User-defined allocators without default constructor]] + +This implementation should work okay for an allocator without a default +constructor, although I don't currently test for this. + +[endsect] From ef2f5116dd6b1291a3e6201d3d3a83c26e7cb358 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 20 May 2007 17:37:27 +0000 Subject: [PATCH 075/175] It is currently proposed that insert, erase and rehash should be stable. Change insert(hint, value) so that it inserts at the end of a group of equivalent keys (all the other functions were already stable). [SVN r4146] --- doc/rationale.qbk | 7 +- .../unordered/detail/hash_table_impl.hpp | 9 ++- test/unordered/Jamfile.v2 | 1 + test/unordered/insert_stable_tests.cpp | 74 +++++++++++++++++++ 4 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 test/unordered/insert_stable_tests.cpp diff --git a/doc/rationale.qbk b/doc/rationale.qbk index dbc6e089..a688c18c 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -118,12 +118,7 @@ should probably change it to a slow swap. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#518 518. Are insert and erase stable for unordered_multiset and unordered_multimap?]] -In this implementation, erase is stable. All inserts are stable, except for -inserting with a hint, which has slightly surprising behaviour. If the hint -points to the first element in the correct equal range it inserts at the end of -the range, for all other elements in the range it inserts immediately before -the element. I am very tempted to change insert with a hint to just ignore the -hint completely. +The current proposal is that insert and erase are stable - so they are here. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#528 528. TR1: issue 6.19 vs 6.3.4.3/2 (and 6.3.4.5/2)]] diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 9350719c..1bca2414 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1561,6 +1561,13 @@ namespace boost { return insert(v); } else { + // Find the first node in the group - so that the node + // will be inserted at the end of the group. + + local_iterator_base start(it.local_); + while(prev_in_group(start.node_)->next_ == start.node_) + start.node_ = prev_in_group(start.node_); + // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). node_constructor a(this->allocators_); @@ -1574,7 +1581,7 @@ namespace boost { // Nothing after this point can throw link_ptr n = a.release(); - this->link_node(n, it.local_); + this->link_node(n, start); return iterator_base(base, n); } diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index c4ce121c..07186630 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -18,6 +18,7 @@ test-suite unordered-tests [ run copy_tests.cpp ] [ run assign_tests.cpp ] [ run insert_tests.cpp ] + [ run insert_stable_tests.cpp ] [ run unnecessary_copy_tests.cpp ] [ run erase_tests.cpp ] [ run erase_equiv_tests.cpp ] diff --git a/test/unordered/insert_stable_tests.cpp b/test/unordered/insert_stable_tests.cpp new file mode 100644 index 00000000..51b16e78 --- /dev/null +++ b/test/unordered/insert_stable_tests.cpp @@ -0,0 +1,74 @@ + +// Copyright 2007 Daniel James. +// Distributed under 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) + +#include +#include +#include + +#include + +struct member { + int tag1_; + int tag2_; + + member() : tag1_(0), tag2_(0) {} + member(int t1, int t2) : tag1_(t1), tag2_(t2) {} + + friend bool operator==(member const& x, member const& y) { + return x.tag1_ == y.tag1_; + } + + friend bool operator!=(member const& x, member const& y) { + return x.tag1_ != y.tag1_; + } +}; + +std::size_t hash_value(member const& x) { + return static_cast(x.tag1_); +} + +void stable_insert_test1() { + boost::unordered_multiset x; + + x.insert(member(1,1)); + x.insert(member(1,2)); + x.insert(member(1,3)); + + boost::unordered_multiset::const_iterator it = x.begin(), end = x.end(); + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 1); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 2); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 3); ++it; } + BOOST_TEST(it == end); +} + +void stable_insert_test2() { + boost::unordered_multimap x; + typedef boost::unordered_multimap::const_iterator iterator; + + iterator it = x.insert(x.end(), std::make_pair(member(1,1), 1)); + it = x.insert(it, std::make_pair(member(1,2), 2)); + it = x.insert(it, std::make_pair(member(1,3), 3)); + + it = x.begin(); + iterator end = x.end(); + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 1 && it->second == 1); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 2 && it->second == 2); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 3 && it->second == 3); ++it; } + BOOST_TEST(it == end); +} + +int main() +{ + stable_insert_test1(); + stable_insert_test2(); + + return boost::report_errors(); +} From b3df9afd7a1d47fd520d5cebd4b26f060dbe8274 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 20 May 2007 17:40:15 +0000 Subject: [PATCH 076/175] Remove the note about issue 560, as it has been marked NAD (not a defect). [SVN r4147] --- doc/rationale.qbk | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/rationale.qbk b/doc/rationale.qbk index a688c18c..bf4ead1f 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -133,10 +133,4 @@ The proposed resolution is to add a new subsection to 17.4.4: [:An implementation shall not supply an overloaded function signature specified in any library clause if such a signature would be inherently ambiguous during overload resolution due to two library types referring to the same type.] So I don't supply the `iterator` overloads. -[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#560 - 560. User-defined allocators without default constructor]] - -This implementation should work okay for an allocator without a default -constructor, although I don't currently test for this. - [endsect] From 22a0d18f850303b324b979144beb0e5fa53503ec Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 20 May 2007 17:41:03 +0000 Subject: [PATCH 077/175] Rehash is also required to be stable. [SVN r4148] --- doc/rationale.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/rationale.qbk b/doc/rationale.qbk index bf4ead1f..3982032a 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -118,7 +118,7 @@ should probably change it to a slow swap. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#518 518. Are insert and erase stable for unordered_multiset and unordered_multimap?]] -The current proposal is that insert and erase are stable - so they are here. +The current proposal is that insert, erase and rehash are stable - so they are here. [h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#528 528. TR1: issue 6.19 vs 6.3.4.3/2 (and 6.3.4.5/2)]] From fe9e2728f6f9726e74dbaffcdd31ebf77e5b8047 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 26 May 2007 00:00:08 +0000 Subject: [PATCH 078/175] Add missing 'this->' for a couple of template dependant function calls. [SVN r4256] --- include/boost/unordered/detail/hash_table_impl.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 1bca2414..17d31c5c 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1565,8 +1565,8 @@ namespace boost { // will be inserted at the end of the group. local_iterator_base start(it.local_); - while(prev_in_group(start.node_)->next_ == start.node_) - start.node_ = prev_in_group(start.node_); + while(this->prev_in_group(start.node_)->next_ == start.node_) + start.node_ = this->prev_in_group(start.node_); // Create the node before rehashing in case it throws an // exception (need strong safety in such a case). From c5b86a0ff84cedffdc1b17979684f8bb4202f758 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 26 May 2007 00:00:54 +0000 Subject: [PATCH 079/175] Remove a couple of pointless comparisons. [SVN r4257] --- test/unordered/constructor_tests.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 34839530..916a0810 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -26,7 +26,6 @@ void constructor_tests1(T* = 0) { T x(0, hf, eq); BOOST_TEST(x.empty()); - BOOST_TEST(x.bucket_count() >= 0); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); @@ -116,7 +115,6 @@ void constructor_tests1(T* = 0) { T x(0, hf, eq, al); BOOST_TEST(x.empty()); - BOOST_TEST(x.bucket_count() >= 0); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.get_allocator(), al)); From 42f319eb6da4fe77991ccc657ee7bf660f2e7ea6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 31 May 2007 19:20:41 +0000 Subject: [PATCH 080/175] And check in working versions of the binary files. [SVN r4392] --- doc/diagrams/buckets.dia | Bin 0 -> 1858 bytes doc/diagrams/buckets.png | Bin 0 -> 13869 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/diagrams/buckets.dia create mode 100644 doc/diagrams/buckets.png diff --git a/doc/diagrams/buckets.dia b/doc/diagrams/buckets.dia new file mode 100644 index 0000000000000000000000000000000000000000..b28596c95f278e62cee4c2580a41095e868d2bf8 GIT binary patch literal 1858 zcmb2|=3oE==C^aBi*H#-w7vhVEm7s&sv@53|zV2act;o(fW@dTk zO@92zGK(JqOef?w2pXR~5>m5Xwf@rY2%&S&_5D9@_^vR?q+{`GJI5XOg)geiW4rif z*Z=SR_wB0w>YInpU*7oN^WSXMe|yyJ1(&{hv8#T5YPbL4hmRjCbIlL7?-mwe^PbZ6 zaDSb|FXzaN8~b;@`fA4P^Y={n`>F4LMNVURoT^qe(O&%e-;MS^ExjM5n!FWXda7u$ zMbG4W&GK>I?e9EY|8E12@tv;EUT<3Mg&({8u)pn`{PWSD2^Y90h8#X8UMO`!>E`D} zVM|o|e(elgrZe@F)5SN>4u}7CF7$dT{W`)|{g&3Gvwu^>TuNncnk8No5UHFJbW?)w zyG5FrvVQKS{dXk7^jfBRCp>NXQYNS#`G`F-wn*mq!Vim`OueT)DwtdHsNLvj-0EwB zTX%48(z#doue@%?zNJh@Z?C?$E&u#n)q`KY_U#T`q+$|GZAwwAZGCJXa-`Q+&(LjN=~2GqX4%YX9^X?p zK3o^Jep+?*VUhgMGgi|%{-#fy@^Js!++E>u4~mZ5%xJCn(?266_3OV=e==UFU%q$z z-u53W_b=l+DRc14uK()y^5$i=rWja$x_T>JaQ!3>{)nx+z8+kp`=uhLcg~68?~5Zf zx?ac|9y)ix^^Rnq`(LlzpPvmJ1-pK}Rq-3v$)?SmF zBUACnQ6>6G*=~NbFHTq4k2XG^fA4tzcHRj)Z#&Me{#`Klh4jqzP5Rr;CGX$oP{zyr z^{cO7wrsBxGB%(Q_2w-2FDgVf|{HeJ~E_0`B%~r0r0dnq(TR`y z^62pX?hlSB2anv(Id=Hm;)SnGO5}TN_Wa${KYiV@r_w>64Hm9CzSl^$@#Dv$PjPu- zXI*c96}?y#x9yc&RIkg*;)b8f9WI9%l3yCSHznSgxc;u7{L{Y47F~I+CTFuZ#=m`~ zdS~$~{m-eJEI!q(W1QP2T6XDB(6+vkLms@W9A+f3EjuT^Oh)$PYsLdp!VgT5 zd89XO*@}gl6|+9^_|BcSEXMX5hePnYu4P{JEQd9>2`}fUHMcps<-vAVh7cp)Fqfx} ze*TecE6<6qw9);U&1^7LM03HZy)i2`@q`?74mGvr4Rs6_y`D0OAtH2#%KZY*9hq9$ z<`I?e+17Aen`9!>v}TWJk%xM$Xlb6x2O^O~iT4--k?VHTK4A*~A+)H{mP4 z^cE}KD8W@H)f8`>iryIda&ke=(eh2ATkrEnXGWS#n?B)!OqoTomA@Q!;r5Wl=Q@6& z5*B@yR&%E|T4~=d2{Zlans{T))@@%G#rPkN5m}uXIVo%jFGqF__q8h}5-Mw7u3B?& zYhR)7`A3$FPq*?~vgRDwRghH7yK!!U=Ol>~Yw48FTcmWDO(V9YZfLl0E$)8XrgPRu z%xtso>1k}!oXMNFm}SQ8n8Uf^ALqK+dVl7bcKe3e_K)1p&C+(fWw~>VXZu|4?SYG~ zCQ7b3yh*pcmBsw~+XrqZ|4ZK4*Pgh+Lps>|@*=AuiL>E5bGQFKGbw9Jh_cqkJ2A7B zj*EPOA=*AKHeWFXj>0xZ&xln8_3F)%LCbVo6vf2JwU2;qM@{e~me~F3x?IYvun;s#NVB~5wfBBIWlkHg#Ze-X8J$MogJ+zT{d+x*hyW#y;3=^Ot{lz$bmY||wEh*XGD~w#UtC%J{%WxQ>`t>fq{X!*vT`5gM;JtL;nX1 z3=EtF9+AZi4D1&`m~m(K^Z5)63Jjhujv*Cu-rlWr@csGU{=@gRx2vMJDCSOcndZrx ztiIUrrc$+9uyL|w8&!{hXP< zUuW;U`>yKU%b!1vzqNW^d+&Lu?faKH@68n%Kwv>s#1(s2SJzW-*%=s4OlWOoRdn&a zd6$8Kq0;dPGlPMck{mB{86>y``56vGbW|`f9CIRG zX_EJ5Bh%F!9sZkcZVh-o&1tSEb7>e)X4=!kfgqDTOGW$zIan^4AF9w1^FAdOkY2dp zTvwx&cH5WIV_+4;*{$ZX=ZimkgGE64QHQyri>F?RJSlkoSO2tW*Cr@B=iT48wn5kI zwq)gZ$B!AI&wu>*5xCgx<%<_HX3VIls@hlcGw9yjK#8cUKM%EXpF4N%)~#E|jvX^j zJ2N3|j_|I9p>s8v8WT=TP&D!3e=2{Jn}w;-As{gD;j?FE#m{_}es}4VouK{eO6BWo zYfn$p)z;PBTmOIG!i5Xl+uMb#m5aZuEcWm?F-g@sZcjyEK){9FmO&YU=*VPf)S zu3O%n9f_HlJBy$DIdRNC|NZml?AzPcu3Pu+%uM6#tgM!|244eWWlC0jo?rjZvg%6) zU;E{4xwj`wm~iDvNaXvapDr#u=jrMB?%liGTU#z(zaGCa$+fI(+nVn|KVJl-J9$)A zRysHwXk=zj*;TyBaeBw|qMf_T-oCoNe*TN2OiWDg?(LmDb?VnACnqoWpKtbey>a@v z9$D+M@9*w*i|ebku&}bGrlh2#rcRwQB_lI)ZPeDTKjxnVH}_w2T^v1ESV)MCPe$Xa z-bJCcuVR9OH`o6DHc{E#Yj^k;=gC&TfBw{s+Oi^gd)}s-+B1KBclMlBIDcv9>1Fe~ zs;a6qH8uVG{I2}=oh@3InVLFv#*B>g^zVMkv(5AGZA^Ce@!{EOrMM*dcwb>*p>6dy zm6j<}rdU{5?5X(p=*Ex3<1?$R_0S@&a|xk^`%=}|JJQrUVF0NJIuAev2WGytTXWUT=I*OGbLRXx zYkq%@UG1;S{`ONV_S@}P&Qkhs|Eg81%HG_VIAOwv>+$uTd$QlZ*L0lAso26JWs>p! z-rg(AnRu1w>8WRxl$4~MpI7_#mgy^Rv)QrH(cXLYw6%rx((>~DJvlk~d$_-+r)Axr z3J(vD%c`G0f3CEVn}2@!zUWVH_MKYbIk#Wl-Y)yv8sqeHC(Q1@Y*s5>CLtlQCSv2G zUteEOdc(Q*;=*&8nVEa5z6#A-x%_si-7c4!+}yYGY^$xx-}%(OeDFX(Na)bhqVBG) zXXoeZ$DBER`t7`=kxZ#@6XJ)uRk}(va+($ zMowQ{-M!-MUf#@i{pI1|QBqdsm$&oTyR-cLz0K+8rKF@l z+2`iX$Q>VkeSQ7*?ORzH8LRSlcUA^3U-q`-#f68To}PB^li}pyQE6Edx3}uXhD1F* zyh5L+bWLtdF$4# zn|3))Tdwxin`h6=YJZi;%F4RCAK$cT(~1=umTxx&U08n3Wcicgk89hD&c|+(Td_H5 z!MU!3bBaGQFent0T3V7+u-!Po%nSz8c={e0o?U0L#6Hb&?$Jgo zZMIiyBraW-bSF@zPdey3c|q5Xn0NQ<|JOc0H}}=6EWPQ`!NHfe`XIoqE{`dFy z=9@nbGE5DOjI8|o>+6~|Ypl!Pg@lG)y>=}uJbd=dnURr^k%=W#i8 zd!+Wq}qcqeqWkzh1p?;ld4amk#d#epOsl)N{k8)>hVu?*!Z0+S+d1xY5zkapT5~ z?fLi3cJJA??c1B1o0*xIe0+RV?yU$4=r_<({Sey?XW9H9Z}jCHwq4J3F7BpTB<9Dy5bUH*@^`&wqV=z5LCM zz$RbqR-t;;M~@$i>&48NKHZ(e(A@m{moI-_tzPdUGtb<_L`7Bg@6YG+_y7B){dK2x z#)Soce}6B3e=l|upN5c9oZ*zv^hSpb#n1if>+AKVuU@zA-J?g3UcP+!?%lbE1ukns z{vrn)6(DH-d?ZES*q zf;beDkM(f!@x8mdySzJUN7dI?yGmaR2@7*7E_Uy?tNpd5ug}kW_d<_L*CRO}R(yGJ zQ7?AalI6>1&z}AM=4N$u_2bvChtH`|Z{d-%`SIlB4ZP`ntQbGxrHQvGGckyu7runVnxsS~`A5!NbY^ zc7pSErD(Of?Qu|u*qGG1e0jRBZ}0BizZbjrKRY*9`{2|(Tid<5(c6w3IT8^O5wp8& zt^a(xojZ4ada>bE)t?_99~^9ce}Dh|t=ZRmdU)#V>q|>ZPtGzjG2!9o_xJL8RA7;Q zeqL!|p{Bn6^xS+#7jJLx;=)2u_C7ID`ThO<@87)9@y(8li?g(}w5|Gb;`Hg?Z#JLL zyT4C&ao(kgPReyJFD;GUo_F=yH9mfR`{HLkFD|E_m9nq9GRJSL?6F-Q}g&3ue9{+)#2;=B#oD?Tj%%7 zjPc0CnKLb`zPxz#DobB~z4vszt+}_uewp3goUcI_= z#|{w@5gs`k3pF*jw(T7^Z{LpJTXpry6%$+A*s%MK3>QzGI(4jH{{OG9uQM+%dwYL> ze4Fm;moJYVIl?1l(y_}VA~;z2@UfT9j)L;Cva&KVYZfjn{Pg5x;9|Fv8&YFq@4k5P z;^EladzAn>Q~<`u&B4&Ot$w=FN+9aXIqs?d{at z76*PmJ2y8vCdQ`Z#f23sR;2lx#qFt>_;vn`8#mIVuV1*((cQgy^XA=^pPwCSKC;a&F!?yZc zMrNk0b(xR1x3}Ev7k77;r=+MTJ~=L5ucM-8vFbC=bxXxurY0sfg^yZd@>jmQ zy88O}`}OA^e-zV+P}slrLS#xxN?hE$xpVJMQuS7en0x&3)M?Y=b{0K7E?@uV-rnCo zKRs&rTB>#&fJDJr!~4R^Z#Y2%gVP_(nN`}OPBr+Z-m0TVhqPsZ2(J$k}^ zhs)#7KN%SrTPB!hU-PJ`UlNd-`gDqB@Vk3^e{W=6vu2H$Zj=kh&7H-|r`%?$nD3zQ zZ|+O=Mu8V(xp!f9sz-d6Is9-pyOL{N`8$&UyO& z?(Xu($9SDOmMweMDXhL?{=+k8d<5Gy-+%bUbsEeW1pb^--Tbk zUXSno`C-+UwDa@Uu3K04>&wecn~aX;OjJ8J&(@n`reSj1k;9f5Sy^RoZ(Tiq{{8j% z`la_4&J+1R!*8xt?Z>0yTeogK-Y-8rr<*}uSXS1TW2RZ|tQqYe4sox|%gdWuXI?lan7mdK45M{{6Uoy++Pr zFLeQq6+zoeI-2-r8KrW4eROqo_@e1c_HABd&&i8>69PJiwZEbaPbF2ORO*H){ zx47P(ijPeKNqKqiUcK_-khCmXGIgq`;*upxo*ZiBPRc94u)tAA?Dem&uOIQ`tkZfT z@ljGzvSor}Gn;0`K2fdmpx|K9oqUp`-M%d? zEgj5k=e<9cf$Qt%ocw(KXP*w8UbQ*x?4oz30l~q-eWE)5T*HNhC%?P9`}(zOMlX+L zTe{hNtquC4@c8equa_@eIC1i1;wATu8CNuNg8VwKUbyh#$H&JLCJ4NmWZHc+YG2LH z7oib4)7GxlomjFVxPd?G;`!pp$eAD5;$P{UwO*f`oP2b@QF?B!FNfgYn2xN8$w$1W zSqs^G|H4;VTAJmz;#%?1sa2;!nmB`IT?-2izI^Rk+0#=~WAv`yxbfq}L}g{So{Vj* zS>NVd3(g8?n64i`>3^GbeppJ1N~yv1{q_HsvHy$RT_$UiA)xr=?d|RE{c@tkm#>^J z&dkj8{_PW8)KifqQNGk?fvc;{^`c2!+}!Es=iOcG z&R;k$=IQ63OpOb6?TTWQWv)0r*SZ|kSGaz^{{LU=`Y+taozI>-x3Bnlp9OEx&KM_; z%ga+jW#1~rJ$gQGR@L8MTOAZKY=hSZAAc{@v+3rRZ8diL7cElK)y+LQN%hsMSN`*C z3JVKQosm+9;BNvnLyc(fX;3SOR_ZGKiy^Iz`!Bt6f$MJbogZx9h2&X1e!` z@3lf}i=Urs5y*O#*tv4$%B`7~(+)H+-rk;ne`7Mc;*w39J}sYLC$v}W>))m4dKgc* zTwfP^vR?eN%Y%;D^tWi}ivaZr!?d>*vp(UH|{!)L&Hh_v>|e=_-F;-?y*V z@2^Y$*}t>$bDFPj@7%d_XU>d-#{a96TULj!*ZNV${VMVF!De>tqQ$3Dv$9@2cn}a9 zTl?~o>P(Z0A0HB5x$Z7|>!Rak`^wign@vIc)vH%}dV0ORz3J!X{JdebK5nm&+|>Q` z`o6)3{`Yow3)kNJ@#*R5mO}gUMLiYL($dw{zc+2#wEy2P@4DzpdC_8?-*%?mQfHli zB&=d#VR8%&4XvvBwJG&ON;^OeUB+P6t<=#(;Ka%h|Nx6-cai$>|`I}Z*vzkTz@ z#UwR5+gtX7sfkI4U3vYB3yPoadzkhp-nY(L@qThg|D*4oX0Im)I659&>OFnS)~%A? zYO};lx7<59S$*a1eMhcr*^rf$)z;Q_FGIh#wR%VGN-Vb z$Nl<-WorKO)+}5I8j$$$@iD)i!lSYu-#MoX7Os1CYO1!SrRB3{&pyql3F&+y{qf3! zIqm%NV#RH`FW=tY-hckN|2&(WUt_YDT3A@{NE$6!v`9%u$48B~?DaKWOUs*=E`3Vo z{`0>6zj8}OWo2e|_UV*v2B)^Rw#{j0AAPKlpS|V5v$M0WuwK2nIolI8Z`-u|Bj4ZLynOZQ*R!+DLw-J4yJ}U|^>wi=6DmJH(@ZEX zah5I2zqe=SvSn^fS0i<%&6^i@eO>Hw-`Qf4l9kWT$+EMz_v_A?IkWP~3Bl6R(q-qO z`MsXMe}6u0Grz3Wm-F`jPgF;Dc6O$pn{)Bv#f8s|l7)8K|8jZi7ZEYz1-s;|xoPPOq$o0Yr>kbK{!_2W^u{;#jE!{g%q9p<<1i2KysnwyhT^ZD7?!|nY2 z^7ii@K2&@Z;ie#<6SZZ<#*G)RU*8_Q+;7u8eg-FwmzS2Fz8+tH_W9@Zv$IsMM{bsu zl3Kwz|N7eK>-%b}f985(&ma_Nv?!M7Ia0%32SQoqd+Ct}c`T9SBblj> zc0X*IUhJ}k`kU0m$Gb<36~8#$i_xg422dv<$!yRcJDZEdTv z_2~d-=fhJpga7?}KL5PkZ=Wwcm)16}U;n;CP&saIm1)6)18MFvGFN6_U*|j1Xz9j{ zg_oE4&aeNsGe?@eVt#k`>04W~D{bb@o;~|T`dZb3KR*gziJaY6`}@e8M6ahB84^b2 z<=?Na4mUS3@tJECx^4ck7`^Lv@7k)W9zAqO$;wKqynI8%n$OSAgA&5a%gdGB`waavytMS|*RSv1z4P?++>}*zdt0t?`Z*B} zmh9{6MEhYCba#JS}>DW@hoDBb+n8 z2s`*JT)1%Fym?!gi`h}Ia7y#vu+Y%juUEsbe6Ee&mcz-zGbPacrN_c%&iwoP zYJYxuT3T9K{QTTplgvpe-@kFrwXfgjKi}@<%a?PlN>80QAyC*Juf1gb`uTI_{J9z) z9~vHBT~Sf<^=i0~{JHx(i_;G@Fg7+eGBqkFC@9$2#2lRbktyJMUD?}POV_Ubdpm!> zrG>>D>vF$M%g)WSy}i_X`iT=Bva)kQt<{@1wOy+}Kl2R?ytvrCKkx1?)x-N|qi^5xGT9v(KC zS^Dn&M+TMIaWiMd1U5x0dV6`P`OKIwZ=N5=#?6~&o9EBlzJ2?~jfI=Fmn~bCcYmL) zmDQ~C&tJcMnR{o)#pB1_eS6DdMzr4&>T2@vzHumnKMT^SYj>(wi+&DE=`*@G! zS8w{G3KZQIHc z6CXlHyJpO&`1hwW%Pzuf_S=^)ch>!_;#6A_Cbrc5{O9NA&GYW;D1RUKFtC5uu3sM> z9=4R}>+9odKdh;zx6Dh|=2HIUWoPGD7K`b}=_E-o?+UD%8_l`&`pn~x-`?Jy|Nh?I zL!Ey)6mQ+S<>uzLYu7FrzU526;H6eyU*Eoc`z|l{&wnYvntyN4&0VFhudR(<$*J@8 z>sN8Tm={k#;L6V8=gU6cy>X-B`MJ4MrcC+q{H*ORZ6x_IR;}w%vi!!sb&(E{9R#0$w+O{C@*Ls$v+qRi$X=y1cK0Gy5`}{oH z-w%w!!ore|^+=j#U0D&h_{*0sOYW~wy13+AkNbS9>C>mbzrWw!*49>A`}N#~d=WZf z$NOa8zI}VVU*10Nj>XBokV}^@tNYAwsEs-^+dTi&6isFKK9T1u_N-c!b$?&2b@@9T z4Go1BpSf11IyydIUfg*f{If&+(Z`C9A3tVhW=2Lv^2^(;S-0+4(aspX>xT{q&c*3@KWW%>B{2nh+v$jTm#pKB?2zT(TDsR?pdA`KBllDWhAtOdgZWdYq@<-y zn>cY})z_@dPjdw>?W_ImJzWnpjkIoE-m5Dsuf$k&HFmHw{IIvNu}Mlw0=Yt1IQ8p+ z-d($@A|h_A3SG^@)VO`Sx#|BEObU+}8UF3RySse8UG1#{2NX0lSC+rOw<*`g!lL5l zrqulV`{tTtUON7+e1fiv4a0%`Ma9J{pT~ZGd;9xI_4y}~g%uSQ2va} zUK&n6t*fE&!(kj?Ee4xT+S9K5EL97y)lVZG3ER`+fBDx`SU&2 ztz~9d(QOBXM0+_p{5H0#PT-`R5Zb$c=|t7VyNd47Jre%ziP z$=ZJN?e1Q<@Zr_f)k=KdmV#ZKetw>6i-%c6L~xGT?fduZe}8*>WxBfmyqX^$9Ifkg+d-m*U=a-)~V+N0;k&7DduOB}GCLiQtVG>NgJ$LS0PQ{fg zR~p@X!LIm-alzKz8ylZKeR^q5*YU@!tgO1RyQcgq`t{}Iwr$(wqi0mD zn_9)-B-4ivu4%*`FQ;9?((HMr!HK$aR0u&wzl_| z$D1BLe8|+8aI8nNt*tF6FOUT^S0-R=y!q$n=kM?AG&V2*jbS`mzhc?4Ws4UlCna6l znC#BZ&Yqs09v&Y4aPeCC&wqXvXBkcS`T4o`)%VXpg$0LV%F|O*A3l7j?msVPTh7e} zMrOMmJ!|Cpe?4rMzqhB-IP;Q9)|teg2Ro%07@RmZZrBj9EoY{K!iO(kL=1Os`w|os zq@=7|T3YJt?EG}QD3_8P!-IH6M#jFrzJ7W8dFJ`^=9qo_@$vD=$?C#R*VaTT%k&1- zftF_czjWn_P1TnbeSLl@DNkmZW~bcy_xk$!{eM24-n7Zc+H?_zh5r<8|D1``T685`^B>ZaZZy$Eu>yg}WcA8XdEk+ZF` zss486;>C-%Z~uOIdAV+uqk_PZW5?>g-Au2m0~K_u!`6CvdM;d686*L+to`eYi;u6Z zjc#mg%*x7Y=ab#EdGlI zEWyCgarDxqPwnz`KOVNr$L+0}I&IptxpV(MIM{5Sf3IL?jNbI@yu7^J+}!N!*AE^T zq?`~qdPn#sXhn|0=@~O;?%cWaWT`+xfsqPTm(xh}=XxuM~!yDPf3Z{ObA+qLzXW?xR6v;!&(uA`Ju5Z#f;=F7etvDk{pkjb~@=?`=%mZseqEe0tZZbaBH@^`1F>tDWf>EFNK`Ko1Z&t3#SUuJ)1x!>Gb z*5&JjtPk^aRGfVI>Xlx^1_gcn{$HQwCJR5D&dbYtdb+;3v9Ym{5f?9S?edAz=6MlO zQSa{ToGftY`RBiX{@mJ@yL$C%>*8l;X3dgHv_50)AuccbN=86nL*3tBo|DyHzIqiE z9W8HB;Bc*aWlhP7;^5il`Ek2SR_c6P6Y6}#yYKYVcel2#j^AImNSvF4V@LJ(ys)rq zlhys_+11|qvj5y1OXIRPH}38(*N@vXLqJGGWXi;e6Q@oM4GLPcbm`Wtt6EMRr8#aw6w@Ml)Zj zSXXD~%bS~*x3;!!slNJZ>!pcmOI2(bE?&Ou?dd7F_rt@(?ZU#sD-~C5&%du%W3Qr; za=cGgTtBX)sA$o>oBQkQV`AnkU8<_6VqtOP;K7IW|9{Vaact#%VY~VBCr{p7^77Jj z{rJ4w+k7wGT_pSK%FZoYOw7%{pPOr)m6dgRn(ps^zuzxjv`FQi{;q}ob6d@FZeW#zYT-_8{I_+_PW`{A40a%XScXsD=SVlpMMdGqDv z=caIr>ut%te(v7AxIeKC{RcaxPQHBc;>|q>uV1s~&%<{4W4+Sq(_=%m ztgg%yD0+EGRjk{z;zPpC_lc`pI;OLf+BY;dI;NyNd3d;erTO=FcdNg>iF9^8+{nys zHn%DL@2{`lzJ254hKF&7hhJm=acx-H} zzyEp9$!dY!Ya=!;%D=x)-lnLey!`uz4;L<8l(eg<`2Ozh;X{X>Y&q@aIN$30Jlors zE?qiz?%bh6i&m|Abv?d*>SaA`*4kG-9GpBnF*}Qv+LUcB{k8oRcr|7FA)lE>s=B(m zmX?yj3r#`Y_v!lacBQYb9B5>Y-lg|S$L>m`;gXe`wHCc|KSaX9Mo>S1l=YVzr`~0J zYEkkcz{{%(R8s!@`7>x+gH<DL6EL`W6v6h&Dqx+bhjPcFo}uC6XBDypok zEG+EW>+9>Yv$I#PTGi9jvu4ejHEY-A=H%SDd-v^|H&gEVGTd@J!dz=x|L@O@jmd(7 zg68@6YW~;Hx2-Pw{Vn&^*H>3pfBo_$?d+_rb$@@Ay}hNmZ8pP~)(&n5?AS5qcD~Y*5*dqv1rsMuT(@rBzI}C(kvA_d_y7Fq)22J9Uqf;i>Zx=Di#&P4RU@m z*0i-9v-XaPj<&Y4;@ZEb=4X+M%aMu7?u!SV=ozv6QJbC1ZimK|i+YlgxWKPa4X48d4I5(T~AL>R8&-0&Bw#TBjf(QyXUIT7NszDV(!SC!djngCk;lp6vYd^X+P@ z^77X4M6-b$6krq|f8V`d?&i&#bFE6ff`fyfy=7-%@tJ3{bJM1xM@KqsCbMK74rV)~)6K^RHdIHto1tgbrvHeeK$}&FuV#4jp>3k(1$;(~;&&94t+$ z-qQq}%<}HsSRJnaGq>Yx+WQ+Dn+1df1U@`E%KbB!qajb=;{ucP^YixB{@ylq>eWTA z-A5w#Em;Cuj`a9g@9P^I7x(q?%|2^pWK>jC^y}a6_m3YxW@T#ZC_UaI`S{thu+Y%0 zsi(yxOXVF{6&^WeHZ?VID6S4$d+E|8p}i@wv3FO6uFlEHsj01f{OA!E7uTy-uYT0j zrKYBqm3@16clZ83pS-K9e-}SL*O>uYSfSD)V^y+Z-@drEYDS0jj*5W3J$v@_^ziVt zf4*COzccRB)z#t7Z9F&kRBnEeaqjHdxfX?sI#_>wd;9vylaQ#Ws$X9+J*#*YGzolM zAan8J#TJ3`^74z}k*{CBKHeugd*)2f+M9c;zvtfGmYSYEed^SY6?@F{@7>v1oc{mc z-^a&#gM+jGwO5{-W7#r6Sl#bL5930g8o zn}s-7n%=z0aq8&q?(R5x>hx)6XXlmI8yg${{rjg>bo1J^Z)av2FWP>4Ww1IoclV`B zm)QB`w(Pmiu!>!f-zy*@A|g2W^14`SXJ==@ZdX^=tJkgxIqfQc|L*y7^~t{cG8PjA zCQY5HI{6wy2A?3mmxHVeXhGVSBu-AwE8RaoKR^9yPZwwj`;(`qryFJRHarvfxPay9 z)2A&H($XL7E`HAUHR;EPhnsF~S-DbEamkuBK4H8};8eHlCiCY|J1{G@BBC{YkzI&I9wbYe0iz&^q}>B z|Nb@4zo(P*aL=ASo|Dx&@4a}x|GyuHqGfB^x&uUR@Qsx9aPyoyE`BMsL^B)|Qr*UcGvCaZyp-|G(cqK0XePwfijo z^K3jlJT6?k`19B6^{IP|8A@91!yPRnbgsR+x>{T>W=G=TwoYO7x;KUzxw&uc|NS_8 z_3GDWXJ@xesQ&&gu$a+H!Ng?C#*Kxywq&}wxlQ^m?XdKLhTN2AyWj8IykWzR%FoMQ zSX$lMnqB_&RcL&C{KftyUGu)aytX!aclrCY%{Nzvub;N(IKwJ7L4L2rOP6jfdmH6+ z;!rEMYd`n0#fvwWzP{Gf)TA4|ZO5)%QPI)anVBnBu9TFLva+($i`eks(o%0rOUt~x zJo)Uzi;G-q?CLKray{Eu%XUD;B9Qr%N%^}w!OQ(r)zs>~-z_&ZG+gAqHuL_zzn`9- zo~|E%@6y^evAe&WnyUTw)>du~jtP6#GiY(Re_Y^`m6gTLFK1$A*4NuR^|I!a6)RRO zSfF4wTUJm|v8tkq;Tfp#jf{$#XI0vDHZA|&o}bI-*GRK35gx`|LY>ZLUib* zotaVi^;M{}^z1KRzR1~D<=oh?aM`kDOM5w3njRnTFMoIE=Z%fYuU@@k=aX5GcZ)G& zuE0kJ<-fnb@2~#8Zu#=(v(59bt&hLIEjPNpzTQy%;bX}Df?Hc(_edIRX=%0b%h$cX zXREKTU-?}Ul${*(tE#Hp`(!*lJtYki7^a^Ftx-LD<_u`yK|(@8TKcxhJ60yg#Kgp+ zqN0q96{}aT-nj8&6DzlzeO*pUiinidtT}UhdcF8B%zC)5V!`6X!);5KE{)w&@$tjM z!xt}JRQH=>QTV9kL;cpRTfct&8o#&d>p4(AFLF~#=kDFRckSAh^qbouxT9i$emEG& z^_Rc8va+YA=gpfpYu2neb?VeE5U_Ic`2Fqe+O=zQb90aPN}H#hk+}1X?*pHU%>~)n zXXhD$M*fXk-e0+TH8eDI=FFL$by8vs0v{JtZP~iD)wrCwfn&!VE;9Dl5bAf6w`*r$ zOzmF#hEL=8Lg#k9_ Date: Thu, 31 May 2007 22:32:06 +0000 Subject: [PATCH 081/175] Fix a couple of typos. [SVN r4406] --- doc/comparison.qbk | 162 ++++++++++++++++++++++++++++++++++++++++++ doc/hash_equality.qbk | 140 ++++++++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 doc/comparison.qbk create mode 100644 doc/hash_equality.qbk diff --git a/doc/comparison.qbk b/doc/comparison.qbk new file mode 100644 index 00000000..7c94da57 --- /dev/null +++ b/doc/comparison.qbk @@ -0,0 +1,162 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[section:comparison Comparison with Associative Containers] + +[table Interface differences. + [[Associative Containers] [Unordered Associative Containers]] + + [ + [Parameterized by an ordering relation `Compare`] + [Parameterized by a function object `Hash` and an equivalence relation + `Pred`] + ] + [ + [`Compare` exposed by member typedef `key_compare`, accessed by member function `key_comp()`] + [`Hash` exposed by member typedef `hasher`, accessed by member function `hash_function()`.\n`Pred` by member typedef `key_equal` and member function `key_eq()`.] + ] + [ + [Member typedef `value_compare` supplies an ordering comparison for member elements, accessed by member function `value_comp()`.] + [No equivalent. No idea why.] + ] + [/TODO: Mention a range? This is meant to be differences but this doesn't + seem to be complete.] + [ + [Constructors have optional extra parameters for the comparison object.] + [Constructors have optional extra parameters for the initial minimum + number of buckets, a hash function and an equality object.] + ] + + [ + [Keys `k1`, `k2` are considered equivalent if + `!Compare(k1, k2) && !Compare(k2, k1)`] + [Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`] + ] + [ + [Member function `lower_bound(k)` and `upper_bound(k)`] + [No equivalent. Since the elements aren't ordered `lower_bound` and + `upper_bound` would be meaningless.] + ] + [ + [`equal_range(k)` returns an empty range at the position that k + would be inserted if k isn't present in the container.] + [`equal_range(k)` returns a range at the end of the container if + k isn't present in the container. It can't return a positioned + range as k could be inserted into multiple place. To find out the + bucket that k would be inserted into use `bucket(k)`. But remember + that an insert can cause the container to rehash - meaning that the + element can be inserted into a different bucket.] + ] + [ + [`iterator`, `const_iterator` are of the biderctional category.] + [`iterator`, `const_iterator` are of at least the forward category.] + ] + [ + [Inserts do not invalidate iterators or references to the container.] + [Inserts can invalidate iterators but not references to the container.] + ] + [ + [Iterators iterate through the container in the order defined by + the comparison object.] + [Iterators iterate through the container in an arbitrary order, that + can change as elements are inserted. Although, equivalent elements + are always adjacent.] + ] + [ + [No equivalent] + [Local iterators can be used to iterate through individual buckets. + (I don't think that the order of local iterators and iterators are + required to have any correspondence.)] + ] + [ + [Can be compared using the `==`, `!=`, `<`, `<=`, `>`, `>=` operators] + [No comparison operators are defined] + ] + [ + [] + [When inserting with a hint, implementations are permitted to ignore + the hint.] + ] + [ + [`erase` never throws an exception] + [The containers hash or predicate function can throw exceptions + from `erase`] + ] +] + +[table Complexity Guarantees + [[Operation] [Associative Containers] [Unordered Associative Containers]] + [ + [Construction of empty container] + [constant] + [/TODO: Do I meet this?] + [O(/n/) where /n/ is the minimum number of buckets.] + ] + [ + [Construction of container from a range of /N/ elements] + [O(/N/ log /N/), O(/N/) if the range is sorted with `value_comp()`] + [Average case O(/N/), worst case + O(/N/'''2''')] + ] + [ + [Insert a single element] + [logarithmic] + [Average case constant, worst case linear] + ] + [ + [Insert a single element with a hint] + [Amortized constant if t elements inserted right after hint, + logarithmic otherwise] + [Average case constant, worst case linear (ie. the same as + a normal insert).] + ] + [ + [Inserting a range of /N/ elements] + [/N/ log(`size()`+/N/)] + [Average case O(/N/), worst case O(/N/ * 'size()')] + ] + [ + [Erase by key, `k`] + [O(log(`size()`) + `count(k)`)] + [Average case: O(`count(k)`), Worst case: O(`size()`)] + ] + [ + [Erase a single element by iterator] + [Amortized constant] + [Average case: O(1), Worst case: O(`size()`)] + ] + [ + [Erase a range of /N/ elements] + [O(log(`size()`) + /N/)] + [Average case: O(/N/), Worst case: O(`size()`)] + ] + [ + [Clearing the container] + [O(`size()`)] + [O(`size()`)] + ] + [ + [Find] + [logarithmic] + [Average case: O(/N/), Worst case: O(`size()`)] + ] + [/ TODO: Average case is probably wrong. ] + [ + [Count] + [O(log(`size()`) + `count(k)`)] + [Average case: O(1), Worst case: O(`size()`)] + ] + [ + [`equal_range(k)`] + [logarithmic] + [Average case: O(`count(k)`), Worst case: O(`size()`)] + ] + [ + [`lower_bound`,`upper_bound`] + [logarithmic] + [n/a] + ] +] + +[endsect] diff --git a/doc/hash_equality.qbk b/doc/hash_equality.qbk new file mode 100644 index 00000000..4a24eefe --- /dev/null +++ b/doc/hash_equality.qbk @@ -0,0 +1,140 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[section:hash_equality Equality Predicates and Hash Functions] + +[/TODO: A better introduction to hash functions?] + +While the associative containers use an ordering relation to specify how the +elements are stored, the unordered associative containers use an equality +predicate and a hash function. For example [classref boost::unordered_set] +is declared as: + + template, + typename Pred = std::equal_to, + typename Alloc = std::allocator > + class ``[classref boost::unordered_set unordered_set]``; + +The hash function comes first as you might want to change the hash function +but not the equality predicate, while if you were to change the behaviour +of the equality predicate you would have to change the hash function to match +it. + +For example, if you wanted to use the +[@http://www.isthe.com/chongo/tech/comp/fnv/ FNV-1 hash] you could write: + + ``[classref boost::unordered_set]`` words; + +An example implementation of FNV-1, and some other hash functions are supplied +in the examples directory. + +Alternatively, you might wish to use a different equality function. If so, make +sure you use a hash function that matches it. For example, a +case-insensitive dictionary: + + struct iequal_to + : std::binary_function + { + bool operator()(std::string const& x, + std::string const& y) const + { + return boost::algorithm::iequals(x, y); + } + }; + + struct ihash + : std::unary_function + { + bool operator()(std::string const& x) const + { + std::size_t seed = 0; + + for(std::string::const_iterator it = x.begin(); + it != x.end(); ++it) + { + boost::hash_combine(seed, std::toupper(*it)); + } + + return seed; + } + }; + + struct word_info; + + boost::unordered_map + idictionary; + +A more generic version of this example is available at: +[@../../libs/unordered/examples/case_insensitive.hpp /libs/unordered/examples/case_insensitive.hpp] + +[h2 Custom Types] + +Similarly, a custom hash function can be used for custom types: + + struct point { + int x; + int y; + }; + + bool operator==(point const& p1, point const& p2) + { + return p1.x == p2.x && p1.y == p2.y; + } + + struct point_hash + : std::unary_function + { + std::size_t operator()(point const& p) const + { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } + } + + boost::unordered_multiset, point_hash> + points; + +Although, customizing Boost.Hash is probably a better solution: + + struct point { + int x; + int y; + }; + + bool operator==(point const& p1, point const& p2) + { + return p1.x == p2.x && p1.y == p2.y; + } + + std::size_t hash_value(point const& x) { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } + + // Now the default functions work. + boost::unordered_multiset points; + +See the Boost.Hash documentation for more detail on how to do this. Remember +that it relies on extensions to the draft standard - so it won't work on other +implementations of the unordered associative containers. + +[table Methods for accessing the hash and equality functions. + [[Method] [Description]] + + [ + [``hasher hash_function() const``] + [Returns the container's hash function.] + ] + [ + [``key_equal key_eq() const``] + [Returns the container's key equality function.] + ] +] + +[endsect] From 17b4216b49680f9eb2627f9a1c052631fba989be Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 31 May 2007 22:33:39 +0000 Subject: [PATCH 082/175] Trying to make the unordered documentation a little better. [SVN r4407] --- doc/buckets.qbk | 148 ++++++++++++++++++++++++++++++++++++++++++++++++ doc/intro.qbk | 113 ++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 doc/buckets.qbk create mode 100644 doc/intro.qbk diff --git a/doc/buckets.qbk b/doc/buckets.qbk new file mode 100644 index 00000000..9227acdb --- /dev/null +++ b/doc/buckets.qbk @@ -0,0 +1,148 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[section:buckets The Data Structure] + +The containers are made up of a number of 'buckets', each of which can contain +any number of elements. For example, the following diagram shows an [classref +boost::unordered_set unordered_set] with 7 buckets containing 5 elements, `A`, +`B`, `C`, `D` and `E` (this is just for illustration, in practise containers +will have more buckets). + +[$../../libs/unordered/doc/diagrams/buckets.png] + +In order to decide which bucket to place an element in, the container applies +the hash function, `Hash`, to the element's key (for `unordered_set` and +`unordered_multiset` the key is the whole element, but is refered to as the key +so that the same terminology can be used for sets and maps). This returns a +value of type `std::size_t`. `std::size_t` has a much greater range of values +then the number of buckets, so that container applies another transformation to +that value to choose a bucket to place the element in. + +If at a later date the container wants to find an element in the container it +just has to apply the same process to the element's key to discover which +bucket it is in. If the hash function has worked well the elements will be +evenly distributed amongst the buckets so it will only have to examine a small +number of elements. + +You can see in the diagram that `A` & `D` have been placed in the same bucket. +This means that when looking for these elements, of another element that would +be placed in the same bucket, up to 2 comparison have to be made, making +searching slower. This is known as a collision. To keep things fast we try to +keep these to a minimum. + +[table Methods for Accessing Buckets + [[Method] [Description]] + + [ + [``size_type bucket_count() const``] + [The number of buckets.] + ] + [ + [``size_type max_bucket_count() const``] + [An upper bound on the number of buckets.] + ] + [ + [``size_type bucket_size(size_type n) const``] + [The number of elements in bucket `n`.] + ] + [ + [``size_type bucket(key_type const& k) const``] + [Returns the index of the bucket which would contain k] + ] + [ + [`` + local_iterator begin(size_type n); + local_iterator end(size_type n); + const_local_iterator begin(size_type n) const; + const_local_iterator end(size_type n) const; + ``] + [Return begin and end iterators for bucket `n`.] + ] +] + +[h2 Controlling the number of buckets] + +As more elements are added to an unordered associative container, the number +of elements in the buckets will increase causing performance to get worse. To +combat this the containers increase the bucket count as elements are inserted. + +The standard gives you two methods to influence the bucket count. First you can +specify the minimum number of buckets in the constructor, and later, by calling +`rehash`. + +The other method is the `max_load_factor` member function. The 'load factor' +is the average number of elements per bucket, `max_load_factor` can be used +to give a /hint/ of a value that the load factor should be kept below. The +draft standard doesn't actually require the container to pay much attention +to this value. The only time the load factor is /required/ to be less than the +maximum is following a call to `rehash`. But most implementations will probably +try to keep the number of elements below the max load factor, and set the +maximum load factor something the same or near to your hint - unless your hint +is unreasonably small. + +It is not specified anywhere how member functions other than `rehash` affect +the bucket count, although `insert` is only allowed to invalidate iterators +when the insertion causes the load factor to reach the maximum. Which will +typically mean that insert will only change the number of buckets when an +insert causes this. + +In a similar manner to using `reserve` for `vector`s, it can be a good idea +to call `rehash` before inserting a large number of elements. This will get +the expensive rehashing out of the way and let you store iterators, safe in +the knowledge that they won't be invalidated. If you are inserting `n` +elements into container `x`, you could first call: + + x.rehash((x.size() + n) / x.max_load_factor() + 1); + +[blurb Note: `rehash`'s argument is the number of buckets, not the number of +elements, which is why the new size is divided by the maximum load factor. The +`+ 1` is required because the container is allowed to resize when the load +factor is equal to the maximum load factor.] + +[table Methods for Controlling Bucket Size + [[Method] [Description]] + + [ + [``float load_factor() const``] + [The average number of elements per bucket.] + ] + [ + [``float max_load_factor() const``] + [Returns the current maximum load factor.] + ] + [ + [``float max_load_factor(float z)``] + [Changes the container's maximum load factor, using `z` as a hint.] + ] + [ + [``void rehash(size_type n)``] + [Changes the number of buckets so that there at least n buckets, and + so that the load factor is less than the maximum load factor.] + ] + +] + +[/ I'm not at all happy with this section. So I've commented it out.] + +[/ h2 Rehash Techniques] + +[/If the container has a load factor much smaller than the maximum, `rehash` +might decrease the number of buckets, reducing the memory usage. This isn't +guaranteed by the standard but this implementation will do it. + +If you want to stop the table from ever rehashing due to an insert, you can +set the maximum load factor to infinity (or perhaps a load factor that it'll +never reach - say `x.max_size()`. As you can only give a 'hint' for the maximum +load factor, this isn't guaranteed to work. But again, it'll work in this +implementation. (TODO: If an unordered container with infinite load factor +is copied, bad things could happen. So maybe this advice should be removed. Or +maybe the implementation should cope with that). + +If you do this and want to make the container rehash, `rehash` will still work. +But be careful that you only ever call it with a sufficient number of buckets +- otherwise it's very likely that the container will decrease the bucket +count to an overly small amount.] + +[endsect] diff --git a/doc/intro.qbk b/doc/intro.qbk new file mode 100644 index 00000000..de4c36ec --- /dev/null +++ b/doc/intro.qbk @@ -0,0 +1,113 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[def __tr1__ + [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf + C++ Standard Library Technical Report]] +[def __boost-tr1__ + [@http://www.boost.org/doc/html/boost_tr1.html + Boost.TR1]] +[def __draft__ + [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2009.pdf + Working Draft of the C++ Standard]] +[def __hash-table__ [@http://en.wikipedia.org/wiki/Hash_table + hash table]] +[def __hash-function__ [@http://en.wikipedia.org/wiki/Hash_function + hash function]] + +[section:intro Introduction] + +For accessing data based on key lookup, the C++ standard library offers `std::set`, +`std::map`, `std::multiset` and `std::multimap`. These are generally +implemented using balanced binary trees so that lookup time has +logarithmic complexity. That is generally okay, but in many cases a +__hash-table__ can perform better, as accessing data has constant complexity, +on average. The worst case complexity is linear, but that occurs rarely and +with some care, can be avoided. + +Also, the existing containers require a 'less than' comparison object +to order their elements. For some data types this is impossible to implement +or isn't practicle. For a hash table you need an equality function +and a hash function for the key. + +So the __tr1__ introduced the unordered associative containers, which are +implemented using hash tables, and they have now been added to the __draft__. + +This library supplies a standards compliant implementation that is proposed for +addition to boost. If accepted they should also be added to __boost-tr1__. + +`unordered_set` and `unordered_multiset` are defined in the header +<[headerref boost/unordered_set.hpp]> + + namespace boost { + template < + class Key, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_set unordered_set]``; + + template< + class Key, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_multiset unordered_multiset]``; + } + +`unordered_map` and `unordered_multimap` are defined in the header +<[headerref boost/unordered_map.hpp]> + + namespace boost { + template < + class Key, class T, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_map unordered_map]``; + + template< + class Key, class T, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_multimap unordered_multimap]``; + } + +If using Boost.TR1, these classes will be included from `` and +``, with the classes included in the `std::tr1` namespace. + +The containers are used in a similar manner to the normal associative +containers: + + #include <``[headerref boost/unordered_map.hpp]``> + #include + + int main() + { + boost::unordered_map x; + x["one"] = 1; + x["two"] = 2; + x["three"] = 3; + + assert(x["one"] == 1); + assert(x["missing"] == 0); + } + +But since the elements aren't ordered, the output of: + + BOOST_FOREACH(map::value_type i, x) { + std::cout< Date: Wed, 20 Jun 2007 20:47:51 +0000 Subject: [PATCH 083/175] Tweak the hash equality documentation a little. [SVN r7116] --- doc/hash_equality.qbk | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/hash_equality.qbk b/doc/hash_equality.qbk index 4a24eefe..509ed783 100644 --- a/doc/hash_equality.qbk +++ b/doc/hash_equality.qbk @@ -8,7 +8,7 @@ While the associative containers use an ordering relation to specify how the elements are stored, the unordered associative containers use an equality -predicate and a hash function. For example [classref boost::unordered_set] +predicate and a hash function. For example, [classref boost::unordered_set] is declared as: template words; @@ -31,7 +29,7 @@ An example implementation of FNV-1, and some other hash functions are supplied in the examples directory. Alternatively, you might wish to use a different equality function. If so, make -sure you use a hash function that matches it. For example, a +sure you use a hash function that matches it. So to implement a case-insensitive dictionary: struct iequal_to @@ -66,7 +64,7 @@ case-insensitive dictionary: boost::unordered_map idictionary; -A more generic version of this example is available at: +A more generic version is available at: [@../../libs/unordered/examples/case_insensitive.hpp /libs/unordered/examples/case_insensitive.hpp] [h2 Custom Types] From ddde77c7379c3248eec7d458676343aacba4d100 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 20 Jun 2007 20:48:40 +0000 Subject: [PATCH 084/175] Remove the silly warning from the unordered container documentation. [SVN r7117] --- doc/unordered.qbk | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 doc/unordered.qbk diff --git a/doc/unordered.qbk b/doc/unordered.qbk new file mode 100644 index 00000000..8f8c4def --- /dev/null +++ b/doc/unordered.qbk @@ -0,0 +1,25 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[library Unordered Associative Containers + [quickbook 1.3] + [authors [Maitin-Shepard, Jeremy B.], [James, Daniel]] + [copyright 2005 2007 Daniel James] + [purpose std::tr1 compliant hash containers] + [id unordered] + [dirname unordered] + [license + Distributed under 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] + ] +] + +[include:unordered intro.qbk] +[include:unordered buckets.qbk] +[include:unordered hash_equality.qbk] +[include:unordered comparison.qbk] +[include:unordered rationale.qbk] + +[xinclude ref.xml] From c08cfa18c4a7600f211f63d7ad48b3ab2889f772 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 1 Jul 2007 11:16:57 +0000 Subject: [PATCH 085/175] Avoid memory leak error for std::set on g++-3.3 [SVN r7331] --- test/helpers/invariants.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index 65d13fb7..91d5b2d6 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -21,7 +21,9 @@ namespace test { typename X::key_equal eq = x1.key_eq(); typedef typename X::key_type key_type; - std::set found_; + // Boost.Test was reporting memory leaks for std::set on g++-3.3. + // So I work around it by using malloc. + std::set, test::exception::detail::malloc_allocator > found_; typename X::const_iterator it = x1.begin(), end = x1.end(); typename X::size_type size = 0; From 10a05bda054cf0ab849e7e3d29e25aa861b68960 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 1 Jul 2007 11:54:22 +0000 Subject: [PATCH 086/175] Move malloc_allocator into its own header so that the normal tests don't have to depend on the exception testing code. [SVN r7332] --- test/helpers/allocator.hpp | 53 +++++++++++++++++++++++++++++++++++++ test/helpers/invariants.hpp | 6 ++--- test/objects/exception.hpp | 41 ++-------------------------- 3 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 test/helpers/allocator.hpp diff --git a/test/helpers/allocator.hpp b/test/helpers/allocator.hpp new file mode 100644 index 00000000..e78280ba --- /dev/null +++ b/test/helpers/allocator.hpp @@ -0,0 +1,53 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_MALLOC_ALLOCATOR_HEADER) +#define BOOST_UNORDERED_TEST_MALLOC_ALLOCATOR_HEADER + +#include +#include +#include + +namespace test +{ + template + struct malloc_allocator + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef malloc_allocator other; }; + + malloc_allocator() {} + template malloc_allocator(malloc_allocator const& x) {} + malloc_allocator(malloc_allocator const& x) {} + + pointer address(reference r) { return &r; } + const_pointer address(const_reference r) { return &r; } + + pointer allocate(size_type n) { + return static_cast(malloc(n * sizeof(T))); + } + + pointer allocate(size_type n, const_pointer u) { return allocate(n); } + void deallocate(pointer p, size_type n) { free(p); } + void construct(pointer p, T const& t) { new(p) T(t); } + void destroy(pointer p) { p->~T(); } + + size_type max_size() const { + return (std::numeric_limits::max)(); + } + + bool operator==(malloc_allocator const& x) const { return true; } + bool operator!=(malloc_allocator const& x) const { return false; } + }; +} + +#endif diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index 91d5b2d6..a4eca0b0 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -13,6 +13,7 @@ #include #include "./metafunctions.hpp" #include "./helpers.hpp" +#include "./allocator.hpp" namespace test { @@ -23,7 +24,7 @@ namespace test typedef typename X::key_type key_type; // Boost.Test was reporting memory leaks for std::set on g++-3.3. // So I work around it by using malloc. - std::set, test::exception::detail::malloc_allocator > found_; + std::set, test::malloc_allocator > found_; typename X::const_iterator it = x1.begin(), end = x1.end(); typename X::size_type size = 0; @@ -32,9 +33,8 @@ namespace test // to test either that keys are unique or that equivalent keys are // adjacent. (6.3.1/6) key_type key = get_key(*it); - if(found_.find(key) != found_.end()) + if(!found_.insert(key).second) BOOST_ERROR("Elements with equivalent keys aren't adjacent."); - found_.insert(key); // Iterate over equivalent keys, counting them. unsigned int count = 0; diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index 6506b3d8..5cb90c18 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -14,8 +14,8 @@ #include #include #include -#include #include "../helpers/fwd.hpp" +#include "../helpers/allocator.hpp" #include #define RUN_EXCEPTION_TESTS(test_seq, param_seq) \ @@ -185,43 +185,6 @@ namespace exception // the most convenient way. namespace { - template - struct malloc_allocator - { - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - typedef T* pointer; - typedef T const* const_pointer; - typedef T& reference; - typedef T const& const_reference; - typedef T value_type; - - template struct rebind { typedef malloc_allocator other; }; - - malloc_allocator() {} - template malloc_allocator(malloc_allocator const& x) {} - malloc_allocator(malloc_allocator const& x) {} - - pointer address(reference r) { return &r; } - const_pointer address(const_reference r) { return &r; } - - pointer allocate(size_type n) { - return static_cast(malloc(n * sizeof(T))); - } - - pointer allocate(size_type n, const_pointer u) { return allocate(n); } - void deallocate(pointer p, size_type n) { free(p); } - void construct(pointer p, T const& t) { new(p) T(t); } - void destroy(pointer p) { p->~T(); } - - size_type max_size() const { - return (std::numeric_limits::max)(); - } - - bool operator==(malloc_allocator const& x) const { return true; } - bool operator!=(malloc_allocator const& x) const { return false; } - }; - struct memory_area { void const* start; void const* end; @@ -252,7 +215,7 @@ namespace exception }; typedef std::map, - malloc_allocator > > + test::malloc_allocator > > allocated_memory_type; allocated_memory_type allocated_memory; unsigned int count_allocators = 0; From e4b6b89ed21750a36804df2abfe03853cab37ba6 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 7 Jul 2007 09:39:31 +0000 Subject: [PATCH 087/175] Get malloc_allocator working with Visual C++ 6 STL. [SVN r7383] --- test/helpers/allocator.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/helpers/allocator.hpp b/test/helpers/allocator.hpp index e78280ba..5e489e7e 100644 --- a/test/helpers/allocator.hpp +++ b/test/helpers/allocator.hpp @@ -47,6 +47,15 @@ namespace test bool operator==(malloc_allocator const& x) const { return true; } bool operator!=(malloc_allocator const& x) const { return false; } + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + template void deallocate(T* p, size_type) { + free(p); + } + char* _Charalloc(size_type n) { + return static_cast(malloc(n * sizeof(char))); + } +#endif }; } From 6af2b965bbc5b44d5057731b5e8b2ca702d02618 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sat, 7 Jul 2007 09:42:56 +0000 Subject: [PATCH 088/175] Use a portable hash_value in insert_stable_tests in order to support compilers without ADL. [SVN r7384] --- test/unordered/insert_stable_tests.cpp | 62 +++++++++++++++----------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/test/unordered/insert_stable_tests.cpp b/test/unordered/insert_stable_tests.cpp index 51b16e78..aad31dd8 100644 --- a/test/unordered/insert_stable_tests.cpp +++ b/test/unordered/insert_stable_tests.cpp @@ -9,34 +9,44 @@ #include -struct member { - int tag1_; - int tag2_; +namespace insert_stable +{ + struct member { + int tag1_; + int tag2_; + + member() : tag1_(0), tag2_(0) {} + member(int t1, int t2) : tag1_(t1), tag2_(t2) {} + + friend bool operator==(member const& x, member const& y) { + return x.tag1_ == y.tag1_; + } + + friend bool operator!=(member const& x, member const& y) { + return x.tag1_ != y.tag1_; + } + }; +} - member() : tag1_(0), tag2_(0) {} - member(int t1, int t2) : tag1_(t1), tag2_(t2) {} - - friend bool operator==(member const& x, member const& y) { - return x.tag1_ == y.tag1_; +#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +namespace boost +#else +namespace insert_stable +#endif +{ + std::size_t hash_value(insert_stable::member const& x) { + return static_cast(x.tag1_); } - - friend bool operator!=(member const& x, member const& y) { - return x.tag1_ != y.tag1_; - } -}; - -std::size_t hash_value(member const& x) { - return static_cast(x.tag1_); } void stable_insert_test1() { - boost::unordered_multiset x; + boost::unordered_multiset x; - x.insert(member(1,1)); - x.insert(member(1,2)); - x.insert(member(1,3)); + x.insert(insert_stable::member(1,1)); + x.insert(insert_stable::member(1,2)); + x.insert(insert_stable::member(1,3)); - boost::unordered_multiset::const_iterator it = x.begin(), end = x.end(); + boost::unordered_multiset::const_iterator it = x.begin(), end = x.end(); BOOST_TEST(it != end); if(it != end) { BOOST_TEST(it->tag2_ == 1); ++it; } BOOST_TEST(it != end); @@ -47,12 +57,12 @@ void stable_insert_test1() { } void stable_insert_test2() { - boost::unordered_multimap x; - typedef boost::unordered_multimap::const_iterator iterator; + boost::unordered_multimap x; + typedef boost::unordered_multimap::const_iterator iterator; - iterator it = x.insert(x.end(), std::make_pair(member(1,1), 1)); - it = x.insert(it, std::make_pair(member(1,2), 2)); - it = x.insert(it, std::make_pair(member(1,3), 3)); + iterator it = x.insert(x.end(), std::make_pair(insert_stable::member(1,1), 1)); + it = x.insert(it, std::make_pair(insert_stable::member(1,2), 2)); + it = x.insert(it, std::make_pair(insert_stable::member(1,3), 3)); it = x.begin(); iterator end = x.end(); From aabf1a408b2edbe38f7e0a52aaec50d372551cf7 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 17 Jul 2007 23:17:21 +0000 Subject: [PATCH 089/175] Improve the unordered tests support for some older compilers. Better testing of elements with equivalent keys. [SVN r7458] --- test/helpers/fwd.hpp | 1 + test/helpers/generators.hpp | 6 ++ test/objects/exception.hpp | 11 ++-- test/objects/test.hpp | 96 +++++++++++++++++++++++++++- test/unordered/assign_tests.cpp | 9 ++- test/unordered/constructor_tests.cpp | 18 ++++++ test/unordered/copy_tests.cpp | 5 ++ test/unordered/erase_equiv_tests.cpp | 24 +++---- test/unordered/erase_tests.cpp | 9 +++ test/unordered/find_tests.cpp | 5 ++ test/unordered/insert_tests.cpp | 12 ++++ 11 files changed, 176 insertions(+), 20 deletions(-) diff --git a/test/helpers/fwd.hpp b/test/helpers/fwd.hpp index 7efd7d44..cc04ad85 100644 --- a/test/helpers/fwd.hpp +++ b/test/helpers/fwd.hpp @@ -12,6 +12,7 @@ namespace test { int generate(int const*); char generate(char const*); + signed char generate(signed char const*); std::string generate(std::string*); float generate(float const*); template diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp index 86be1d4d..d6c875b0 100644 --- a/test/helpers/generators.hpp +++ b/test/helpers/generators.hpp @@ -56,6 +56,12 @@ namespace test return (rand() >> 1) % (128-32) + 32; } + inline signed char generate(signed char const*) + { + using namespace std; + return rand(); + } + inline std::string generate(std::string const*) { using namespace std; diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index 5cb90c18..a20588e6 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -624,13 +624,14 @@ namespace exception } return (std::numeric_limits::max)(); } - - friend void swap(allocator& x, allocator& y) - { - std::swap(x.tag_, y.tag_); - } }; + template + void swap(allocator& x, allocator& y) + { + std::swap(x.tag_, y.tag_); + } + // It's pretty much impossible to write a compliant swap when these // two can throw. So they don't. diff --git a/test/objects/test.hpp b/test/objects/test.hpp index ba6d4bf0..5f473070 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -50,7 +50,6 @@ namespace test (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); } - friend object generate(object const*) { int* x = 0; return object(generate(x), generate(x)); @@ -62,6 +61,44 @@ namespace test } }; + // This object is usd to test how well the containers cope with equivalent keys. + class equivalent_object + { + friend class hash; + friend class equal_to; + friend class less; + int tag1_, tag2_; + public: + explicit equivalent_object(int t1 = 0, int t2 = 0) : tag1_(t1), tag2_(t2) {} + + ~equivalent_object() { + tag1_ = -1; + tag2_ = -1; + } + + friend bool operator==(equivalent_object const& x1, equivalent_object const& x2) { + return x1.tag1_ == x2.tag1_; + } + + friend bool operator!=(equivalent_object const& x1, equivalent_object const& x2) { + return x1.tag1_ != x2.tag1_; + } + + friend bool operator<(equivalent_object const& x1, equivalent_object const& x2) { + return x1.tag1_ < x2.tag1_; + } + + friend equivalent_object generate(equivalent_object const*) { + signed char* x = 0; + return equivalent_object(generate(x), generate(x)); + } + + friend std::ostream& operator<<(std::ostream& out, equivalent_object const& o) + { + return out<<"("< const& x, allocator const& y, test::derived_type) { return x == y; } + +#if BOOST_WORKAROUND(__GNUC__, < 3) + void swap(test::object& x, test::object& y) { + test::object tmp; + tmp = x; + x = y; + y = tmp; + } + + void swap(test::equivalent_object& x, test::equivalent_object& y) { + test::equivalent_object tmp; + tmp = x; + x = y; + y = tmp; + } + + void swap(test::hash& x, test::hash& y) { + test::hash tmp; + tmp = x; + x = y; + y = tmp; + } + + void swap(test::less& x, test::less& y) { + test::less tmp; + tmp = x; + x = y; + y = tmp; + } + + void swap(test::equal_to& x, test::equal_to& y) { + test::equal_to tmp; + tmp = x; + x = y; + y = tmp; + } + + template + void swap(test::allocator& x, test::allocator& y) { + test::allocator tmp; + tmp = x; + x = y; + y = tmp; + } +#endif } #endif diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index 7de95b89..4018192d 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -95,9 +95,14 @@ int main() assign_tests1((boost::unordered_map >*) 0); assign_tests1((boost::unordered_multimap >*) 0); + assign_tests1((boost::unordered_set >*) 0); + assign_tests1((boost::unordered_multiset >*) 0); + assign_tests1((boost::unordered_map >*) 0); + assign_tests1((boost::unordered_multimap >*) 0); + assign_tests2((boost::unordered_set >*) 0); - assign_tests2((boost::unordered_multiset >*) 0); - assign_tests2((boost::unordered_map >*) 0); + assign_tests2((boost::unordered_multiset >*) 0); + assign_tests2((boost::unordered_map >*) 0); assign_tests2((boost::unordered_multimap >*) 0); return boost::report_errors(); diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 916a0810..36f15bef 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -273,6 +273,15 @@ int main() std::cerr<<"Test1 unordered_multimap\n"; constructor_tests1((boost::unordered_multimap >*) 0); + std::cerr<<"Test1 unordered_set\n"; + constructor_tests1((boost::unordered_set >*) 0); + std::cerr<<"Test1 unordered_multiset\n"; + constructor_tests1((boost::unordered_multiset >*) 0); + std::cerr<<"Test1 unordered_map\n"; + constructor_tests1((boost::unordered_map >*) 0); + std::cerr<<"Test1 unordered_multimap\n"; + constructor_tests1((boost::unordered_multimap >*) 0); + std::cerr<<"Test2 unordered_set\n"; constructor_tests2((boost::unordered_set >*) 0); std::cerr<<"Test2 unordered_multiset\n"; @@ -282,6 +291,15 @@ int main() std::cerr<<"Test2 unordered_multimap\n"; constructor_tests2((boost::unordered_multimap >*) 0); + std::cerr<<"Test2 unordered_set\n"; + constructor_tests2((boost::unordered_set >*) 0); + std::cerr<<"Test2 unordered_multiset\n"; + constructor_tests2((boost::unordered_multiset >*) 0); + std::cerr<<"Test2 unordered_map\n"; + constructor_tests2((boost::unordered_map >*) 0); + std::cerr<<"Test2 unordered_multimap\n"; + constructor_tests2((boost::unordered_multimap >*) 0); + std::cerr<<"Map Test unordered_map\n"; map_constructor_test((boost::unordered_map >*) 0); std::cerr<<"Map Test unordered_multimap\n"; diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index 190fdade..7cc46b72 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -100,5 +100,10 @@ int main() copy_construct_tests2((boost::unordered_map >*) 0); copy_construct_tests2((boost::unordered_multimap >*) 0); + copy_construct_tests2((boost::unordered_set >*) 0); + copy_construct_tests2((boost::unordered_multiset >*) 0); + copy_construct_tests2((boost::unordered_map >*) 0); + copy_construct_tests2((boost::unordered_multimap >*) 0); + return boost::report_errors(); } diff --git a/test/unordered/erase_equiv_tests.cpp b/test/unordered/erase_equiv_tests.cpp index d8f11e1a..e245b1ef 100644 --- a/test/unordered/erase_equiv_tests.cpp +++ b/test/unordered/erase_equiv_tests.cpp @@ -49,8 +49,8 @@ typedef boost::unordered_multimap, test::allocator > > collide_map2; -typedef collide_map::value_type pair; -typedef std::list list; +typedef collide_map::value_type collide_value; +typedef std::list collide_list; void empty_range_tests() @@ -63,8 +63,8 @@ void empty_range_tests() void single_item_tests() { - list init; - init.push_back(pair(1,1)); + collide_list init; + init.push_back(collide_value(1,1)); collide_map x(init.begin(), init.end()); x.erase(x.begin(), x.begin()); @@ -77,9 +77,9 @@ void single_item_tests() void two_equivalent_item_tests() { - list init; - init.push_back(pair(1,1)); - init.push_back(pair(1,2)); + collide_list init; + init.push_back(collide_value(1,1)); + init.push_back(collide_value(1,2)); { collide_map x(init.begin(), init.end()); @@ -109,8 +109,8 @@ void two_equivalent_item_tests() template bool compare(Range1 const& x, Range2 const& y) { - list a; - list b; + collide_list a; + collide_list b; std::copy(x.begin(), x.end(), std::back_inserter(a)); std::copy(y.begin(), y.end(), std::back_inserter(b)); a.sort(); @@ -121,7 +121,7 @@ bool compare(Range1 const& x, Range2 const& y) template bool general_erase_range_test(Container& x, int start, int end) { - list l; + collide_list l; std::copy(x.begin(), x.end(), std::back_inserter(l)); l.erase(boost::next(l.begin(), start), boost::next(l.begin(), end)); x.erase(boost::next(x.begin(), start), boost::next(x.begin(), end)); @@ -134,7 +134,7 @@ void erase_subrange_tests(Container const& x) for(std::size_t length = 0; length < x.size(); ++length) { for(std::size_t position = 0; position < x.size() - length; ++position) { Container y(x); - list init; + collide_list init; std::copy(y.begin(), y.end(), std::back_inserter(init)); if(!general_erase_range_test(y, position, position + length)) { BOOST_ERROR("general_erase_range_test failed."); @@ -153,7 +153,7 @@ void x_by_y_erase_range_tests(Container*, int values, int duplicates) for(int i = 0; i < values; ++i) { for(int j = 0; j < duplicates; ++j) { - y.insert(pair(i, j)); + y.insert(collide_value(i, j)); } } diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index f12e02e8..449ac392 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -135,5 +135,14 @@ int main() std::cerr<<"\nErase unordered_multimap.\n"; erase_tests1((boost::unordered_multimap >*) 0); + std::cerr<<"\nErase unordered_set.\n"; + erase_tests1((boost::unordered_set >*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + erase_tests1((boost::unordered_multiset >*) 0); + std::cerr<<"\nErase unordered_map.\n"; + erase_tests1((boost::unordered_map >*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + erase_tests1((boost::unordered_multimap >*) 0); + return boost::report_errors(); } diff --git a/test/unordered/find_tests.cpp b/test/unordered/find_tests.cpp index 7535d9e2..b551f509 100644 --- a/test/unordered/find_tests.cpp +++ b/test/unordered/find_tests.cpp @@ -88,5 +88,10 @@ int main() find_tests1((boost::unordered_map >*) 0); find_tests1((boost::unordered_multimap >*) 0); + find_tests1((boost::unordered_set >*) 0); + find_tests1((boost::unordered_multiset >*) 0); + find_tests1((boost::unordered_map >*) 0); + find_tests1((boost::unordered_multimap >*) 0); + return 0; } diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 2a32307c..3e2281f9 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -269,6 +269,11 @@ int main() unique_insert_tests1((boost::unordered_map >*) 0); equivalent_insert_tests1((boost::unordered_multimap >*) 0); + unique_insert_tests1((boost::unordered_set >*) 0); + equivalent_insert_tests1((boost::unordered_multiset >*) 0); + unique_insert_tests1((boost::unordered_map >*) 0); + equivalent_insert_tests1((boost::unordered_multimap >*) 0); + insert_tests2((boost::unordered_set*) 0); insert_tests2((boost::unordered_multiset*) 0); insert_tests2((boost::unordered_map*) 0); @@ -279,11 +284,18 @@ int main() insert_tests2((boost::unordered_map >*) 0); insert_tests2((boost::unordered_multimap >*) 0); + insert_tests2((boost::unordered_set >*) 0); + insert_tests2((boost::unordered_multiset >*) 0); + insert_tests2((boost::unordered_map >*) 0); + insert_tests2((boost::unordered_multimap >*) 0); + map_tests((boost::unordered_map*) 0); map_tests((boost::unordered_map >*) 0); + map_tests((boost::unordered_map >*) 0); associative_insert_range_test((boost::unordered_map >*) 0); associative_insert_range_test((boost::unordered_multimap >*) 0); + associative_insert_range_test((boost::unordered_multimap >*) 0); return boost::report_errors(); } From f11b2272e478f38a28a108e2adba5ca296d7c873 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 17 Jul 2007 23:19:33 +0000 Subject: [PATCH 090/175] Change a comment. [SVN r7459] --- test/unordered/erase_equiv_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unordered/erase_equiv_tests.cpp b/test/unordered/erase_equiv_tests.cpp index e245b1ef..29f4756e 100644 --- a/test/unordered/erase_equiv_tests.cpp +++ b/test/unordered/erase_equiv_tests.cpp @@ -104,7 +104,7 @@ void two_equivalent_item_tests() } } -// At this point I got bored and wrote more automated tests... +// More automated tests... template bool compare(Range1 const& x, Range2 const& y) From 6eaf45a0b0cda052b339678ab58cf810973767f3 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Fri, 10 Aug 2007 12:01:11 +0000 Subject: [PATCH 091/175] Add the -Wsign-promo flag to the unordered tests on gcc. [SVN r38566] --- test/container/Jamfile.v2 | 1 + test/exception/Jamfile.v2 | 1 + test/unordered/Jamfile.v2 | 1 + 3 files changed, 3 insertions(+) diff --git a/test/container/Jamfile.v2 b/test/container/Jamfile.v2 index b9a378d4..b5f89567 100644 --- a/test/container/Jamfile.v2 +++ b/test/container/Jamfile.v2 @@ -8,6 +8,7 @@ import testing ; project unordered-test/container : requirements intel-linux:"-strict_ansi -cxxlib-icc" + gcc:-Wsign-promo ; test-suite container-tests diff --git a/test/exception/Jamfile.v2 b/test/exception/Jamfile.v2 index 5feb6125..fccc5097 100644 --- a/test/exception/Jamfile.v2 +++ b/test/exception/Jamfile.v2 @@ -11,6 +11,7 @@ alias framework : /boost/test//boost_unit_test_framework ; project unordered-test/exception-tests : requirements intel-linux:"-strict_ansi -cxxlib-icc" + gcc:-Wsign-promo ; test-suite unordered-tests diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 07186630..64cd63d1 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -8,6 +8,7 @@ import testing ; project unordered-test/unordered : requirements intel-linux:"-strict_ansi -cxxlib-icc" + gcc:-Wsign-promo ; test-suite unordered-tests From 3ebd8bc9f237aefc6d2ef973bc785d5bbdcf009b Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 12 Aug 2007 09:06:26 +0000 Subject: [PATCH 092/175] Remove an old macro definition that's no longer relevant. [SVN r38607] --- include/boost/unordered/detail/hash_table.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index 8992bccd..ff1e9767 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -11,8 +11,6 @@ # pragma once #endif -#define BOOST_UNORDERED_PARANOID - #include #include From 36f45080194c27e7598f29ae9522b2fccfa7befc Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 14 Aug 2007 09:55:30 +0000 Subject: [PATCH 093/175] Run most of the unordered container tests (not the exception tests) on warning level 4 on Visual C++. [SVN r38648] --- test/container/Jamfile.v2 | 1 + test/container/compile_tests.hpp | 10 ++++++++++ test/helpers/allocator.hpp | 16 +++++++++++++--- test/helpers/generators.hpp | 2 +- test/helpers/invariants.hpp | 9 +++++++++ test/objects/minimal.hpp | 9 +++++++++ test/unordered/Jamfile.v2 | 1 + test/unordered/compile_tests.cpp | 10 ++++++++++ test/unordered/load_factor_tests.cpp | 10 ++++++++++ test/unordered/unnecessary_copy_tests.cpp | 6 +++--- 10 files changed, 67 insertions(+), 7 deletions(-) diff --git a/test/container/Jamfile.v2 b/test/container/Jamfile.v2 index b5f89567..7d050bd7 100644 --- a/test/container/Jamfile.v2 +++ b/test/container/Jamfile.v2 @@ -9,6 +9,7 @@ project unordered-test/container : requirements intel-linux:"-strict_ansi -cxxlib-icc" gcc:-Wsign-promo + msvc:/W4 ; test-suite container-tests diff --git a/test/container/compile_tests.hpp b/test/container/compile_tests.hpp index 0c1175a8..521f1d4e 100644 --- a/test/container/compile_tests.hpp +++ b/test/container/compile_tests.hpp @@ -3,7 +3,17 @@ // Distributed under 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) +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + #include + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + #include #include #include diff --git a/test/helpers/allocator.hpp b/test/helpers/allocator.hpp index 5e489e7e..0c15f0d9 100644 --- a/test/helpers/allocator.hpp +++ b/test/helpers/allocator.hpp @@ -10,6 +10,11 @@ #include #include +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + namespace test { template @@ -26,8 +31,8 @@ namespace test template struct rebind { typedef malloc_allocator other; }; malloc_allocator() {} - template malloc_allocator(malloc_allocator const& x) {} - malloc_allocator(malloc_allocator const& x) {} + template malloc_allocator(malloc_allocator const&) {} + malloc_allocator(malloc_allocator const&) {} pointer address(reference r) { return &r; } const_pointer address(const_reference r) { return &r; } @@ -37,7 +42,7 @@ namespace test } pointer allocate(size_type n, const_pointer u) { return allocate(n); } - void deallocate(pointer p, size_type n) { free(p); } + void deallocate(pointer p, size_type) { free(p); } void construct(pointer p, T const& t) { new(p) T(t); } void destroy(pointer p) { p->~T(); } @@ -59,4 +64,9 @@ namespace test }; } +#if defined(BOOST_MSVC) +#pragma warning(pop) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + #endif diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp index d6c875b0..792b240e 100644 --- a/test/helpers/generators.hpp +++ b/test/helpers/generators.hpp @@ -59,7 +59,7 @@ namespace test inline signed char generate(signed char const*) { using namespace std; - return rand(); + return static_cast(rand()); } inline std::string generate(std::string const*) diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index a4eca0b0..db64c132 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -15,6 +15,11 @@ #include "./helpers.hpp" #include "./allocator.hpp" +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant +#endif + namespace test { template @@ -92,5 +97,9 @@ namespace test } } +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + #endif diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index 31d26d5d..3fd48da0 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -8,6 +8,11 @@ #include +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + namespace test { namespace minimal @@ -226,4 +231,8 @@ namespace minimal } } +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + #endif diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 64cd63d1..c3dce0b3 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -9,6 +9,7 @@ project unordered-test/unordered : requirements intel-linux:"-strict_ansi -cxxlib-icc" gcc:-Wsign-promo + msvc:/W4 ; test-suite unordered-tests diff --git a/test/unordered/compile_tests.cpp b/test/unordered/compile_tests.cpp index aae63f55..9573b2de 100644 --- a/test/unordered/compile_tests.cpp +++ b/test/unordered/compile_tests.cpp @@ -6,7 +6,17 @@ #include #include +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + #include + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + #include #include #include "../helpers/check_return_type.hpp" diff --git a/test/unordered/load_factor_tests.cpp b/test/unordered/load_factor_tests.cpp index cbb99c3b..97687892 100644 --- a/test/unordered/load_factor_tests.cpp +++ b/test/unordered/load_factor_tests.cpp @@ -9,6 +9,11 @@ #include #include "../helpers/random_values.hpp" +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant +#endif + template void load_factor_tests(X* = 0) { @@ -70,3 +75,8 @@ int main() return boost::report_errors(); } + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#pragma warning(disable:4127) // conditional expression is constant +#endif diff --git a/test/unordered/unnecessary_copy_tests.cpp b/test/unordered/unnecessary_copy_tests.cpp index 19255ba2..0968d8d4 100644 --- a/test/unordered/unnecessary_copy_tests.cpp +++ b/test/unordered/unnecessary_copy_tests.cpp @@ -11,7 +11,7 @@ struct count_copies { static int count; count_copies() { ++count; } - count_copies(count_copies const& x) { ++count; } + count_copies(count_copies const&) { ++count; } private: count_copies& operator=(count_copies const&); }; @@ -20,7 +20,7 @@ private: namespace boost { #endif -std::size_t hash_value(count_copies const& x) { +std::size_t hash_value(count_copies const&) { return 0; } @@ -28,7 +28,7 @@ std::size_t hash_value(count_copies const& x) { } #endif -bool operator==(count_copies const& x, count_copies const& y) { +bool operator==(count_copies const&, count_copies const&) { return true; } From 58c7bb031ca8e1080d4c41e96fcf483f1fe2f2e0 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 27 Aug 2007 20:58:21 +0000 Subject: [PATCH 094/175] Remove a couple of unused parameter names. [SVN r39019] --- include/boost/unordered/detail/hash_table_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 17d31c5c..d0c09f33 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -1742,7 +1742,7 @@ namespace boost { } template - size_type insert_size(I i, I j, boost::incrementable_traversal_tag) + size_type insert_size(I, I, boost::incrementable_traversal_tag) { return 1; } From 2cd1659c33c7f20df4ada4a35c660eda35e3e530 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 27 Aug 2007 21:08:54 +0000 Subject: [PATCH 095/175] Back to using quickbook 1.4 for the documentation. [SVN r39021] --- doc/comparison.qbk | 4 +++- doc/hash_equality.qbk | 34 +++------------------------------- doc/unordered.qbk | 2 +- 3 files changed, 7 insertions(+), 33 deletions(-) diff --git a/doc/comparison.qbk b/doc/comparison.qbk index 7c94da57..f81a2392 100644 --- a/doc/comparison.qbk +++ b/doc/comparison.qbk @@ -14,7 +14,9 @@ ] [ [`Compare` exposed by member typedef `key_compare`, accessed by member function `key_comp()`] - [`Hash` exposed by member typedef `hasher`, accessed by member function `hash_function()`.\n`Pred` by member typedef `key_equal` and member function `key_eq()`.] + [`Hash` exposed by member typedef `hasher`, accessed by member function `hash_function()`. + + `Pred` by member typedef `key_equal` and member function `key_eq()`.] ] [ [Member typedef `value_compare` supplies an ordering comparison for member elements, accessed by member function `value_comp()`.] diff --git a/doc/hash_equality.qbk b/doc/hash_equality.qbk index 509ed783..86a46917 100644 --- a/doc/hash_equality.qbk +++ b/doc/hash_equality.qbk @@ -32,37 +32,9 @@ Alternatively, you might wish to use a different equality function. If so, make sure you use a hash function that matches it. So to implement a case-insensitive dictionary: - struct iequal_to - : std::binary_function - { - bool operator()(std::string const& x, - std::string const& y) const - { - return boost::algorithm::iequals(x, y); - } - }; - - struct ihash - : std::unary_function - { - bool operator()(std::string const& x) const - { - std::size_t seed = 0; - - for(std::string::const_iterator it = x.begin(); - it != x.end(); ++it) - { - boost::hash_combine(seed, std::toupper(*it)); - } - - return seed; - } - }; - - struct word_info; - - boost::unordered_map - idictionary; +[import src_code/insensitive.cpp] +[case_insensitive_functions] +[case_insensitive_dictionary] A more generic version is available at: [@../../libs/unordered/examples/case_insensitive.hpp /libs/unordered/examples/case_insensitive.hpp] diff --git a/doc/unordered.qbk b/doc/unordered.qbk index 8f8c4def..c8a020ed 100644 --- a/doc/unordered.qbk +++ b/doc/unordered.qbk @@ -3,7 +3,7 @@ / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] [library Unordered Associative Containers - [quickbook 1.3] + [quickbook 1.4] [authors [Maitin-Shepard, Jeremy B.], [James, Daniel]] [copyright 2005 2007 Daniel James] [purpose std::tr1 compliant hash containers] From 4bd65e069e84b4126e6433324d24aa89a8cb2cc3 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 27 Aug 2007 22:48:13 +0000 Subject: [PATCH 096/175] Fix up the member typedef descriptions. [SVN r39025] --- doc/ref.xml | 2946 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2946 insertions(+) create mode 100644 doc/ref.xml diff --git a/doc/ref.xml b/doc/ref.xml new file mode 100644 index 00000000..55a8b2ba --- /dev/null +++ b/doc/ref.xml @@ -0,0 +1,2946 @@ + + + + + +
+ + + + + + + An unordered associative container that stores unique values. + + + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2009]. + + + + Template Parameters + + + + + + Value + + Value must be Assignable and CopyConstructible + + + + + Hash + + A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. + + + + + Pred + + A binary function object that implements an equivalence relation on values of type Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + + + + + Alloc + + An allocator whose value type is the same as the container's value type. + + + + + + + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. + + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. + + + + Value + + + Value + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + lvalue of value_type. + + + typename allocator_type::const_reference + const lvalue of value_type. + + + implementation-defined + + An unsigned integral type. + + + size_type can represent any non-negative value of + difference_type. + + + + + implementation-defined + + A signed integral type. + + Is identical to the difference type of + iterator and + const_iterator. + + + + + implementation-defined + + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + Convertible to + const_iterator. + + + + + implementation-defined + + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + + + + implementation-defined + + An iterator with the same value type, difference type and pointer and reference type as + iterator. + + A local_iterator object can be used to iterate through a single bucket. + + + + + implementation-defined + + A constant iterator with the same value type, difference type and pointer and reference type as + const_iterator. + + A const_local_iterator object can be used to iterate through a single bucket. + + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. + + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + + unordered_set const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + + unordered_set const& + + unordered_set& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + + + + + size_type + std::distance(begin(), end()) + + + + + value_type const& + + std::pair<iterator, bool> + + Inserts obj in the container if and only if there is no element in the container with an equivalent value. + + + + The bool component of the return type is true if an insert took place. + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + + value_type const& + + iterator + + + + const_iterator + + + value_type const& + + const_iterator + + + Inserts obj in the container if and only if there is no element in the container with an equivalent value. + + hint is a suggestion to where the element should be inserted. + + + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. + + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent value. + + + + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + iterator + + + + const_iterator + + const_iterator + + + Erase the element pointed to by position. + + + + The iterator following position before the erasure. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. + + + + + + + key_type const& + + size_type + + + Erase all elements with key equivalent to k. + + + + The number of elements erased. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + + + + + + iterator + + + iterator + + iterator + + + + const_iterator + + + const_iterator + + const_iterator + + + Erases the elements in the range from first to last. + + + + The iterator following the erased elements - i.e. last. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + void + + Erases all elements in the container. + + + + size() == 0 + + + + Never throws an exception. + + + + + + unordered_set& + + void + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. + + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. + + + + + + key_type const& + + size_type + + The number of elements with key equivalent to k. + + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<iterator, iterator> + + + A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + + size_type + + An upper bound on the number of buckets. + + + + + + size_type + + size_type + + + n < bucket_count() + + + + The number of elements in bucket + n. + + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key k. + + + + The return value is less than bucket_count() + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the first element in the bucket with index n. + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + + + float + + The average number of elements per bucket. + + + + + float + + Returns the current maximum load factor. + + + + + + float + + float + + Changes the container's maximum load factor, using z as a hint. + + + + + + size_type + + void + + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + + Invalidates iterators, and changes the order of elements + + + + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. + + + + + + + + + unordered_set<Key, T, Hash, Pred, Alloc>& + + + unordered_set<Key, T, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. + + + + + + + + + An unordered associative container that stores values. The same key can be stored multiple times. + + + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2009]. + + + + Template Parameters + + + + + + Value + + Value must be Assignable and CopyConstructible + + + + + Hash + + A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. + + + + + Pred + + A binary function object that implements an equivalence relation on values of type Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + + + + + Alloc + + An allocator whose value type is the same as the container's value type. + + + + + + + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. + + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. + + + + Value + + + Value + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + lvalue of value_type. + + + typename allocator_type::const_reference + const lvalue of value_type. + + + implementation-defined + + An unsigned integral type. + + + size_type can represent any non-negative value of + difference_type. + + + + + implementation-defined + + A signed integral type. + + Is identical to the difference type of + iterator and + const_iterator. + + + + + implementation-defined + + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + Convertible to + const_iterator. + + + + + implementation-defined + + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + + + + implementation-defined + + An iterator with the same value type, difference type and pointer and reference type as + iterator. + + A local_iterator object can be used to iterate through a single bucket. + + + + + implementation-defined + + A constant iterator with the same value type, difference type and pointer and reference type as + const_iterator. + + A const_local_iterator object can be used to iterate through a single bucket. + + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. + + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + + unordered_multiset const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + + unordered_multiset const& + + unordered_multiset& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + + + + + size_type + std::distance(begin(), end()) + + + + + value_type const& + + iterator + + Inserts obj in the container. + + + + An iterator pointing to the inserted element. + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + + value_type const& + + iterator + + + + const_iterator + + + value_type const& + + const_iterator + + + Inserts obj in the container. + + hint is a suggestion to where the element should be inserted. + + + + An iterator pointing to the inserted element. + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. + + + + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + iterator + + + + const_iterator + + const_iterator + + + Erase the element pointed to by position. + + + + The iterator following position before the erasure. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + + + + + key_type const& + + size_type + + + Erase all elements with key equivalent to k. + + + + The number of elements erased. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + + + + + + iterator + + + iterator + + iterator + + + + const_iterator + + + const_iterator + + const_iterator + + + Erases the elements in the range from first to last. + + + + The iterator following the erased elements - i.e. last. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + void + + Erases all elements in the container. + + + + size() == 0 + + + + Never throws an exception. + + + + + + unordered_multiset& + + void + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. + + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. + + + + + + key_type const& + + size_type + + The number of elements with key equivalent to k. + + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<iterator, iterator> + + + A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + + size_type + + An upper bound on the number of buckets. + + + + + + size_type + + size_type + + + n < bucket_count() + + + + The number of elements in bucket + n. + + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key k. + + + + The return value is less than bucket_count() + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the first element in the bucket with index n. + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + + + float + + The average number of elements per bucket. + + + + + float + + Returns the current maximum load factor. + + + + + + float + + float + + Changes the container's maximum load factor, using z as a hint. + + + + + + size_type + + void + + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + + Invalidates iterators, and changes the order of elements + + + + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. + + + + + + + + + unordered_multiset<Key, T, Hash, Pred, Alloc>& + + + unordered_multiset<Key, T, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. + + + + +
+ + +
+ + + + + + + An unordered associative container that associates unique keys with another value. + + + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2009]. + + + + Template Parameters + + + + + + Key + + Key must be Assignable and CopyConstructible. + + + + + T + + T must be CopyConstructible + + + + + Hash + + A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. + + + + + Pred + + A binary function object that implements an equivalence relation on values of type Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + + + + + Alloc + + An allocator whose value type is the same as the container's value type. + + + + + + + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. + + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. + + + + Key + + + std::pair<Key const, Value> + + + T + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + lvalue of value_type. + + + typename allocator_type::const_reference + const lvalue of value_type. + + + implementation-defined + + An unsigned integral type. + + + size_type can represent any non-negative value of + difference_type. + + + + + implementation-defined + + A signed integral type. + + Is identical to the difference type of + iterator and + const_iterator. + + + + + implementation-defined + + A iterator whose value type is + value_type. + + Any iterator category except output iterator. + + Convertible to + const_iterator. + + + + + implementation-defined + + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + + + + implementation-defined + + An iterator with the same value type, difference type and pointer and reference type as + iterator. + + A local_iterator object can be used to iterate through a single bucket. + + + + + implementation-defined + + A constant iterator with the same value type, difference type and pointer and reference type as + const_iterator. + + A const_local_iterator object can be used to iterate through a single bucket. + + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. + + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + + unordered_map const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + + unordered_map const& + + unordered_map& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + + + + + size_type + std::distance(begin(), end()) + + + + + value_type const& + + std::pair<iterator, bool> + + Inserts obj in the container if and only if there is no element in the container with an equivalent key. + + + + The bool component of the return type is true if an insert took place. + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + + value_type const& + + iterator + + + + const_iterator + + + value_type const& + + const_iterator + + + Inserts obj in the container if and only if there is no element in the container with an equivalent key. + + hint is a suggestion to where the element should be inserted. + + + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. + + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key. + + + + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + iterator + + + + const_iterator + + const_iterator + + + Erase the element pointed to by position. + + + + The iterator following position before the erasure. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. + + + + + + + key_type const& + + size_type + + + Erase all elements with key equivalent to k. + + + + The number of elements erased. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + + + + + + iterator + + + iterator + + iterator + + + + const_iterator + + + const_iterator + + const_iterator + + + Erases the elements in the range from first to last. + + + + The iterator following the erased elements - i.e. last. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + void + + Erases all elements in the container. + + + + size() == 0 + + + + Never throws an exception. + + + + + + unordered_map& + + void + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. + + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. + + + + + + key_type const& + + size_type + + The number of elements with key equivalent to k. + + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<iterator, iterator> + + + A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). + + + + + + key_type const& + + mapped_type& + + If the container does not already contain an elements with a key equivalent to k, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type()) + + + + A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to k + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + size_type + + The number of buckets. + + + + + size_type + + An upper bound on the number of buckets. + + + + + + size_type + + size_type + + + n < bucket_count() + + + + The number of elements in bucket + n. + + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key k. + + + + The return value is less than bucket_count() + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the first element in the bucket with index n. + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + + + float + + The average number of elements per bucket. + + + + + float + + Returns the current maximum load factor. + + + + + + float + + float + + Changes the container's maximum load factor, using z as a hint. + + + + + + size_type + + void + + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + + Invalidates iterators, and changes the order of elements + + + + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. + + + + + + + + + unordered_map<Key, T, Hash, Pred, Alloc>& + + + unordered_map<Key, T, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. + + + + + + + + + An unordered associative container that associates keys with another value. The same key can be stored multiple times. + + + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2009]. + + + + Template Parameters + + + + + + Key + + Key must be Assignable and CopyConstructible. + + + + + T + + T must be CopyConstructible + + + + + Hash + + A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. + + + + + Pred + + A binary function object that implements an equivalence relation on values of type Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. + + + + + Alloc + + An allocator whose value type is the same as the container's value type. + + + + + + + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. + + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. + + + + Key + + + std::pair<Key const, Value> + + + T + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + lvalue of value_type. + + + typename allocator_type::const_reference + const lvalue of value_type. + + + implementation-defined + + An unsigned integral type. + + + size_type can represent any non-negative value of + difference_type. + + + + + implementation-defined + + A signed integral type. + + Is identical to the difference type of + iterator and + const_iterator. + + + + + implementation-defined + + A iterator whose value type is + value_type. + + Any iterator category except output iterator. + + Convertible to + const_iterator. + + + + + implementation-defined + + A constant iterator whose value type is + value_type. + + Any iterator category except output iterator. + + + + + implementation-defined + + An iterator with the same value type, difference type and pointer and reference type as + iterator. + + A local_iterator object can be used to iterate through a single bucket. + + + + + implementation-defined + + A constant iterator with the same value type, difference type and pointer and reference type as + const_iterator. + + A const_local_iterator object can be used to iterate through a single bucket. + + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. + + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + + unordered_multimap const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + + unordered_multimap const& + + unordered_multimap& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + + + + + size_type + std::distance(begin(), end()) + + + + + value_type const& + + iterator + + Inserts obj in the container. + + + + An iterator pointing to the inserted element. + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + + value_type const& + + iterator + + + + const_iterator + + + value_type const& + + const_iterator + + + Inserts obj in the container. + + hint is a suggestion to where the element should be inserted. + + + + An iterator pointing to the inserted element. + + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. + + + + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. + + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + + + + + + iterator + + iterator + + + + const_iterator + + const_iterator + + + Erase the element pointed to by position. + + + + The iterator following position before the erasure. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + + + + + key_type const& + + size_type + + + Erase all elements with key equivalent to k. + + + + The number of elements erased. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + + + + + + iterator + + + iterator + + iterator + + + + const_iterator + + + const_iterator + + const_iterator + + + Erases the elements in the range from first to last. + + + + The iterator following the erased elements - i.e. last. + + + + Only throws an exception, if it is thrown by a call to hasher or key_equal. + + They don't get called by the current implementation Boost.Unordered but other implementations may call them. + + + + + void + + Erases all elements in the container. + + + + size() == 0 + + + + Never throws an exception. + + + + + + unordered_multimap& + + void + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. + + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. + + + + + + key_type const& + + size_type + + The number of elements with key equivalent to k. + + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<iterator, iterator> + + + A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + + size_type + + An upper bound on the number of buckets. + + + + + + size_type + + size_type + + + n < bucket_count() + + + + The number of elements in bucket + n. + + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key k. + + + + The return value is less than bucket_count() + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the first element in the bucket with index n. + + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range + [0, bucket_count()). + + + + A local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + + + float + + The average number of elements per bucket. + + + + + float + + Returns the current maximum load factor. + + + + + + float + + float + + Changes the container's maximum load factor, using z as a hint. + + + + + + size_type + + void + + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + + Invalidates iterators, and changes the order of elements + + + + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. + + + + + + + + + unordered_multimap<Key, T, Hash, Pred, Alloc>& + + + unordered_multimap<Key, T, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. + + + + +
+
From 048eed83152142ee45a75d097748ad4a3b08a88e Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 27 Aug 2007 23:00:30 +0000 Subject: [PATCH 097/175] Slight improvement to the unordered containers boostbook code. [SVN r39026] --- doc/ref.xml | 1106 ++++++++++++++++----------------------------------- 1 file changed, 346 insertions(+), 760 deletions(-) diff --git a/doc/ref.xml b/doc/ref.xml index 55a8b2ba..44743955 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -34,50 +34,27 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) For the normative reference see chapter 23 of - the working draft of the C++ standard [n2009]. - - - - Template Parameters + the working draft of the C++ standard [n2009]. + Template Parameters - - Value - - Value must be Assignable and CopyConstructible - - + Value + Value must be Assignable and CopyConstructible - - Hash - - A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. - - + Hash + A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. - - Pred - - A binary function object that implements an equivalence relation on values of type Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. - - + Pred + A binary function object that implements an equivalence relation on values of type Value. + A binary function object that induces an equivalence relation on values of type Key. + It takes two arguments of type Key and returns a value of type bool. - - Alloc - - An allocator whose value type is the same as the container's value type. - - - - - - - The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. - - The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. - + Alloc + An allocator whose value type is the same as the container's value type. + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. Value @@ -111,66 +88,44 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) implementation-defined - An unsigned integral type. - - - size_type can represent any non-negative value of - difference_type. - + An unsigned integral type. + size_type can represent any non-negative value of difference_type. implementation-defined - A signed integral type. - - Is identical to the difference type of - iterator and - const_iterator. - + A signed integral type. + Is identical to the difference type of iterator and const_iterator. implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - - Convertible to - const_iterator. - + A constant iterator whose value type is value_type. + Any iterator category except output iterator. + Convertible to const_iterator. implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - + A constant iterator whose value type is value_type. + Any iterator category except output iterator. implementation-defined - An iterator with the same value type, difference type and pointer and reference type as - iterator. - - A local_iterator object can be used to iterate through a single bucket. - + An iterator with the same value type, difference type and pointer and reference type as iterator. + A local_iterator object can be used to iterate through a single bucket. implementation-defined - A constant iterator with the same value type, difference type and pointer and reference type as - const_iterator. - - A const_local_iterator object can be used to iterate through a single bucket. - + A constant iterator with the same value type, difference type and pointer and reference type as const_iterator. + A const_local_iterator object can be used to iterate through a single bucket. @@ -194,8 +149,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size() == 0 - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. - + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. @@ -226,8 +180,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) allocator_type() - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. - + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. @@ -235,8 +188,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) unordered_set const& - The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. - + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. @@ -247,8 +199,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) unordered_set& - The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. - + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. @@ -313,22 +264,17 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, bool> - Inserts obj in the container if and only if there is no element in the container with an equivalent value. - + Inserts obj in the container if and only if there is no element in the container with an equivalent value. - The bool component of the return type is true if an insert took place. - - If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. - + The bool component of the return type is true if an insert took place. + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -351,26 +297,19 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Inserts obj in the container if and only if there is no element in the container with an equivalent value. - - hint is a suggestion to where the element should be inserted. - + Inserts obj in the container if and only if there is no element in the container with an equivalent value. + hint is a suggestion to where the element should be inserted. - If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. - + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent value. - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. @@ -386,16 +325,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent value. - + Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent value. - When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -412,22 +348,17 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Erase the element pointed to by position. - + Erase the element pointed to by position. - The iterator following position before the erasure. - + The iterator following position before the erasure. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. + They don't get called by the current implementation Boost.Unordered but other implementations may call them. - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. @@ -438,16 +369,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - Erase all elements with key equivalent to k. - + Erase all elements with key equivalent to k. - The number of elements erased. - + The number of elements erased. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. @@ -470,33 +398,26 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Erases the elements in the range from first to last. - + Erases the elements in the range from first to last. - The iterator following the erased elements - i.e. last. - + The iterator following the erased elements - i.e. last. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. + They don't get called by the current implementation Boost.Unordered but other implementations may call them. void - Erases all elements in the container. - + Erases all elements in the container. - size() == 0 - + size() == 0 - Never throws an exception. - + Never throws an exception. @@ -505,8 +426,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. - + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. @@ -537,8 +457,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) iterator - An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. @@ -547,8 +466,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The number of elements with key equivalent to k. - + The number of elements with key equivalent to k. @@ -565,8 +483,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, iterator> - A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - + A range with containing all elements with key equivalent to k. + If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + @@ -574,15 +494,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The number of buckets. - + The number of buckets. size_type - An upper bound on the number of buckets. - + An upper bound on the number of buckets. @@ -591,14 +509,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - - n < bucket_count() - + n < bucket_count() - The number of elements in bucket - n. - + The number of elements in bucket n. @@ -607,12 +521,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The index of the bucket which would contain an element with key k. - + The index of the bucket which would contain an element with key k. - The return value is less than bucket_count() - + The return value is less than bucket_count() @@ -629,13 +541,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_local_iterator - n shall be in the range - [0, bucket_count()). - + n shall be in the range [0, bucket_count()). - A local iterator pointing the first element in the bucket with index n. - + A local iterator pointing the first element in the bucket with index n. @@ -652,13 +561,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_local_iterator - n shall be in the range - [0, bucket_count()). - + n shall be in the range [0, bucket_count()). - A local iterator pointing the 'one past the end' element in the bucket with index n. - + A local iterator pointing the 'one past the end' element in the bucket with index n. @@ -666,15 +572,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) float - The average number of elements per bucket. - + The average number of elements per bucket. float - Returns the current maximum load factor. - + Returns the current maximum load factor. @@ -683,8 +587,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) float - Changes the container's maximum load factor, using z as a hint. - + Changes the container's maximum load factor, using z as a hint. @@ -693,14 +596,11 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. - - Invalidates iterators, and changes the order of elements - + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + Invalidates iterators, and changes the order of elements - The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. - + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. @@ -724,12 +624,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - x.swap(y) - + x.swap(y) - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. - + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. @@ -755,50 +653,27 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) For the normative reference see chapter 23 of - the working draft of the C++ standard [n2009]. - - - - Template Parameters + the working draft of the C++ standard [n2009]. + Template Parameters - - Value - - Value must be Assignable and CopyConstructible - - + Value + Value must be Assignable and CopyConstructible - - Hash - - A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. - - + Hash + A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. - - Pred - - A binary function object that implements an equivalence relation on values of type Value. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. - - + Pred + A binary function object that implements an equivalence relation on values of type Value. + A binary function object that induces an equivalence relation on values of type Key. + It takes two arguments of type Key and returns a value of type bool. - - Alloc - - An allocator whose value type is the same as the container's value type. - - - - - - - The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. - - The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. - + Alloc + An allocator whose value type is the same as the container's value type. + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. Value @@ -832,66 +707,44 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) implementation-defined - An unsigned integral type. - - - size_type can represent any non-negative value of - difference_type. - + An unsigned integral type. + size_type can represent any non-negative value of difference_type. implementation-defined - A signed integral type. - - Is identical to the difference type of - iterator and - const_iterator. - + A signed integral type. + Is identical to the difference type of iterator and const_iterator. implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - - Convertible to - const_iterator. - + A constant iterator whose value type is value_type. + Any iterator category except output iterator. + Convertible to const_iterator. implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - + A constant iterator whose value type is value_type. + Any iterator category except output iterator. implementation-defined - An iterator with the same value type, difference type and pointer and reference type as - iterator. - - A local_iterator object can be used to iterate through a single bucket. - + An iterator with the same value type, difference type and pointer and reference type as iterator. + A local_iterator object can be used to iterate through a single bucket. implementation-defined - A constant iterator with the same value type, difference type and pointer and reference type as - const_iterator. - - A const_local_iterator object can be used to iterate through a single bucket. - + A constant iterator with the same value type, difference type and pointer and reference type as const_iterator. + A const_local_iterator object can be used to iterate through a single bucket. @@ -915,8 +768,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size() == 0 - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. - + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. @@ -947,8 +799,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) allocator_type() - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. - + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. @@ -956,8 +807,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) unordered_multiset const& - The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. - + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. @@ -968,8 +818,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) unordered_multiset& - The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. - + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. @@ -1034,20 +883,16 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) iterator - Inserts obj in the container. - + Inserts obj in the container. - An iterator pointing to the inserted element. - + An iterator pointing to the inserted element. - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1070,24 +915,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Inserts obj in the container. - - hint is a suggestion to where the element should be inserted. - + Inserts obj in the container. + hint is a suggestion to where the element should be inserted. - An iterator pointing to the inserted element. - + An iterator pointing to the inserted element. - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1103,16 +942,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Inserts a range of elements into the container. - + Inserts a range of elements into the container. - When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1129,18 +965,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Erase the element pointed to by position. - + Erase the element pointed to by position. - The iterator following position before the erasure. - + The iterator following position before the erasure. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. + They don't get called by the current implementation Boost.Unordered but other implementations may call them. @@ -1153,16 +985,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - Erase all elements with key equivalent to k. - + Erase all elements with key equivalent to k. - The number of elements erased. - + The number of elements erased. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. @@ -1185,33 +1014,26 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Erases the elements in the range from first to last. - + Erases the elements in the range from first to last. - The iterator following the erased elements - i.e. last. - + The iterator following the erased elements - i.e. last. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. + They don't get called by the current implementation Boost.Unordered but other implementations may call them. void - Erases all elements in the container. - + Erases all elements in the container. - size() == 0 - + size() == 0 - Never throws an exception. - + Never throws an exception. @@ -1220,8 +1042,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. - + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. @@ -1252,8 +1073,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) iterator - An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. @@ -1262,8 +1082,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The number of elements with key equivalent to k. - + The number of elements with key equivalent to k. @@ -1280,8 +1099,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, iterator> - A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - + A range with containing all elements with key equivalent to k. + If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + @@ -1289,15 +1110,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The number of buckets. - + The number of buckets. size_type - An upper bound on the number of buckets. - + An upper bound on the number of buckets. @@ -1306,14 +1125,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - - n < bucket_count() - + n < bucket_count() - The number of elements in bucket - n. - + The number of elements in bucket n. @@ -1322,12 +1137,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The index of the bucket which would contain an element with key k. - + The index of the bucket which would contain an element with key k. - The return value is less than bucket_count() - + The return value is less than bucket_count() @@ -1344,13 +1157,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_local_iterator - n shall be in the range - [0, bucket_count()). - + n shall be in the range [0, bucket_count()). - A local iterator pointing the first element in the bucket with index n. - + A local iterator pointing the first element in the bucket with index n. @@ -1367,13 +1177,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_local_iterator - n shall be in the range - [0, bucket_count()). - + n shall be in the range [0, bucket_count()). - A local iterator pointing the 'one past the end' element in the bucket with index n. - + A local iterator pointing the 'one past the end' element in the bucket with index n. @@ -1381,15 +1188,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) float - The average number of elements per bucket. - + The average number of elements per bucket. float - Returns the current maximum load factor. - + Returns the current maximum load factor. @@ -1398,8 +1203,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) float - Changes the container's maximum load factor, using z as a hint. - + Changes the container's maximum load factor, using z as a hint. @@ -1408,14 +1212,11 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. - - Invalidates iterators, and changes the order of elements - + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + Invalidates iterators, and changes the order of elements - The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. - + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. @@ -1439,12 +1240,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - x.swap(y) - + x.swap(y) - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. - + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. @@ -1480,57 +1279,30 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) For the normative reference see chapter 23 of - the working draft of the C++ standard [n2009]. - - - - Template Parameters + the working draft of the C++ standard [n2009]. + Template Parameters - - Key - - Key must be Assignable and CopyConstructible. - - + Key + Key must be Assignable and CopyConstructible. - - T - - T must be CopyConstructible - - + T + T must be CopyConstructible - - Hash - - A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. - - + Hash + A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. - - Pred - - A binary function object that implements an equivalence relation on values of type Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. - - + Pred + A binary function object that implements an equivalence relation on values of type Key. + A binary function object that induces an equivalence relation on values of type Key. + It takes two arguments of type Key and returns a value of type bool. - - Alloc - - An allocator whose value type is the same as the container's value type. - - - - - - - The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. - - The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. - + Alloc + An allocator whose value type is the same as the container's value type. + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. Key @@ -1567,66 +1339,44 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) implementation-defined - An unsigned integral type. - - - size_type can represent any non-negative value of - difference_type. - + An unsigned integral type. + size_type can represent any non-negative value of difference_type. implementation-defined - A signed integral type. - - Is identical to the difference type of - iterator and - const_iterator. - + A signed integral type. + Is identical to the difference type of iterator and const_iterator. implementation-defined - A iterator whose value type is - value_type. - - Any iterator category except output iterator. - - Convertible to - const_iterator. - + A iterator whose value type is value_type. + Any iterator category except output iterator. + Convertible to const_iterator. implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - + A constant iterator whose value type is value_type. + Any iterator category except output iterator. implementation-defined - An iterator with the same value type, difference type and pointer and reference type as - iterator. - - A local_iterator object can be used to iterate through a single bucket. - + An iterator with the same value type, difference type and pointer and reference type as iterator. + A local_iterator object can be used to iterate through a single bucket. implementation-defined - A constant iterator with the same value type, difference type and pointer and reference type as - const_iterator. - - A const_local_iterator object can be used to iterate through a single bucket. - + A constant iterator with the same value type, difference type and pointer and reference type as const_iterator. + A const_local_iterator object can be used to iterate through a single bucket. @@ -1650,8 +1400,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size() == 0 - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. - + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. @@ -1682,8 +1431,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) allocator_type() - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. - + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. @@ -1691,8 +1439,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) unordered_map const& - The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. - + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. @@ -1703,8 +1450,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) unordered_map& - The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. - + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. @@ -1769,22 +1515,17 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, bool> - Inserts obj in the container if and only if there is no element in the container with an equivalent key. - + Inserts obj in the container if and only if there is no element in the container with an equivalent key. - The bool component of the return type is true if an insert took place. - - If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. - + The bool component of the return type is true if an insert took place. + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1807,26 +1548,19 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Inserts obj in the container if and only if there is no element in the container with an equivalent key. - - hint is a suggestion to where the element should be inserted. - + Inserts obj in the container if and only if there is no element in the container with an equivalent key. + hint is a suggestion to where the element should be inserted. - If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. - + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the elment with equivalent key. - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. @@ -1842,16 +1576,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key. - + Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key. - When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -1868,22 +1599,17 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Erase the element pointed to by position. - + Erase the element pointed to by position. - The iterator following position before the erasure. - + The iterator following position before the erasure. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. + They don't get called by the current implementation Boost.Unordered but other implementations may call them. - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - + In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. @@ -1894,16 +1620,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - Erase all elements with key equivalent to k. - + Erase all elements with key equivalent to k. - The number of elements erased. - + The number of elements erased. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. @@ -1926,33 +1649,26 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Erases the elements in the range from first to last. - + Erases the elements in the range from first to last. - The iterator following the erased elements - i.e. last. - + The iterator following the erased elements - i.e. last. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. + They don't get called by the current implementation Boost.Unordered but other implementations may call them. void - Erases all elements in the container. - + Erases all elements in the container. - size() == 0 - + size() == 0 - Never throws an exception. - + Never throws an exception. @@ -1961,8 +1677,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. - + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. @@ -1993,8 +1708,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) iterator - An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. @@ -2003,8 +1717,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The number of elements with key equivalent to k. - + The number of elements with key equivalent to k. @@ -2021,8 +1734,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, iterator> - A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - + A range with containing all elements with key equivalent to k. + If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + @@ -2031,20 +1746,16 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) mapped_type& - If the container does not already contain an elements with a key equivalent to k, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type()) - + If the container does not already contain an elements with a key equivalent to k, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type()) - A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to k - + A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to k - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2052,15 +1763,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The number of buckets. - + The number of buckets. size_type - An upper bound on the number of buckets. - + An upper bound on the number of buckets. @@ -2069,14 +1778,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - - n < bucket_count() - + n < bucket_count() - The number of elements in bucket - n. - + The number of elements in bucket n. @@ -2085,12 +1790,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The index of the bucket which would contain an element with key k. - + The index of the bucket which would contain an element with key k. - The return value is less than bucket_count() - + The return value is less than bucket_count() @@ -2107,13 +1810,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_local_iterator - n shall be in the range - [0, bucket_count()). - + n shall be in the range [0, bucket_count()). - A local iterator pointing the first element in the bucket with index n. - + A local iterator pointing the first element in the bucket with index n. @@ -2130,13 +1830,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_local_iterator - n shall be in the range - [0, bucket_count()). - + n shall be in the range [0, bucket_count()). - A local iterator pointing the 'one past the end' element in the bucket with index n. - + A local iterator pointing the 'one past the end' element in the bucket with index n. @@ -2144,15 +1841,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) float - The average number of elements per bucket. - + The average number of elements per bucket. float - Returns the current maximum load factor. - + Returns the current maximum load factor. @@ -2161,8 +1856,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) float - Changes the container's maximum load factor, using z as a hint. - + Changes the container's maximum load factor, using z as a hint. @@ -2171,14 +1865,11 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. - - Invalidates iterators, and changes the order of elements - + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + Invalidates iterators, and changes the order of elements - The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. - + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. @@ -2204,12 +1895,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - x.swap(y) - + x.swap(y) - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. - + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. @@ -2237,57 +1926,30 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) For the normative reference see chapter 23 of - the working draft of the C++ standard [n2009]. - - - - Template Parameters + the working draft of the C++ standard [n2009]. + Template Parameters - - Key - - Key must be Assignable and CopyConstructible. - - + Key + Key must be Assignable and CopyConstructible. - - T - - T must be CopyConstructible - - + T + T must be CopyConstructible - - Hash - - A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. - - + Hash + A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. - - Pred - - A binary function object that implements an equivalence relation on values of type Key. A binary function object that induces an equivalence relation on values of type Key. It takes two arguments of type Key and returns a value of type bool. - - + Pred + A binary function object that implements an equivalence relation on values of type Key. + A binary function object that induces an equivalence relation on values of type Key. + It takes two arguments of type Key and returns a value of type bool. - - Alloc - - An allocator whose value type is the same as the container's value type. - - - - - - - The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. - - The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. - + Alloc + An allocator whose value type is the same as the container's value type. + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. Key @@ -2324,66 +1986,44 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) implementation-defined - An unsigned integral type. - - - size_type can represent any non-negative value of - difference_type. - + An unsigned integral type. + size_type can represent any non-negative value of difference_type. implementation-defined - A signed integral type. - - Is identical to the difference type of - iterator and - const_iterator. - + A signed integral type. + Is identical to the difference type of iterator and const_iterator. implementation-defined - A iterator whose value type is - value_type. - - Any iterator category except output iterator. - - Convertible to - const_iterator. - + A iterator whose value type is value_type. + Any iterator category except output iterator. + Convertible to const_iterator. implementation-defined - A constant iterator whose value type is - value_type. - - Any iterator category except output iterator. - + A constant iterator whose value type is value_type. + Any iterator category except output iterator. implementation-defined - An iterator with the same value type, difference type and pointer and reference type as - iterator. - - A local_iterator object can be used to iterate through a single bucket. - + An iterator with the same value type, difference type and pointer and reference type as iterator. + A local_iterator object can be used to iterate through a single bucket. implementation-defined - A constant iterator with the same value type, difference type and pointer and reference type as - const_iterator. - - A const_local_iterator object can be used to iterate through a single bucket. - + A constant iterator with the same value type, difference type and pointer and reference type as const_iterator. + A const_local_iterator object can be used to iterate through a single bucket. @@ -2407,8 +2047,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size() == 0 - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. - + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocatorand a maximum load factor of 1.0. @@ -2439,8 +2078,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) allocator_type() - Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. - + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. @@ -2448,8 +2086,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) unordered_multimap const& - The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. - + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. @@ -2460,8 +2097,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) unordered_multimap& - The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. - + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. @@ -2526,20 +2162,16 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) iterator - Inserts obj in the container. - + Inserts obj in the container. - An iterator pointing to the inserted element. - + An iterator pointing to the inserted element. - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2562,24 +2194,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Inserts obj in the container. - - hint is a suggestion to where the element should be inserted. - + Inserts obj in the container. + hint is a suggestion to where the element should be inserted. - An iterator pointing to the inserted element. - + An iterator pointing to the inserted element. - If an exception is thrown by an operation other than a call to hasher the function has no effect. - + If an exception is thrown by an operation other than a call to hasher the function has no effect. - The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. - - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2595,16 +2221,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Inserts a range of elements into the container. - + Inserts a range of elements into the container. - When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. - Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. @@ -2621,18 +2244,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Erase the element pointed to by position. - + Erase the element pointed to by position. - The iterator following position before the erasure. - + The iterator following position before the erasure. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. + They don't get called by the current implementation Boost.Unordered but other implementations may call them. @@ -2645,16 +2264,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - Erase all elements with key equivalent to k. - + Erase all elements with key equivalent to k. - The number of elements erased. - + The number of elements erased. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. @@ -2677,33 +2293,26 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_iterator - Erases the elements in the range from first to last. - + Erases the elements in the range from first to last. - The iterator following the erased elements - i.e. last. - + The iterator following the erased elements - i.e. last. - Only throws an exception, if it is thrown by a call to hasher or key_equal. - - They don't get called by the current implementation Boost.Unordered but other implementations may call them. - + Only throws an exception, if it is thrown by a call to hasher or key_equal. + They don't get called by the current implementation Boost.Unordered but other implementations may call them. void - Erases all elements in the container. - + Erases all elements in the container. - size() == 0 - + size() == 0 - Never throws an exception. - + Never throws an exception. @@ -2712,8 +2321,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. - + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. @@ -2744,8 +2352,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) iterator - An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. - + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. @@ -2754,8 +2361,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The number of elements with key equivalent to k. - + The number of elements with key equivalent to k. @@ -2772,8 +2378,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, iterator> - A range with containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). - + A range with containing all elements with key equivalent to k. + If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + @@ -2781,15 +2389,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The number of buckets. - + The number of buckets. size_type - An upper bound on the number of buckets. - + An upper bound on the number of buckets. @@ -2798,14 +2404,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - - n < bucket_count() - + n < bucket_count() - The number of elements in bucket - n. - + The number of elements in bucket n. @@ -2814,12 +2416,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) size_type - The index of the bucket which would contain an element with key k. - + The index of the bucket which would contain an element with key k. - The return value is less than bucket_count() - + The return value is less than bucket_count() @@ -2836,13 +2436,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_local_iterator - n shall be in the range - [0, bucket_count()). - + n shall be in the range [0, bucket_count()). - A local iterator pointing the first element in the bucket with index n. - + A local iterator pointing the first element in the bucket with index n. @@ -2859,13 +2456,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) const_local_iterator - n shall be in the range - [0, bucket_count()). - + n shall be in the range [0, bucket_count()). - A local iterator pointing the 'one past the end' element in the bucket with index n. - + A local iterator pointing the 'one past the end' element in the bucket with index n. @@ -2873,15 +2467,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) float - The average number of elements per bucket. - + The average number of elements per bucket. float - Returns the current maximum load factor. - + Returns the current maximum load factor. @@ -2890,8 +2482,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) float - Changes the container's maximum load factor, using z as a hint. - + Changes the container's maximum load factor, using z as a hint. @@ -2900,14 +2491,11 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. - - Invalidates iterators, and changes the order of elements - + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + Invalidates iterators, and changes the order of elements - The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. - + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. @@ -2933,12 +2521,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) void - x.swap(y) - + x.swap(y) - Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. - + Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. From f5a56fee81286d1af06bd7d53b96f8f2dc3e3a9f Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 27 Aug 2007 23:01:34 +0000 Subject: [PATCH 098/175] Fix a typo. [SVN r39027] --- doc/ref.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ref.xml b/doc/ref.xml index 44743955..99b161ae 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -483,7 +483,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, iterator> - A range with containing all elements with key equivalent to k. + A range containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). @@ -1099,7 +1099,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, iterator> - A range with containing all elements with key equivalent to k. + A range containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). @@ -1734,7 +1734,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, iterator> - A range with containing all elements with key equivalent to k. + A range containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). @@ -2378,7 +2378,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) std::pair<iterator, iterator> - A range with containing all elements with key equivalent to k. + A range containing all elements with key equivalent to k. If the container doesn't container any such elements, returns std::make_pair(b.end(),b.end()). From ba7d4151c94469520ebfe1a5ed0cf3fede2bd924 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Wed, 10 Oct 2007 21:47:21 +0000 Subject: [PATCH 099/175] Fix the authors and copyrights in the unordered container document info. [SVN r39911] --- doc/unordered.qbk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/unordered.qbk b/doc/unordered.qbk index c8a020ed..1cef8921 100644 --- a/doc/unordered.qbk +++ b/doc/unordered.qbk @@ -4,8 +4,9 @@ [library Unordered Associative Containers [quickbook 1.4] - [authors [Maitin-Shepard, Jeremy B.], [James, Daniel]] - [copyright 2005 2007 Daniel James] + [authors [James, Daniel]] + [copyright 2003 2004 Jeremy B. Maitin-Shepard] + [copyright 2005 2006 2007 Daniel James] [purpose std::tr1 compliant hash containers] [id unordered] [dirname unordered] From 2cdb800ec39b2f0c1c537a8dc78c7220e42b41fe Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 11 Oct 2007 22:47:17 +0000 Subject: [PATCH 100/175] Change insert/erase as specified in n2369. [SVN r39949] --- doc/rationale.qbk | 13 -- doc/ref.xml | 380 +++++++++++++++------------------------------- 2 files changed, 120 insertions(+), 273 deletions(-) diff --git a/doc/rationale.qbk b/doc/rationale.qbk index 3982032a..33033798 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -120,17 +120,4 @@ should probably change it to a slow swap. The current proposal is that insert, erase and rehash are stable - so they are here. -[h3 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#528 - 528. TR1: issue 6.19 vs 6.3.4.3/2 (and 6.3.4.5/2)]] - -In the current implementation, for `unordered_set` and -`unordered_multiset`, `iterator` and `const_iterator` have the same type and -`local_iterator` and `const_local_iterator` also have the same type. This makes -it impossible to implement the header exactly as described in the synopsis, as -some member functions are overloaded by the same type. - -The proposed resolution is to add a new subsection to 17.4.4: -[:An implementation shall not supply an overloaded function signature specified in any library clause if such a signature would be inherently ambiguous during overload resolution due to two library types referring to the same type.] -So I don't supply the `iterator` overloads. - [endsect] diff --git a/doc/ref.xml b/doc/ref.xml index 99b161ae..4ee929ab 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -277,25 +277,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - - - - iterator - - - value_type const& - - iterator - - - - const_iterator - - - value_type const& - - const_iterator - + + + const_iterator + + + value_type const& + + iterator Inserts obj in the container if and only if there is no element in the container with an equivalent value. hint is a suggestion to where the element should be inserted. @@ -309,9 +298,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - In this implementation, iterator and const_iterator are the same type, so a single overload is defined to implement both signatures. - +