| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // <memory> |
| |
| // unique_ptr |
| |
| //============================================================================= |
| // TESTING unique_ptr(pointer, deleter) |
| // |
| // Concerns: |
| // 1 unique_ptr(pointer, deleter&&) only requires a MoveConstructible deleter. |
| // 2 unique_ptr(pointer, deleter&) requires a CopyConstructible deleter. |
| // 3 unique_ptr<T, D&>(pointer, deleter) does not require a CopyConstructible deleter. |
| // 4 unique_ptr<T, D const&>(pointer, deleter) does not require a CopyConstructible deleter. |
| // 5 unique_ptr(pointer, deleter) should work for derived pointers. |
| // 6 unique_ptr(pointer, deleter) should work with function pointers. |
| // 7 unique_ptr<void> should work. |
| |
| #include <memory> |
| #include <cassert> |
| |
| #include "test_macros.h" |
| #include "unique_ptr_test_helper.h" |
| |
| bool my_free_called = false; |
| |
| void my_free(void*) { my_free_called = true; } |
| |
| #if TEST_STD_VER >= 11 |
| struct DeleterBase { |
| void operator()(void*) const {} |
| }; |
| struct CopyOnlyDeleter : DeleterBase { |
| CopyOnlyDeleter() = default; |
| CopyOnlyDeleter(CopyOnlyDeleter const&) = default; |
| CopyOnlyDeleter(CopyOnlyDeleter&&) = delete; |
| }; |
| struct MoveOnlyDeleter : DeleterBase { |
| MoveOnlyDeleter() = default; |
| MoveOnlyDeleter(MoveOnlyDeleter&&) = default; |
| }; |
| struct NoCopyMoveDeleter : DeleterBase { |
| NoCopyMoveDeleter() = default; |
| NoCopyMoveDeleter(NoCopyMoveDeleter const&) = delete; |
| }; |
| #endif |
| |
| template <bool IsArray> |
| void test_sfinae() { |
| #if TEST_STD_VER >= 11 |
| typedef typename std::conditional<!IsArray, int, int[]>::type VT; |
| { |
| using D = CopyOnlyDeleter; |
| using U = std::unique_ptr<VT, D>; |
| static_assert(std::is_constructible<U, int*, D const&>::value, ""); |
| static_assert(std::is_constructible<U, int*, D&>::value, ""); |
| static_assert(std::is_constructible<U, int*, D&&>::value, ""); |
| // FIXME: __libcpp_compressed_pair attempts to perform a move even though |
| // it should only copy. |
| //D d; |
| //U u(nullptr, std::move(d)); |
| } |
| { |
| using D = MoveOnlyDeleter; |
| using U = std::unique_ptr<VT, D>; |
| static_assert(!std::is_constructible<U, int*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, int*, D&>::value, ""); |
| static_assert(std::is_constructible<U, int*, D&&>::value, ""); |
| D d; |
| U u(nullptr, std::move(d)); |
| } |
| { |
| using D = NoCopyMoveDeleter; |
| using U = std::unique_ptr<VT, D>; |
| static_assert(!std::is_constructible<U, int*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, int*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, int*, D&&>::value, ""); |
| } |
| { |
| using D = NoCopyMoveDeleter; |
| using U = std::unique_ptr<VT, D&>; |
| static_assert(!std::is_constructible<U, int*, D const&>::value, ""); |
| static_assert(std::is_constructible<U, int*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, int*, D&&>::value, ""); |
| static_assert(!std::is_constructible<U, int*, const D&&>::value, ""); |
| } |
| { |
| using D = NoCopyMoveDeleter; |
| using U = std::unique_ptr<VT, const D&>; |
| static_assert(std::is_constructible<U, int*, D const&>::value, ""); |
| static_assert(std::is_constructible<U, int*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, int*, D&&>::value, ""); |
| static_assert(!std::is_constructible<U, int*, const D&&>::value, ""); |
| } |
| #endif |
| } |
| |
| template <bool IsArray> |
| void test_noexcept() { |
| #if TEST_STD_VER >= 11 |
| typedef typename std::conditional<!IsArray, int, int[]>::type VT; |
| { |
| using D = CopyOnlyDeleter; |
| using U = std::unique_ptr<VT, D>; |
| static_assert(std::is_nothrow_constructible<U, int*, D const&>::value, ""); |
| static_assert(std::is_nothrow_constructible<U, int*, D&>::value, ""); |
| static_assert(std::is_nothrow_constructible<U, int*, D&&>::value, ""); |
| } |
| { |
| using D = MoveOnlyDeleter; |
| using U = std::unique_ptr<VT, D>; |
| static_assert(std::is_nothrow_constructible<U, int*, D&&>::value, ""); |
| D d; |
| U u(nullptr, std::move(d)); |
| } |
| { |
| using D = NoCopyMoveDeleter; |
| using U = std::unique_ptr<VT, D&>; |
| static_assert(std::is_nothrow_constructible<U, int*, D&>::value, ""); |
| } |
| { |
| using D = NoCopyMoveDeleter; |
| using U = std::unique_ptr<VT, const D&>; |
| static_assert(std::is_nothrow_constructible<U, int*, D const&>::value, ""); |
| static_assert(std::is_nothrow_constructible<U, int*, D&>::value, ""); |
| } |
| #endif |
| } |
| |
| void test_sfinae_runtime() { |
| #if TEST_STD_VER >= 11 |
| { |
| using D = CopyOnlyDeleter; |
| using U = std::unique_ptr<A[], D>; |
| static_assert(std::is_nothrow_constructible<U, A*, D const&>::value, ""); |
| static_assert(std::is_nothrow_constructible<U, A*, D&>::value, ""); |
| static_assert(std::is_nothrow_constructible<U, A*, D&&>::value, ""); |
| |
| static_assert(!std::is_constructible<U, B*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&&>::value, ""); |
| // FIXME: __libcpp_compressed_pair attempts to perform a move even though |
| // it should only copy. |
| //D d; |
| //U u(nullptr, std::move(d)); |
| } |
| { |
| using D = MoveOnlyDeleter; |
| using U = std::unique_ptr<A[], D>; |
| static_assert(!std::is_constructible<U, A*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, A*, D&>::value, ""); |
| static_assert(std::is_nothrow_constructible<U, A*, D&&>::value, ""); |
| |
| static_assert(!std::is_constructible<U, B*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&&>::value, ""); |
| D d; |
| U u(nullptr, std::move(d)); |
| } |
| { |
| using D = NoCopyMoveDeleter; |
| using U = std::unique_ptr<A[], D>; |
| static_assert(!std::is_constructible<U, A*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, A*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, A*, D&&>::value, ""); |
| |
| static_assert(!std::is_constructible<U, B*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&&>::value, ""); |
| } |
| { |
| using D = NoCopyMoveDeleter; |
| using U = std::unique_ptr<A[], D&>; |
| static_assert(!std::is_constructible<U, A*, D const&>::value, ""); |
| static_assert(std::is_nothrow_constructible<U, A*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, A*, D&&>::value, ""); |
| static_assert(!std::is_constructible<U, A*, const D&&>::value, ""); |
| |
| static_assert(!std::is_constructible<U, B*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, const D&&>::value, ""); |
| } |
| { |
| using D = NoCopyMoveDeleter; |
| using U = std::unique_ptr<A[], const D&>; |
| static_assert(std::is_nothrow_constructible<U, A*, D const&>::value, ""); |
| static_assert(std::is_nothrow_constructible<U, A*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, A*, D&&>::value, ""); |
| static_assert(!std::is_constructible<U, A*, const D&&>::value, ""); |
| |
| static_assert(!std::is_constructible<U, B*, D const&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, D&&>::value, ""); |
| static_assert(!std::is_constructible<U, B*, const D&&>::value, ""); |
| } |
| #endif |
| } |
| |
| template <bool IsArray> |
| void test_basic() { |
| typedef typename std::conditional<!IsArray, A, A[]>::type VT; |
| const int expect_alive = IsArray ? 5 : 1; |
| { // MoveConstructible deleter (C-1) |
| A* p = newValue<VT>(expect_alive); |
| assert(A::count == expect_alive); |
| std::unique_ptr<VT, Deleter<VT> > s(p, Deleter<VT>(5)); |
| assert(s.get() == p); |
| assert(s.get_deleter().state() == 5); |
| } |
| assert(A::count == 0); |
| { // CopyConstructible deleter (C-2) |
| A* p = newValue<VT>(expect_alive); |
| assert(A::count == expect_alive); |
| CopyDeleter<VT> d(5); |
| std::unique_ptr<VT, CopyDeleter<VT> > s(p, d); |
| assert(s.get() == p); |
| assert(s.get_deleter().state() == 5); |
| d.set_state(6); |
| assert(s.get_deleter().state() == 5); |
| } |
| assert(A::count == 0); |
| { // Reference deleter (C-3) |
| A* p = newValue<VT>(expect_alive); |
| assert(A::count == expect_alive); |
| NCDeleter<VT> d(5); |
| std::unique_ptr<VT, NCDeleter<VT>&> s(p, d); |
| assert(s.get() == p); |
| assert(&s.get_deleter() == &d); |
| assert(s.get_deleter().state() == 5); |
| d.set_state(6); |
| assert(s.get_deleter().state() == 6); |
| } |
| assert(A::count == 0); |
| { // Const Reference deleter (C-4) |
| A* p = newValue<VT>(expect_alive); |
| assert(A::count == expect_alive); |
| NCConstDeleter<VT> d(5); |
| std::unique_ptr<VT, NCConstDeleter<VT> const&> s(p, d); |
| assert(s.get() == p); |
| assert(s.get_deleter().state() == 5); |
| assert(&s.get_deleter() == &d); |
| } |
| assert(A::count == 0); |
| { // Void and function pointers (C-6,7) |
| typedef typename std::conditional<IsArray, int[], int>::type VT2; |
| my_free_called = false; |
| { |
| int i = 0; |
| std::unique_ptr<VT2, void (*)(void*)> s(&i, my_free); |
| assert(s.get() == &i); |
| assert(s.get_deleter() == my_free); |
| assert(!my_free_called); |
| } |
| assert(my_free_called); |
| } |
| } |
| |
| void test_basic_single() { |
| assert(A::count == 0); |
| assert(B::count == 0); |
| { // Derived pointers (C-5) |
| B* p = new B; |
| assert(A::count == 1); |
| assert(B::count == 1); |
| std::unique_ptr<A, Deleter<A> > s(p, Deleter<A>(5)); |
| assert(s.get() == p); |
| assert(s.get_deleter().state() == 5); |
| } |
| assert(A::count == 0); |
| assert(B::count == 0); |
| { // Void and function pointers (C-6,7) |
| my_free_called = false; |
| { |
| int i = 0; |
| std::unique_ptr<void, void (*)(void*)> s(&i, my_free); |
| assert(s.get() == &i); |
| assert(s.get_deleter() == my_free); |
| assert(!my_free_called); |
| } |
| assert(my_free_called); |
| } |
| } |
| |
| template <bool IsArray> |
| void test_nullptr() { |
| #if TEST_STD_VER >= 11 |
| typedef typename std::conditional<!IsArray, A, A[]>::type VT; |
| { |
| std::unique_ptr<VT, Deleter<VT> > u(nullptr, Deleter<VT>{}); |
| assert(u.get() == nullptr); |
| } |
| { |
| NCDeleter<VT> d; |
| std::unique_ptr<VT, NCDeleter<VT>& > u(nullptr, d); |
| assert(u.get() == nullptr); |
| } |
| { |
| NCConstDeleter<VT> d; |
| std::unique_ptr<VT, NCConstDeleter<VT> const& > u(nullptr, d); |
| assert(u.get() == nullptr); |
| } |
| #endif |
| } |
| |
| int main(int, char**) { |
| { |
| test_basic</*IsArray*/ false>(); |
| test_nullptr<false>(); |
| test_basic_single(); |
| test_sfinae<false>(); |
| test_noexcept<false>(); |
| } |
| { |
| test_basic</*IsArray*/ true>(); |
| test_nullptr<true>(); |
| test_sfinae<true>(); |
| test_sfinae_runtime(); |
| test_noexcept<true>(); |
| } |
| |
| return 0; |
| } |