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