diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -39,6 +39,7 @@ ------------------ - P2499R0 - ``string_view`` range constructor should be ``explicit`` - P2417R2 - A more constexpr bitset +- P0591R4 - Utility functions to implement uses-allocator construction Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -55,8 +55,8 @@ "`P0357R3 `__","LWG","reference_wrapper for incomplete types","San Diego","|Complete|","8.0" "`P0482R6 `__","CWG","char8_t: A type for UTF-8 characters and strings","San Diego","|In Progress|","" "`P0487R1 `__","LWG","Fixing ``operator>>(basic_istream&, CharT*)``\ (LWG 2499)","San Diego","|Complete|","8.0" -"`P0591R4 `__","LWG","Utility functions to implement uses-allocator construction","San Diego","* *","" -"`P0595R2 `__","CWG","P0595R2 std::is_constant_evaluated()","San Diego","|Complete|","9.0" +"`P0591R4 `__","LWG","Utility functions to implement uses-allocator construction","San Diego","|Complete|","16.0" +"`P0595R2 `__","CWG","std::is_constant_evaluated()","San Diego","|Complete|","9.0" "`P0602R4 `__","LWG","variant and optional should propagate copy/move triviality","San Diego","|Complete|","8.0" "`P0608R3 `__","LWG","A sane variant converting constructor","San Diego","|Complete|","9.0" "`P0655R1 `__","LWG","visit: Explicit Return Type for visit","San Diego","|Complete|","12.0" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -399,6 +399,7 @@ __memory/uninitialized_algorithms.h __memory/unique_ptr.h __memory/uses_allocator.h + __memory/uses_allocator_construction.h __memory/voidify.h __mutex_base __node_handle diff --git a/libcxx/include/__memory/uses_allocator_construction.h b/libcxx/include/__memory/uses_allocator_construction.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__memory/uses_allocator_construction.h @@ -0,0 +1,210 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H +#define _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H + +#include <__config> +#include <__memory/construct_at.h> +#include <__memory/uses_allocator.h> +#include <__type_traits/enable_if.h> +#include <__type_traits/is_same.h> +#include <__utility/pair.h> +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 14 + +template +inline constexpr bool __is_std_pair = false; + +template +inline constexpr bool __is_std_pair> = true; + +template < + class _Type, + class _Alloc, + class... _Args, + class = __enable_if_t< + !__is_std_pair<_Type> && + ((!uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args...>) || + (uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, allocator_arg_t, const _Alloc&, _Args...>) || + (uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args..., const _Alloc&>))>> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept { + if constexpr (!uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args...>) { + return std::forward_as_tuple(std::forward<_Args>(__args)...); + } else if constexpr (uses_allocator_v<_Type, _Alloc> && + is_constructible_v<_Type, allocator_arg_t, const _Alloc&, _Args...>) { + return tuple(allocator_arg, __alloc, std::forward<_Args>(__args)...); + } else if constexpr (uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args..., const _Alloc&>) { + return std::forward_as_tuple(std::forward<_Args>(__args)..., __alloc); + } +} + +template >> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, piecewise_construct_t, _Tuple1&& __x, _Tuple2&& __y) { + return std::make_tuple( + piecewise_construct, + std::apply( + [&__alloc](auto&&... __args1) { + return std::__uses_allocator_construction_args( + __alloc, std::forward(__args1)...); + }, + std::forward<_Tuple1>(__x)), + std::apply( + [&__alloc](auto&&... __args2) { + return std::__uses_allocator_construction_args( + __alloc, std::forward(__args2)...); + }, + std::forward<_Tuple2>(__y))); +} + +template >> +_LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc) noexcept { + return std::__uses_allocator_construction_args<_Pair>(__alloc, piecewise_construct, tuple<>{}, tuple<>{}); +} + +template >> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v) noexcept { + return std::__uses_allocator_construction_args<_Pair>( + __alloc, + piecewise_construct, + std::forward_as_tuple(std::forward<_Up>(__u)), + std::forward_as_tuple(std::forward<_Vp>(__v))); +} + +template >> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>& __pair) noexcept { + return std::__uses_allocator_construction_args<_Pair>( + __alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second)); +} + +template >> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>& __pair) noexcept { + return std::__uses_allocator_construction_args<_Pair>( + __alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second)); +} + +template >> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pair) noexcept { + return std::__uses_allocator_construction_args<_Pair>( + __alloc, + piecewise_construct, + std::forward_as_tuple(std::get<0>(std::move(__pair))), + std::forward_as_tuple(std::get<1>(std::move(__pair)))); +} + +template >> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>&& __pair) noexcept { + return std::__uses_allocator_construction_args<_Pair>( + __alloc, + piecewise_construct, + std::forward_as_tuple(std::get<0>(std::move(__pair))), + std::forward_as_tuple(std::get<1>(std::move(__pair)))); +} + +namespace __detail { + +template +void __fun(const pair<_Ap, _Bp>&); + +template +decltype(__detail::__fun(std::declval<_Tp>()), true_type()) __convertible_to_const_pair_ref_impl(int); + +template +false_type __convertible_to_const_pair_ref_impl(...); + +template +constexpr bool __convertible_to_const_pair_ref = + decltype(__detail::__convertible_to_const_pair_ref_impl<_Tp>(0))::value; + +} // namespace __detail + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args) + -> decltype(std::make_from_tuple<_Type>( + std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...))) { + return std::make_from_tuple<_Type>( + std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...)); +} + +template && !__detail::__convertible_to_const_pair_ref<_Type>>> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept { + struct __pair_constructor { + using _PairMut = remove_cv_t<_Pair>; + + _LIBCPP_HIDE_FROM_ABI constexpr auto __do_construct(const _PairMut& __pair) const { + return std::__make_obj_using_allocator<_PairMut>(__alloc_, __pair); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto __do_construct(_PairMut&& __pair) const { + return std::__make_obj_using_allocator<_PairMut>(__alloc_, std::move(__pair)); + } + + const _Alloc& __alloc_; + _Type& __value_; + + _LIBCPP_HIDE_FROM_ABI constexpr operator _PairMut() const { return __do_construct(std::forward<_Type>(__value_)); } + }; + + return std::make_tuple(__pair_constructor(__alloc, __value)); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uninitialized_construct_using_allocator(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args) + -> decltype(std::apply( + [](auto&&... __xs) { + return std::__construct_at(std::declval<_Type*>(), std::forward(__xs)...); + }, + std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...))) { + return std::apply( + [&__ptr](auto&&... __xs) { return std::__construct_at(__ptr, std::forward(__xs)...); }, + std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...)); +} + +#endif // _LIBCPP_STD_VER > 14 + +#if _LIBCPP_STD_VER > 17 + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto uses_allocator_construction_args(const _Alloc& __alloc, _Args... __args) noexcept + -> decltype(std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...)) { + return /*--*/ std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args) + -> decltype(std::__make_obj_using_allocator<_Type>(__alloc, std::forward<_Args>(__args)...)) { + return /*--*/ std::__make_obj_using_allocator<_Type>(__alloc, std::forward<_Args>(__args)...); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto +uninitialized_construct_using_allocator(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args) + -> decltype(std::__uninitialized_construct_using_allocator(__ptr, __alloc, __args...)) { + return /*--*/ std::__uninitialized_construct_using_allocator(__ptr, __alloc, __args...); +} + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -876,6 +876,7 @@ #include <__memory/uninitialized_algorithms.h> #include <__memory/unique_ptr.h> #include <__memory/uses_allocator.h> +#include <__memory/uses_allocator_construction.h> #include #include #include diff --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/make_obj_using_allocator.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/make_obj_using_allocator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/make_obj_using_allocator.pass.cpp @@ -0,0 +1,149 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// template +// constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args); + +#include +#include +#include +#include + +#include "test_allocator.h" + +using Alloc = test_allocator; + +struct UsesAllocArgT { + using allocator_type = Alloc; + + bool allocator_constructed_ = false; + const Alloc& alloc_ = Alloc(); + + constexpr UsesAllocArgT() = default; + constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc) : allocator_constructed_(true), alloc_(alloc) {} + constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc, int) + : allocator_constructed_(true), alloc_(alloc) {} + constexpr UsesAllocArgT(std::allocator_arg_t, int, const Alloc& alloc) + : allocator_constructed_(true), alloc_(alloc) {} +}; + +struct UsesAllocLast { + using allocator_type = Alloc; + + bool allocator_constructed_ = false; + const Alloc& alloc_ = Alloc(); + + constexpr UsesAllocLast() = default; + constexpr UsesAllocLast(const Alloc& alloc) : allocator_constructed_(true), alloc_(alloc) {} + constexpr UsesAllocLast(int, const Alloc& alloc) : allocator_constructed_(true), alloc_(alloc) {} + constexpr UsesAllocLast(const Alloc& alloc, int) : allocator_constructed_(true), alloc_(alloc) {} +}; + +struct NotAllocatorAware { + bool allocator_constructed_ = false; + + constexpr NotAllocatorAware() = default; + constexpr NotAllocatorAware(const Alloc&) : allocator_constructed_(true) {} + constexpr NotAllocatorAware(const Alloc&, int) : allocator_constructed_(true) {} +}; + +template +concept HasMakeObjUsingAllocator = requires(Args... args) { std::make_obj_using_allocator(args...); }; + +static_assert(HasMakeObjUsingAllocator); +static_assert(!HasMakeObjUsingAllocator); +static_assert(!HasMakeObjUsingAllocator); + +constexpr bool test() { + Alloc a(12); + { + std::same_as auto ret = std::make_obj_using_allocator(a); + assert(ret.allocator_constructed_); + assert(&ret.alloc_ == &a); + } + { + std::same_as auto ret = std::make_obj_using_allocator(a); + assert(ret.allocator_constructed_); + assert(&ret.alloc_ == &a); + } + { + std::same_as auto ret = std::make_obj_using_allocator(a); + assert(!ret.allocator_constructed_); + } + { + std::same_as> auto ret = + std::make_obj_using_allocator>( + a, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + assert(ret.first.allocator_constructed_); + assert(&ret.first.alloc_ == &a); + assert(ret.second.allocator_constructed_); + assert(&ret.second.alloc_ == &a); + } + { + std::same_as> auto ret = + std::make_obj_using_allocator>(a); + assert(ret.first.allocator_constructed_); + assert(&ret.first.alloc_ == &a); + assert(ret.second.allocator_constructed_); + assert(&ret.second.alloc_ == &a); + } + { + std::same_as> auto ret = + std::make_obj_using_allocator>(a, 0, 0); + assert(ret.first.allocator_constructed_); + assert(&ret.first.alloc_ == &a); + assert(ret.second.allocator_constructed_); + assert(&ret.second.alloc_ == &a); + } + { + std::pair p{0, 0}; + + std::same_as> auto ret = + std::make_obj_using_allocator>(a, p); + assert(ret.first.allocator_constructed_); + assert(&ret.first.alloc_ == &a); + assert(ret.second.allocator_constructed_); + assert(&ret.second.alloc_ == &a); + } + { + std::pair p{0, 0}; + std::same_as> auto ret = + std::make_obj_using_allocator>(a, std::as_const(p)); + assert(ret.first.allocator_constructed_); + assert(&ret.first.alloc_ == &a); + assert(ret.second.allocator_constructed_); + assert(&ret.second.alloc_ == &a); + } + { + std::pair p{0, 0}; + std::same_as> auto ret = + std::make_obj_using_allocator>(a, std::move(p)); + assert(ret.first.allocator_constructed_); + assert(&ret.first.alloc_ == &a); + assert(ret.second.allocator_constructed_); + assert(&ret.second.alloc_ == &a); + } + { + std::pair p{0, 0}; + std::same_as> auto ret = + std::make_obj_using_allocator>(a, std::move(std::as_const(p))); + assert(ret.first.allocator_constructed_); + assert(&ret.first.alloc_ == &a); + assert(ret.second.allocator_constructed_); + assert(&ret.second.alloc_ == &a); + } + + // TODO: test make_obj_using_allocator(Alloc, NotConvertibleToPair) + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +} diff --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/tested_elsewhere.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/tested_elsewhere.pass.cpp deleted file mode 100644 --- a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/tested_elsewhere.pass.cpp +++ /dev/null @@ -1,13 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -int main(int, char**) -{ - - return 0; -} diff --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uninitialized_construct_using_allocator.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uninitialized_construct_using_allocator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uninitialized_construct_using_allocator.pass.cpp @@ -0,0 +1,170 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// template +// constexpr T uninitialized_construct_using_allocator(const Alloc& alloc, Args&&... args); + +#include +#include +#include +#include + +#include "test_allocator.h" + +using Alloc = test_allocator; + +struct UsesAllocArgT { + using allocator_type = Alloc; + + bool allocator_constructed_ = false; + const Alloc& alloc_ = Alloc(); + + constexpr UsesAllocArgT() = default; + constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc) : allocator_constructed_(true), alloc_(alloc) {} + constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc, int) + : allocator_constructed_(true), alloc_(alloc) {} + constexpr UsesAllocArgT(std::allocator_arg_t, int, const Alloc& alloc) + : allocator_constructed_(true), alloc_(alloc) {} +}; + +struct UsesAllocLast { + using allocator_type = Alloc; + + bool allocator_constructed_ = false; + const Alloc& alloc_ = Alloc(); + + constexpr UsesAllocLast() = default; + constexpr UsesAllocLast(const Alloc& alloc) : allocator_constructed_(true), alloc_(alloc) {} + constexpr UsesAllocLast(int, const Alloc& alloc) : allocator_constructed_(true), alloc_(alloc) {} + constexpr UsesAllocLast(const Alloc& alloc, int) : allocator_constructed_(true), alloc_(alloc) {} +}; + +struct NotAllocatorAware { + bool allocator_constructed_ = false; + + constexpr NotAllocatorAware() = default; + constexpr NotAllocatorAware(const Alloc&) : allocator_constructed_(true) {} + constexpr NotAllocatorAware(const Alloc&, int) : allocator_constructed_(true) {} +}; + +template +concept HasMakeObjUsingAllocator = + requires(T* ptr, Args... args) { std::uninitialized_construct_using_allocator(ptr, args...); }; + +static_assert(HasMakeObjUsingAllocator); +static_assert(!HasMakeObjUsingAllocator); +static_assert(!HasMakeObjUsingAllocator); + +constexpr bool test() { + Alloc a(12); + { + auto* ptr = std::allocator{}.allocate(1); + std::same_as auto ret = std::uninitialized_construct_using_allocator(ptr, a); + assert(ret == ptr); + assert(ret->allocator_constructed_); + assert(&ret->alloc_ == &a); + std::allocator{}.deallocate(ptr, 1); + } + { + auto* ptr = std::allocator{}.allocate(1); + std::same_as auto ret = std::uninitialized_construct_using_allocator(ptr, a); + assert(ret->allocator_constructed_); + assert(&ret->alloc_ == &a); + std::allocator{}.deallocate(ptr, 1); + } + { + auto* ptr = std::allocator{}.allocate(1); + std::same_as auto ret = std::uninitialized_construct_using_allocator(ptr, a); + assert(!ret->allocator_constructed_); + std::allocator{}.deallocate(ptr, 1); + } + { + auto* ptr = std::allocator>{}.allocate(1); + std::same_as*> auto ret = + std::uninitialized_construct_using_allocator(ptr, a, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + assert(ret->first.allocator_constructed_); + assert(&ret->first.alloc_ == &a); + assert(ret->second.allocator_constructed_); + assert(&ret->second.alloc_ == &a); + std::allocator>{}.deallocate(ptr, 1); + } + { + auto* ptr = std::allocator>{}.allocate(1); + std::same_as*> auto ret = + std::uninitialized_construct_using_allocator(ptr, a); + assert(ret->first.allocator_constructed_); + assert(&ret->first.alloc_ == &a); + assert(ret->second.allocator_constructed_); + assert(&ret->second.alloc_ == &a); + std::allocator>{}.deallocate(ptr, 1); + } + { + auto* ptr = std::allocator>{}.allocate(1); + std::same_as*> auto ret = + std::uninitialized_construct_using_allocator(ptr, a, 0, 0); + assert(ret->first.allocator_constructed_); + assert(&ret->first.alloc_ == &a); + assert(ret->second.allocator_constructed_); + assert(&ret->second.alloc_ == &a); + std::allocator>{}.deallocate(ptr, 1); + } + { + std::pair p{0, 0}; + + auto* ptr = std::allocator>{}.allocate(1); + std::same_as*> auto ret = + std::uninitialized_construct_using_allocator(ptr, a, p); + assert(ret->first.allocator_constructed_); + assert(&ret->first.alloc_ == &a); + assert(ret->second.allocator_constructed_); + assert(&ret->second.alloc_ == &a); + std::allocator>{}.deallocate(ptr, 1); + } + { + std::pair p{0, 0}; + auto* ptr = std::allocator>{}.allocate(1); + std::same_as*> auto ret = + std::uninitialized_construct_using_allocator(ptr, a, std::as_const(p)); + assert(ret->first.allocator_constructed_); + assert(&ret->first.alloc_ == &a); + assert(ret->second.allocator_constructed_); + assert(&ret->second.alloc_ == &a); + std::allocator>{}.deallocate(ptr, 1); + } + { + std::pair p{0, 0}; + auto* ptr = std::allocator>{}.allocate(1); + std::same_as*> auto ret = + std::uninitialized_construct_using_allocator(ptr, a, std::move(p)); + assert(ret->first.allocator_constructed_); + assert(&ret->first.alloc_ == &a); + assert(ret->second.allocator_constructed_); + assert(&ret->second.alloc_ == &a); + std::allocator>{}.deallocate(ptr, 1); + } + { + std::pair p{0, 0}; + auto* ptr = std::allocator>{}.allocate(1); + std::same_as*> auto ret = + std::uninitialized_construct_using_allocator(ptr, a, std::move(std::as_const(p))); + assert(ret->first.allocator_constructed_); + assert(&ret->first.alloc_ == &a); + assert(ret->second.allocator_constructed_); + assert(&ret->second.alloc_ == &a); + std::allocator>{}.deallocate(ptr, 1); + } + + // TODO: test uninitialized_construct_using_allocator(Alloc, NotConvertibleToPair) + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +} diff --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp @@ -0,0 +1,145 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// template +// constexpr auto uses_allocator_construction_args(const Alloc& alloc, ...) noexcept; + +#include +#include +#include +#include + +#include "test_allocator.h" + +using Alloc = test_allocator; + +struct UsesAllocArgT { + using allocator_type = Alloc; + + bool allocator_constructed_ = false; + + UsesAllocArgT() = default; + UsesAllocArgT(std::allocator_arg_t, const Alloc&) : allocator_constructed_(true) {} + UsesAllocArgT(std::allocator_arg_t, const Alloc&, int) : allocator_constructed_(true) {} + UsesAllocArgT(std::allocator_arg_t, int, const Alloc&) : allocator_constructed_(true) {} +}; + +struct UsesAllocLast { + using allocator_type = Alloc; + + bool allocator_constructed_ = false; + + UsesAllocLast() = default; + UsesAllocLast(const Alloc&) : allocator_constructed_(true) {} + UsesAllocLast(int, const Alloc&) : allocator_constructed_(true) {} + UsesAllocLast(const Alloc&, int) : allocator_constructed_(true) {} +}; + +struct NotAllocatorAware { + bool allocator_constructed_ = false; + + NotAllocatorAware() = default; + NotAllocatorAware(const Alloc&) : allocator_constructed_(true) {} + NotAllocatorAware(const Alloc&, int) : allocator_constructed_(true) {} +}; + +template +concept HasUsesAllocatorConstructionArgs = + requires(Args... args) { std::uses_allocator_construction_args(args...); }; + +static_assert(HasUsesAllocatorConstructionArgs); +static_assert(!HasUsesAllocatorConstructionArgs); +static_assert(!HasUsesAllocatorConstructionArgs); + +constexpr bool test() { + Alloc a(12); + { + [[maybe_unused]] std::same_as> auto ret = + std::uses_allocator_construction_args(a); + assert(std::get<1>(ret).get_data() == 12); + } + { + [[maybe_unused]] std::same_as> auto ret = + std::uses_allocator_construction_args(a); + assert(std::get<0>(ret).get_data() == 12); + } + { + [[maybe_unused]] std::same_as> auto ret = std::uses_allocator_construction_args(a); + } + { + [[maybe_unused]] std::same_as, + std::tuple>> auto ret = + std::uses_allocator_construction_args>( + a, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + assert(std::get<1>(std::get<1>(ret)).get_data() == 12); + assert(std::get<0>(std::get<2>(ret)).get_data() == 12); + } + { + [[maybe_unused]] std::same_as, + std::tuple>> auto ret = + std::uses_allocator_construction_args>(a); + assert(std::get<1>(std::get<1>(ret)).get_data() == 12); + assert(std::get<0>(std::get<2>(ret)).get_data() == 12); + } + { + [[maybe_unused]] std::same_as, + std::tuple>> auto ret = + std::uses_allocator_construction_args>(a, 0, 0); + assert(std::get<1>(std::get<1>(ret)).get_data() == 12); + assert(std::get<1>(std::get<2>(ret)).get_data() == 12); + } + { + std::pair p{0, 0}; + + [[maybe_unused]] std::same_as, + std::tuple>> auto ret = + std::uses_allocator_construction_args>(a, p); + assert(std::get<1>(std::get<1>(ret)).get_data() == 12); + assert(std::get<1>(std::get<2>(ret)).get_data() == 12); + } + { + std::pair p{0, 0}; + [[maybe_unused]] std::same_as, + std::tuple>> auto ret = + std::uses_allocator_construction_args>(a, std::as_const(p)); + assert(std::get<1>(std::get<1>(ret)).get_data() == 12); + assert(std::get<1>(std::get<2>(ret)).get_data() == 12); + } + { + std::pair p{0, 0}; + [[maybe_unused]] std::same_as, + std::tuple>> auto ret = + std::uses_allocator_construction_args>(a, std::move(p)); + assert(std::get<1>(std::get<1>(ret)).get_data() == 12); + assert(std::get<1>(std::get<2>(ret)).get_data() == 12); + } + { + std::pair p{0, 0}; + [[maybe_unused]] std::same_as, + std::tuple>> auto ret = + std::uses_allocator_construction_args>(a, std::move(std::as_const(p))); + assert(std::get<1>(std::get<1>(ret)).get_data() == 12); + assert(std::get<1>(std::get<2>(ret)).get_data() == 12); + } + + // TODO: test uses_allocator_construction_args(Alloc, NotConvertibleToPair) + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +}