//===----------------------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is dual licensed under the MIT and the University of Illinois Open // Source Licenses. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef SUPPORT_CONTAINER_TEST_TYPES_H #define SUPPORT_CONTAINER_TEST_TYPES_H // container_test_types.h - A set of types used for testing STL containers. // The types container within this header are used to test the requirements in // [container.requirements.general]. The header is made up of 3 main components: // // * test-types: 'CopyInsertable', 'MoveInsertable' and 'EmplaceConstructible' - // These test types are used to test the container requirements of the same // name. These test types use the global 'AllocatorConstructController' to // assert that they are only constructed by the containers allocator. // // * test-allocator: 'ContainerTestAllocator' - This test allocator is used to // test the portions of [container.requirements.general] that pertain to the // containers allocator. The three primary jobs of the test allocator are: // 1. Enforce that 'a.construct(...)' and 'a.destroy(...)' are only ever // instantiated for 'Container::value_type'. // 2. Provide a mechanism of checking calls to 'a.construct(Args...)'. // Including controlling when and with what types 'a.construct(...)' // may be called with. // 3. Support the test types internals by controlling the global // 'AllocatorConstructController' object. // // * 'AllocatorConstructController' - This type defines an interface for testing // the construction of types using an allocator. This type is used to communicate // between the test author, the containers allocator, and the types // being constructed by the container. // The controller's primary functions are: // 1. Allow calls to 'a.construct(p, args...)' to be checked by a test. // The test uses 'cc->expect()' to specify that the allocator // should expect one call to 'a.construct' with the specified argument // types. // 2. Controlling the value of 'cc->isInAllocatorConstruct()' within the // 'construct' method. The test-types use this value to assert that // they are being constructed by the allocator. // // 'AllocatorConstructController' enforces the Singleton pattern since the // test-types, test-allocator and test need to share the same controller // object. A pointer to the global controller is returned by // 'getConstructController()'. // //---------------------------------------------------------------------------- /* * Usage: The following example checks that 'unordered_map::emplace(Args&&...)' * with 'Args = [CopyInsertable<1> const&, CopyInsertible<2>&&]' * calls 'alloc.construct(value_type*, Args&&...)' with the same types. * * // Typedefs for container * using Key = CopyInsertible<1>; * using Value = CopyInsertible<2>; * using ValueTp = std::pair; * using Alloc = ContainerTestAllocator; * using Map = std::unordered_map, std::equal_to, Alloc>; * * // Get the global controller, reset it, and construct an allocator with * // the controller. * ConstructController* cc = getConstructController(); * cc->reset(); * * // Create a Map and a Key and Value to insert. Note that the test-allocator * // does not need to be given 'cc'. * Map m; * const Key k(1); * Value v(1); * * // Tell the controller to expect a construction from the specified types. * cc->expect(); * * // Emplace the objects into the container. 'Alloc.construct(p, UArgs...)' * // will assert 'cc->check()' is true which will consume * // the call to 'cc->expect<...>()'. * m.emplace(k, std::move(v)); * * // Assert that the "expect" was consumed by a matching "check" call within * // Alloc. * assert(!cc->unexpected()); * */ #include #include #include "test_macros.h" #if TEST_STD_VER < 11 #error This header requires C++11 or greater #endif namespace detail { // TypeID - Represent a unique identifier for a type. TypeID allows equality // comparisons between different types. struct TypeID { friend bool operator==(TypeID const& LHS, TypeID const& RHS) {return LHS.m_id == RHS.m_id; } friend bool operator!=(TypeID const& LHS, TypeID const& RHS) {return LHS.m_id != RHS.m_id; } private: explicit constexpr TypeID(const int* xid) : m_id(xid) {} const int* const m_id; template friend class TypeInfo; }; // TypeInfo - Represent information for the specified type 'T', including a // unique TypeID. template class TypeInfo { public: typedef T value_type; typedef TypeID ID; static ID const& GetID() { static ID id(&dummy_addr); return id; } private: static const int dummy_addr; }; template inline bool operator==(TypeInfo const&, TypeInfo const&) { return std::is_same::value; } template inline bool operator!=(TypeInfo const& lhs, TypeInfo const& rhs) { return !(lhs == rhs); } template const int TypeInfo::dummy_addr = 42; // makeTypeID - Return the TypeID for the specified type 'T'. template inline constexpr TypeID const& makeTypeID() { return TypeInfo::GetID(); } template struct ArgumentListID {}; // makeArgumentID - Create and return a unique identifier for a given set // of arguments. template inline constexpr TypeID const& makeArgumentID() { return makeTypeID>(); } } // namespace detail //===----------------------------------------------------------------------===// // AllocatorConstructController //===----------------------------------------------------------------------===// struct AllocatorConstructController { const detail::TypeID* m_expected_args; bool m_allow_constructions; bool m_allow_unchecked; int m_expected_count; void clear() { m_expected_args = nullptr; m_expected_count = -1; } // Check for and consume an expected construction added by 'expect'. // Return true if the construction was expected and false otherwise. // This should only be called by 'Allocator.construct'. bool check(detail::TypeID const& tid) { if (!m_expected_args) assert(m_allow_unchecked); bool res = *m_expected_args == tid; if (m_expected_count == -1 || --m_expected_count == -1) m_expected_args = nullptr; return res; } // Return true iff there is an unchecked construction expression. bool unchecked() { return m_expected_args != nullptr; } // Expect a call to Allocator::construct with Args that match 'tid'. void expect(detail::TypeID const& tid) { assert(!unchecked()); m_expected_args = &tid; } template void expect(int times = 1) { assert(!unchecked()); assert(times > 0); m_expected_count = times - 1; m_expected_args = &detail::makeArgumentID(); } template bool check() { return check(detail::makeArgumentID()); } // Return true iff the program is currently within a call to "Allocator::construct" bool isInAllocatorConstruct() const { return m_allow_constructions; } void inAllocatorConstruct(bool value = true) { m_allow_constructions = value; } void allowUnchecked(bool value = true) { m_allow_unchecked = value; } void reset() { m_allow_constructions = false; m_expected_args = nullptr; m_allow_unchecked = false; m_expected_count = -1; } private: friend AllocatorConstructController* getConstructController(); AllocatorConstructController() { reset(); } AllocatorConstructController(AllocatorConstructController const&); AllocatorConstructController& operator=(AllocatorConstructController const&); }; typedef AllocatorConstructController ConstructController; // getConstructController - Return the global allocator construction controller. inline ConstructController* getConstructController() { static ConstructController c; return &c; } //===----------------------------------------------------------------------===// // ContainerTestAllocator //===----------------------------------------------------------------------===// // ContainerTestAllocator - A STL allocator type that only allows 'construct' // and 'destroy' to be called for 'AllowConstructT' types. ContainerTestAllocator // uses the 'AllocatorConstructionController' interface. template class ContainerTestAllocator { struct InAllocatorConstructGuard { ConstructController *m_cc; bool m_old; InAllocatorConstructGuard(ConstructController* cc) : m_cc(cc) { if (m_cc) { m_old = m_cc->isInAllocatorConstruct(); m_cc->inAllocatorConstruct(true); } } ~InAllocatorConstructGuard() { if (m_cc) m_cc->inAllocatorConstruct(m_old); } private: InAllocatorConstructGuard(InAllocatorConstructGuard const&); InAllocatorConstructGuard& operator=(InAllocatorConstructGuard const&); }; public: typedef T value_type; int construct_called; int destroy_called; ConstructController* controller; ContainerTestAllocator() TEST_NOEXCEPT : controller(getConstructController()) {} explicit ContainerTestAllocator(ConstructController* c) : controller(c) {} template ContainerTestAllocator(ContainerTestAllocator other) TEST_NOEXCEPT : controller(other.controller) {} T* allocate(std::size_t n) { return static_cast(::operator new(n*sizeof(T))); } void deallocate(T* p, std::size_t) { return ::operator delete(static_cast(p)); } template void construct(Up* p, Args&&... args) { static_assert((std::is_same::value), "Only allowed to construct Up"); assert(controller->check()); { InAllocatorConstructGuard g(controller); ::new ((void*)p) Up(std::forward(args)...); } } template void destroy(Up* p) { static_assert((std::is_same::value), "Only allowed to destroy Up"); { InAllocatorConstructGuard g(controller); p->~Up(); } } friend bool operator==(ContainerTestAllocator, ContainerTestAllocator) {return true;} friend bool operator!=(ContainerTestAllocator x, ContainerTestAllocator y) {return !(x == y);} }; namespace test_detail { typedef ContainerTestAllocator A1; typedef std::allocator_traits A1T; typedef ContainerTestAllocator A2; typedef std::allocator_traits A2T; static_assert(std::is_same, A2T>::value, ""); static_assert(std::is_same, A1T>::value, ""); } // end namespace test_detail //===----------------------------------------------------------------------===// // 'CopyInsertable', 'MoveInsertable' and 'EmplaceConstructible' test types //===----------------------------------------------------------------------===// template struct CopyInsertable { int data; mutable bool copied_once; bool constructed_under_allocator; explicit CopyInsertable(int val) : data(val), copied_once(false), constructed_under_allocator(false) { if (getConstructController()->isInAllocatorConstruct()) { copied_once = true; constructed_under_allocator = true; } } CopyInsertable() : data(0), copied_once(false), constructed_under_allocator(true) { assert(getConstructController()->isInAllocatorConstruct()); } CopyInsertable(CopyInsertable const& other) : data(other.data), copied_once(true), constructed_under_allocator(true) { assert(getConstructController()->isInAllocatorConstruct()); assert(other.copied_once == false); other.copied_once = true; } CopyInsertable(CopyInsertable& other) : data(other.data), copied_once(true), constructed_under_allocator(true) { assert(getConstructController()->isInAllocatorConstruct()); assert(other.copied_once == false); other.copied_once = true; } CopyInsertable(CopyInsertable&& other) : CopyInsertable(other) {} // Forgive pair for not downcasting this to an lvalue in its constructors. CopyInsertable(CopyInsertable const && other) : CopyInsertable(other) {} template CopyInsertable(Args&&...) { assert(false); } ~CopyInsertable() { assert(constructed_under_allocator == getConstructController()->isInAllocatorConstruct()); } void reset(int value) { data = value; copied_once = false; constructed_under_allocator = false; } }; template bool operator==(CopyInsertable const& L, CopyInsertable const& R) { return L.data == R.data; } template bool operator!=(CopyInsertable const& L, CopyInsertable const& R) { return L.data != R.data; } template bool operator <(CopyInsertable const& L, CopyInsertable const& R) { return L.data < R.data; } #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD #else namespace std { #endif template struct hash< ::CopyInsertable > { typedef ::CopyInsertable argument_type; typedef size_t result_type; size_t operator()(argument_type const& arg) const { return arg.data; } }; template class map; template class multimap; template class set; template class multiset; template class unordered_map; template class unordered_multimap; template class unordered_set; template class unordered_multiset; #ifdef _LIBCPP_END_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD #else } // end namespace std #endif // TCT - Test container type namespace TCT { template , class Value = CopyInsertable<2>, class ValueTp = std::pair > using unordered_map = std::unordered_map, std::equal_to, ContainerTestAllocator >; template , class Value = CopyInsertable<2>, class ValueTp = std::pair > using map = std::map, ContainerTestAllocator >; template , class Value = CopyInsertable<2>, class ValueTp = std::pair > using unordered_multimap = std::unordered_multimap, std::equal_to, ContainerTestAllocator >; template , class Value = CopyInsertable<2>, class ValueTp = std::pair > using multimap = std::multimap, ContainerTestAllocator >; template > using unordered_set = std::unordered_set, std::equal_to, ContainerTestAllocator >; template > using set = std::set, ContainerTestAllocator >; template > using unordered_multiset = std::unordered_multiset, std::equal_to, ContainerTestAllocator >; template > using multiset = std::multiset, ContainerTestAllocator >; } // end namespace TCT #endif // SUPPORT_CONTAINER_TEST_TYPES_H