Index: include/memory =================================================================== --- include/memory +++ include/memory @@ -626,6 +626,24 @@ atomic_compare_exchange_strong_explicit(shared_ptr* p, shared_ptr* v, shared_ptr w, memory_order success, memory_order failure); + +template + auto uses_allocator_construction_args(const Alloc& alloc, Args&&... args); +template + auto uses_allocator_construction_args(const Alloc& alloc, piecewise_construct_t, Tuple1&& x, Tuple2&& y); +template + auto uses_allocator_construction_args(const Alloc& alloc); +template + auto uses_allocator_construction_args(const Alloc& alloc, U&& u, V&& v); +template + auto uses_allocator_construction_args(const Alloc& alloc, const pair& pr); +template + auto uses_allocator_construction_args(const Alloc& alloc, pair&& pr); +template + T make_obj_using_allocator(const Alloc& alloc, Args&&... args); +template + T* uninitialized_construct_using_allocator(T* p, const Alloc& alloc, Args&&... args); + // Hash support template struct hash; template struct hash >; @@ -5668,6 +5686,149 @@ > : true_type {}; +#if _LIBCPP_STD_VER > 17 + +template struct __is_pair : false_type { }; + +template +struct __is_pair> : true_type { }; + +template +struct __pair_uses_allocator : _VSTD::uses_allocator<_Tp, _Alloc> {}; + +template +struct __pair_uses_allocator<_VSTD::pair<_T1, _T2>, _Alloc> + : _VSTD::integral_constant::value || + __pair_uses_allocator<_T2, _Alloc>::value> {}; + +template +auto uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args); // this needs to be forward declared for some __uses_allocator_construction_args_impl overloads + +template +auto +__uses_allocator_construction_args_impl(_Un1, // is pair + false_type, // uses allocator + _Un2, // is constructible + const _Alloc&, _Args&&... __args) +{ + return _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)...); +} + +template +auto +__uses_allocator_construction_args_impl(false_type, // is pair + true_type, // uses allocator + true_type, // is constructible + const _Alloc& __alloc, _Args&&... __args) +{ + return _VSTD::forward_as_tuple(allocator_arg, __alloc, _VSTD::forward<_Args>(__args)...); +} + +template +auto + __uses_allocator_construction_args_impl(false_type, // is pair + true_type, // uses allocator + false_type, // is constructible + const _Alloc& __alloc, _Args&&... __args) +{ + return _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)..., __alloc); +} + +template +auto +__uses_allocator_construction_args_impl(true_type, // is pair + true_type, // uses allocator + false_type, // is constructible + const _Alloc& __alloc, + piecewise_construct_t, + _T1&& __a, _T2&& __b) +{ + using _First = typename _Tp::first_type; + using _Second = typename _Tp::second_type; + + return _VSTD::make_tuple(_VSTD::piecewise_construct, + _VSTD::apply([&__alloc](auto&&... __args_a) -> auto { + return uses_allocator_construction_args<_First>(__alloc, + _VSTD::forward(__args_a)...); + }, _VSTD::forward<_T1>(__a)), + _VSTD::apply([&__alloc](auto&&... __args_b) -> auto { + return uses_allocator_construction_args<_Second>(__alloc, + _VSTD::forward(__args_b)...); + }, _VSTD::forward<_T2>(__b))); +} + +template +auto +__uses_allocator_construction_args_impl(true_type, // is pair + true_type, // uses allocator + false_type, // is constructible + const _Alloc& __alloc) +{ + return uses_allocator_construction_args<_Tp>(__alloc, + piecewise_construct, + _VSTD::tuple<>{}, + _VSTD::tuple<>{}); +} + +template +auto +__uses_allocator_construction_args_impl(true_type, // is pair + true_type, // uses allocator + false_type, // is constructible + const _Alloc& __alloc, + const _VSTD::pair<_T1, _T2>& __p) +{ + return uses_allocator_construction_args<_Tp>(__alloc, + piecewise_construct, + _VSTD::forward_as_tuple(__p.first), + _VSTD::forward_as_tuple(__p.second)); +} + +template +auto +__uses_allocator_construction_args_impl(true_type, // is pair + true_type, // uses allocator + false_type, // is constructible + const _Alloc& __alloc, + _VSTD::pair<_T1, _T2>&& __p) +{ + return uses_allocator_construction_args<_Tp>(__alloc, + piecewise_construct, + _VSTD::forward_as_tuple(_VSTD::forward<_T1>(__p.first)), + _VSTD::forward_as_tuple(_VSTD::forward<_T2>(__p.second))); +} + +template +auto +uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) +{ + return __uses_allocator_construction_args_impl<_Tp>( + __is_pair<_Tp>{}, // TODO: vstd::is_pair + __pair_uses_allocator<_Tp, _Alloc>{}, + _VSTD::is_constructible<_Tp, allocator_arg_t, _Alloc, _Args...>{}, + __alloc, + _VSTD::forward<_Args>(__args)...); +} + +template +_Tp +make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args) +{ + return make_from_tuple<_Tp>(uses_allocator_construction_args<_Tp>(__alloc, + _VSTD::forward<_Args>(__args)...)); +} + +template +_Tp* +uninitialized_construct_using_allocator(_Tp* __p, const _Alloc& __alloc, _Args&&... __args) +{ + return ::new(static_cast(__p)) _Tp(make_obj_using_allocator<_Tp>(__alloc, + _VSTD::forward<_Args>(__args)...)); +} + +#endif // _LIBCPP_STD_VER > 17 + _LIBCPP_END_NAMESPACE_STD _LIBCPP_POP_MACROS Index: include/tuple =================================================================== --- include/tuple +++ include/tuple @@ -1374,7 +1374,7 @@ inline _LIBCPP_INLINE_VISIBILITY constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, __tuple_indices<_Idx...>) _LIBCPP_NOEXCEPT_RETURN( - _Tp(_VSTD::get<_Idx>(_VSTD::forward<_Tuple>(__t))...) + _Tp(_VSTD::move(_VSTD::get<_Idx>(_VSTD::forward<_Tuple>(__t)))...) ) template Index: test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp =================================================================== --- /dev/null +++ test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp @@ -0,0 +1,306 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +#include +#include +#include +#include + +#include "test_macros.h" + +struct A +{ + int a; + float b; + + A(int a, float b) : a(a), b(b) { } +}; + +struct B : A +{ + using allocator_type = std::allocator; + B(std::allocator_arg_t, std::allocator, int a, int b) : A(a, b) { } +}; + +struct C : A +{ + using allocator_type = std::allocator; + C(int a, int b, std::allocator) : A(a, b) { } +}; + +struct D +{ + using allocator_type = std::allocator; +}; + +struct E +{ + using allocator_type = std::allocator; + + int i; + + E(std::allocator_arg_t, std::allocator, int i) : i(i) { } +}; + +template +T test_uses_alloc(Args&&... args) +{ + auto args_tuple = std::uses_allocator_construction_args(std::forward(args)...); + const auto arg_count = std::tuple_size::value; + static_assert(arg_count == sizeof...(Args) - 1); + + return std::make_from_tuple(args_tuple); +} + +void general_tests() +{ + using pair_t = std::pair; + + // non pair contruction + std::allocator alloc; + auto a = std::make_obj_using_allocator(alloc, 10, 5.0); + + ASSERT_SAME_TYPE(decltype(a), A); + assert(a.a == 10); + assert(a.b == 5.0); + + auto* b = std::uninitialized_construct_using_allocator(&a, alloc, 20, 10.0); + + assert(a.a == 20); + assert(a.b == 10.0); + + ASSERT_SAME_TYPE(decltype(b), A*); + assert(b->a == 20); + assert(b->b == 10.0); + + assert(&a == b); + + // pair construction + std::allocator p_alloc; + pair_t p { 10, 5 }; + + test_uses_alloc(p_alloc); + auto res = test_uses_alloc(p_alloc, 10, 5); + auto res1 = test_uses_alloc(p_alloc, p); + assert(res.first == res1.first && + res.second == res1.second && + res.first == 10 && + res.second == 5); + + auto res2 = test_uses_alloc(std::allocator(), 10, 10.0); + assert(res2.a == res2.b && res2.a == 10); +} + +void test_no_allocator() +{ + auto args_tuple = std::uses_allocator_construction_args(std::allocator{}, 10, 5.0); + const auto arg_count = std::tuple_size::value; + static_assert(arg_count == 2); + + assert(std::get<0>(args_tuple) == 10); + assert(std::get<1>(args_tuple) == 5.0); + + auto a = std::make_from_tuple(args_tuple); + assert(a.a == 10); + assert(a.b == 5.0); +} + +void test_no_pair() +{ + { // make sure we are calling the correct overload + static_assert(!std::uses_allocator>::value); + static_assert(!std::is_constructible, int, int>::value); + static_assert( std::is_constructible::value); + static_assert( std::uses_allocator>::value); + static_assert( std::is_constructible, int, int>::value); + static_assert( std::uses_allocator>::value); + static_assert( std::is_constructible>::value); // TODO: should this be checked? + } + { + auto args_tuple = std::uses_allocator_construction_args(std::allocator{}, 10, 5.0); + const auto arg_count = std::tuple_size::value; + static_assert(arg_count == 4); + + assert(std::get<2>(args_tuple) == 10); + assert(std::get<3>(args_tuple) == 5.0); + + auto b = std::make_from_tuple(args_tuple); + assert(b.a == 10); + assert(b.b == 5.0); + } + { + auto args_tuple = std::uses_allocator_construction_args(std::allocator{}, 10, 5.0); + const auto arg_count = std::tuple_size::value; + static_assert(arg_count == 3); + + assert(std::get<0>(args_tuple) == 10); + assert(std::get<1>(args_tuple) == 5.0); + + auto c = std::make_from_tuple(args_tuple); + assert(c.a == 10); + assert(c.b == 5.0); + } +} + +void test_pair() +{ + using pair_t = std::pair; + + { + auto args_tuple = std::uses_allocator_construction_args(std::allocator{}, + std::piecewise_construct, + std::make_tuple(10, 5), + std::make_tuple(4, 2)); + const auto arg_count = std::tuple_size::value; + const auto arg_count1 = std::tuple_size(args_tuple))>>::value; + const auto arg_count2 = std::tuple_size(args_tuple))>>::value; + static_assert(arg_count == 3); + static_assert(arg_count1 == 4); + static_assert(arg_count2 == 4); + + // ASSERT_SAME_TYPE(decltype(std::get<0>(args_tuple)), decltype(std::piecewise_construct)); + // ASSERT_SAME_TYPE(decltype(std::get<0>(std::get<1>(args_tuple))), std::allocator_arg_t); + // ASSERT_SAME_TYPE(decltype(std::get<1>(std::get<1>(args_tuple))), std::allocator); + // ASSERT_SAME_TYPE(decltype(std::get<0>(std::get<2>(args_tuple))), std::allocator_arg_t); + // ASSERT_SAME_TYPE(decltype(std::get<1>(std::get<2>(args_tuple))), std::allocator); + + assert(std::get<2>(std::get<1>(args_tuple)) == 10); + assert(std::get<3>(std::get<1>(args_tuple)) == 5); + assert(std::get<2>(std::get<2>(args_tuple)) == 4); + assert(std::get<3>(std::get<2>(args_tuple)) == 2); + + auto p = std::make_from_tuple(args_tuple); + assert(p.first.a == 10); + assert(p.first.b == 5); + assert(p.second.a == 4); + assert(p.second.b == 2); + } + { + auto args_tuple = std::uses_allocator_construction_args(std::allocator{}); + const auto arg_count = std::tuple_size::value; + const auto arg_count1 = std::tuple_size(args_tuple))>>::value; + const auto arg_count2 = std::tuple_size(args_tuple))>>::value; + static_assert(arg_count == 3); + static_assert(arg_count1 == 1); + static_assert(arg_count2 == 1); + + // ASSERT_SAME_TYPE(decltype(std::get<0>(args_tuple)), decltype(std::piecewise_construct)); + // ASSERT_SAME_TYPE(decltype(std::get<0>(std::get<1>(args_tuple))), std::allocator_arg_t); + // ASSERT_SAME_TYPE(decltype(std::get<1>(std::get<1>(args_tuple))), std::allocator); + // ASSERT_SAME_TYPE(decltype(std::get<0>(std::get<2>(args_tuple))), std::allocator_arg_t); + // ASSERT_SAME_TYPE(decltype(std::get<1>(std::get<2>(args_tuple))), std::allocator); + } + { + using pair_e = std::pair; + auto args_pair = std::make_pair(10, 5); + auto args_tuple = std::uses_allocator_construction_args(std::allocator{}, + args_pair); + const auto arg_count = std::tuple_size::value; + const auto arg_count1 = std::tuple_size(args_tuple))>>::value; + const auto arg_count2 = std::tuple_size(args_tuple))>>::value; + static_assert(arg_count == 3); + static_assert(arg_count1 == 3); + static_assert(arg_count2 == 3); + + // ASSERT_SAME_TYPE(decltype(std::get<0>(args_tuple)), decltype(std::piecewise_construct)); + // ASSERT_SAME_TYPE(decltype(std::get<0>(std::get<1>(args_tuple))), std::allocator_arg_t); + // ASSERT_SAME_TYPE(decltype(std::get<1>(std::get<1>(args_tuple))), std::allocator); + // ASSERT_SAME_TYPE(decltype(std::get<0>(std::get<2>(args_tuple))), std::allocator_arg_t); + // ASSERT_SAME_TYPE(decltype(std::get<1>(std::get<2>(args_tuple))), std::allocator); + + assert(std::get<2>(std::get<1>(args_tuple)) == 10); + assert(std::get<2>(std::get<2>(args_tuple)) == 5); + + auto p = std::make_from_tuple(args_tuple); + assert(p.first.i == 10); + assert(p.second.i == 5); + } + { + using pair_e = std::pair; + auto args_pair = std::make_pair(10, 5); + auto args_tuple = std::uses_allocator_construction_args(std::allocator{}, + std::move(args_pair)); + const auto arg_count = std::tuple_size::value; + const auto arg_count1 = std::tuple_size(args_tuple))>>::value; + const auto arg_count2 = std::tuple_size(args_tuple))>>::value; + static_assert(arg_count == 3); + static_assert(arg_count1 == 3); + static_assert(arg_count2 == 3); + + // ASSERT_SAME_TYPE(decltype(std::get<0>(args_tuple)), decltype(std::piecewise_construct)); + // ASSERT_SAME_TYPE(decltype(std::get<0>(std::get<1>(args_tuple))), std::allocator_arg_t); + // ASSERT_SAME_TYPE(decltype(std::get<1>(std::get<1>(args_tuple))), std::allocator); + // ASSERT_SAME_TYPE(decltype(std::get<0>(std::get<2>(args_tuple))), std::allocator_arg_t); + // ASSERT_SAME_TYPE(decltype(std::get<1>(std::get<2>(args_tuple))), std::allocator); + + assert(std::get<2>(std::get<1>(args_tuple)) == 10); + assert(std::get<2>(std::get<2>(args_tuple)) == 5); + + auto p = std::make_from_tuple(args_tuple); + assert(p.first.i == 10); + assert(p.second.i == 5); + } +} + +void test_make_obj() +{ + using pair_t = std::pair; + + { + pair_t p = std::make_obj_using_allocator(std::allocator{}, + std::piecewise_construct, + std::make_tuple(10, 5), + std::make_tuple(4, 2)); + assert(p.first.a == 10); + assert(p.first.b == 5); + assert(p.second.a == 4); + assert(p.second.b == 2); + } + { + using pair_e = std::pair; + auto args_pair = std::make_pair(10, 5); + auto p = std::make_obj_using_allocator(std::allocator{}, args_pair); + + assert(p.first.i == 10); + assert(p.second.i == 5); + } + { + using pair_e = std::pair; + auto args_pair = std::make_pair(10, 5); + auto p = std::make_obj_using_allocator(std::allocator{}, + std::move(args_pair)); + + assert(p.first.i == 10); + assert(p.second.i == 5); + } + { + auto b = std::make_obj_using_allocator(std::allocator{}, 10, 5.0); + + assert(b.a == 10); + assert(b.b == 5.0); + } + { + auto c = std::make_obj_using_allocator(std::allocator{}, 10, 5.0); + + assert(c.a == 10); + assert(c.b == 5.0); + } +} + +int main(int, char**) +{ + general_tests(); + test_no_allocator(); + test_no_pair(); + test_pair(); + test_make_obj(); + + return 0; +}