This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Re: [PATCH] C++2a Utility functions to implement uses-allocator construction (P0591R4)
- From: Jonathan Wakely <jwakely at redhat dot com>
- To: libstdc++ at gcc dot gnu dot org, gcc-patches at gcc dot gnu dot org
- Date: Fri, 1 Mar 2019 14:06:09 +0000
- Subject: Re: [PATCH] C++2a Utility functions to implement uses-allocator construction (P0591R4)
- References: <20190301135035.GA15423@redhat.com>
On 01/03/19 13:50 +0000, Jonathan Wakely wrote:
* include/std/memory (uses_allocator_construction_args): New set of
overloaded functions.
(make_obj_using_allocator, uninitialized_construct_using_allocator):
New functions.
* include/std/memory_resource (polymorphic_allocator::construct)
[__cplusplus > 201703l]: Replace all overloads with a single function
using uses_allocator_construction_args.
* testsuite/20_util/polymorphic_allocator/construct_c++2a.cc: New
test.
* testsuite/20_util/uses_allocator/make_obj.cc: New test.
If we don't care about providing the exact signatures from the C++2a
draft, we could do this and use it in C++17 as well ...
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
index 00a85eef25e..045974a1b46 100644
--- a/libstdc++-v3/include/std/memory
+++ b/libstdc++-v3/include/std/memory
@@ -168,7 +168,7 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; }
}
#endif // C++2a
-#if __cplusplus > 201703L
+#if __cplusplus >= 201703L
template<typename _Tp>
struct __is_pair : false_type { };
template<typename _Tp, typename _Up>
@@ -176,167 +176,94 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; }
template<typename _Tp, typename _Up>
struct __is_pair<const pair<_Tp, _Up>> : true_type { };
- template<typename _Tp, typename __ = _Require<__not_<__is_pair<_Tp>>>,
- typename _Alloc, typename... _Args>
+ // Equivalent of uses_allocator_construction_args for internal use in C++17
+ template<typename _Tp, typename _Alloc, typename... _Args>
constexpr auto
__uses_alloc_args(const _Alloc& __a, _Args&&... __args) noexcept
{
- if constexpr (uses_allocator_v<remove_cv_t<_Tp>, _Alloc>)
+ if constexpr (!__is_pair<_Tp>::value)
{
- if constexpr (is_constructible_v<_Tp, allocator_arg_t,
- const _Alloc&, _Args...>)
+ if constexpr (uses_allocator_v<remove_cv_t<_Tp>, _Alloc>)
{
- return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(
- allocator_arg, __a, std::forward<_Args>(__args)...);
+ if constexpr (is_constructible_v<_Tp, allocator_arg_t,
+ const _Alloc&, _Args...>)
+ {
+ return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(
+ allocator_arg, __a, std::forward<_Args>(__args)...);
+ }
+ else
+ {
+ static_assert(
+ is_constructible_v<_Tp, _Args..., const _Alloc&>);
+
+ return tuple<_Args&&..., const _Alloc&>(
+ std::forward<_Args>(__args)..., __a);
+ }
}
else
{
- static_assert(is_constructible_v<_Tp, _Args..., const _Alloc&>);
+ static_assert(is_constructible_v<_Tp, _Args...>);
- return tuple<_Args&&..., const _Alloc&>(
- std::forward<_Args>(__args)..., __a);
+ return tuple<_Args&&...>(std::forward<_Args>(__args)...);
}
}
else
{
- static_assert(is_constructible_v<_Tp, _Args...>);
-
- return tuple<_Args&&...>(std::forward<_Args>(__args)...);
- }
- }
-
-#if __cpp_concepts
- template<typename _Tp>
- concept bool _Std_pair = __is_pair<_Tp>::value;
-#endif
-
-// This is a temporary workaround until -fconcepts is implied by -std=gnu++2a
-#if __cpp_concepts
-# define _GLIBCXX_STD_PAIR_CONSTRAINT(T) _Std_pair T
-# define _GLIBCXX_STD_PAIR_CONSTRAINT_(T) _Std_pair T
-#else
-# define _GLIBCXX_STD_PAIR_CONSTRAINT(T) \
- typename T, typename __ = _Require<__is_pair<T>>
-# define _GLIBCXX_STD_PAIR_CONSTRAINT_(T) typename T, typename
-#endif
-
- template<typename _Tp,
-#if ! __cpp_concepts
- typename __ = _Require<__not_<__is_pair<_Tp>>>,
-#endif
- typename _Alloc, typename... _Args>
- constexpr auto
- uses_allocator_construction_args(const _Alloc& __a,
- _Args&&... __args) noexcept
-#if __cpp_concepts
- requires ! _Std_pair<_Tp>
-#endif
- {
- return std::__uses_alloc_args<_Tp>(__a, std::forward<_Args>(__args)...);
- }
-
- template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc,
- typename _Tuple1, typename _Tuple2>
- constexpr auto
- uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t,
- _Tuple1&& __x, _Tuple2&& __y) noexcept;
-
- template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc>
- constexpr auto
- uses_allocator_construction_args(const _Alloc&) noexcept;
-
- template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc,
- typename _Up, typename _Vp>
- constexpr auto
- uses_allocator_construction_args(const _Alloc&, _Up&&, _Vp&&) noexcept;
-
- template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc,
- typename _Up, typename _Vp>
- constexpr auto
- uses_allocator_construction_args(const _Alloc&,
- const pair<_Up, _Vp>&) noexcept;
-
- template<_GLIBCXX_STD_PAIR_CONSTRAINT(_Tp), typename _Alloc,
- typename _Up, typename _Vp>
- constexpr auto
- uses_allocator_construction_args(const _Alloc&, pair<_Up, _Vp>&&) noexcept;
+ static_assert(sizeof...(__args) <= 3);
- template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc,
- typename _Tuple1, typename _Tuple2>
- constexpr auto
- uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t,
- _Tuple1&& __x, _Tuple2&& __y) noexcept
- {
- using _Tp1 = typename _Tp::first_type;
- using _Tp2 = typename _Tp::second_type;
-
- return std::make_tuple(piecewise_construct,
- std::apply([&__a](auto&&... __args1) {
- return std::uses_allocator_construction_args<_Tp1>(
- __a, std::forward<decltype(__args1)>(__args1)...);
- }, std::forward<_Tuple1>(__x)),
- std::apply([&__a](auto&&... __args2) {
- return std::uses_allocator_construction_args<_Tp2>(
- __a, std::forward<decltype(__args2)>(__args2)...);
- }, std::forward<_Tuple2>(__y)));
- }
-
- template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc>
- constexpr auto
- uses_allocator_construction_args(const _Alloc& __a) noexcept
- {
- using _Tp1 = typename _Tp::first_type;
- using _Tp2 = typename _Tp::second_type;
-
- return std::make_tuple(piecewise_construct,
- std::uses_allocator_construction_args<_Tp1>(__a),
- std::uses_allocator_construction_args<_Tp2>(__a));
- }
+ using _Tp1 = typename _Tp::first_type;
+ using _Tp2 = typename _Tp::second_type;
- template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc,
- typename _Up, typename _Vp>
- constexpr auto
- uses_allocator_construction_args(const _Alloc& __a, _Up&& __u, _Vp&& __v)
- noexcept
- {
- using _Tp1 = typename _Tp::first_type;
- using _Tp2 = typename _Tp::second_type;
-
- return std::make_tuple(piecewise_construct,
- std::uses_allocator_construction_args<_Tp1>(__a,
- std::forward<_Up>(__u)),
- std::uses_allocator_construction_args<_Tp2>(__a,
- std::forward<_Vp>(__v)));
- }
-
- template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc,
- typename _Up, typename _Vp>
- constexpr auto
- uses_allocator_construction_args(const _Alloc& __a,
- const pair<_Up, _Vp>& __pr) noexcept
- {
- using _Tp1 = typename _Tp::first_type;
- using _Tp2 = typename _Tp::second_type;
-
- return std::make_tuple(piecewise_construct,
- std::uses_allocator_construction_args<_Tp1>(__a, __pr.first),
- std::uses_allocator_construction_args<_Tp2>(__a, __pr.second));
+ if constexpr (sizeof...(__args) == 0)
+ {
+ return std::make_tuple(piecewise_construct,
+ std::__uses_alloc_args<_Tp1>(__a),
+ std::__uses_alloc_args<_Tp2>(__a));
+ }
+ else if constexpr (sizeof...(__args) == 1)
+ {
+ return std::make_tuple(piecewise_construct,
+ std::__uses_alloc_args<_Tp1>(__a,
+ std::forward<_Args>(__args).first...),
+ std::__uses_alloc_args<_Tp2>(__a,
+ std::forward<_Args>(__args).second...));
+ }
+ else if constexpr (sizeof...(__args) == 2)
+ {
+ return [&](auto&& __arg1, auto&& __arg2)
+ {
+ return std::make_tuple(piecewise_construct,
+ std::__uses_alloc_args<_Tp1>(__a,
+ std::forward<decltype(__arg1)>(__arg1)),
+ std::__uses_alloc_args<_Tp2>(__a,
+ std::forward<decltype(__arg2)>(__arg2)));
+ }(std::forward<_Args>(__args)...);
+ }
+ else if constexpr (sizeof...(__args) == 3)
+ {
+ return [&](piecewise_construct_t, auto&& __arg1, auto&& __arg2)
+ {
+ return std::make_tuple(piecewise_construct,
+ std::apply([&](auto&&... __args1) {
+ return std::__uses_alloc_args<_Tp1>(__a,
+ std::forward<decltype(__args1)>(__args1)...);
+ }, std::forward<decltype(__arg1)>(__arg1)),
+ std::apply([&](auto&&... __args2) {
+ return std::__uses_alloc_args<_Tp2>(__a,
+ std::forward<decltype(__args2)>(__args2)...);
+ }, std::forward<decltype(__arg2)>(__arg2)));
+ }(std::forward<_Args>(__args)...);
+ }
+ }
}
- template<_GLIBCXX_STD_PAIR_CONSTRAINT_(_Tp), typename _Alloc,
- typename _Up, typename _Vp>
+#if __cplusplus > 201703L
+ template<typename _Tp, typename _Alloc, typename... _Args>
constexpr auto
- uses_allocator_construction_args(const _Alloc& __a,
- pair<_Up, _Vp>&& __pr) noexcept
+ uses_allocator_construction_args(const _Alloc& __a, _Args&&... __args)
{
- using _Tp1 = typename _Tp::first_type;
- using _Tp2 = typename _Tp::second_type;
-
- return std::make_tuple(piecewise_construct,
- std::uses_allocator_construction_args<_Tp1>(__a,
- std::move(__pr).first),
- std::uses_allocator_construction_args<_Tp2>(__a,
- std::move(__pr).second));
+ return std::__uses_alloc_args<_Tp>(__a,
+ std::forward<_Args>(__args)...);
}
template<typename _Tp, typename _Alloc, typename... _Args>
@@ -356,8 +283,8 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; }
return ::new(__vp) _Tp(std::make_obj_using_allocator<_Tp>(__a,
std::forward<_Args>(__args)...));
}
-
#endif // C++2a
+#endif // C++17
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
diff --git a/libstdc++-v3/include/std/memory_resource b/libstdc++-v3/include/std/memory_resource
index a212bccc9b1..3ea6990ecae 100644
--- a/libstdc++-v3/include/std/memory_resource
+++ b/libstdc++-v3/include/std/memory_resource
@@ -124,14 +124,6 @@ namespace pmr
template<typename _Tp>
class polymorphic_allocator
{
- // _GLIBCXX_RESOLVE_LIB_DEFECTS
- // 2975. Missing case for pair construction in polymorphic allocators
- template<typename _Up>
- struct __not_pair { using type = void; };
-
- template<typename _Up1, typename _Up2>
- struct __not_pair<pair<_Up1, _Up2>> { };
-
public:
using value_type = _Tp;
@@ -170,89 +162,15 @@ namespace pmr
__attribute__((__nonnull__))
{ _M_resource->deallocate(__p, __n * sizeof(_Tp), alignof(_Tp)); }
-#if __cplusplus <= 201703L
- template<typename _Tp1, typename... _Args>
- __attribute__((__nonnull__))
- typename __not_pair<_Tp1>::type
- construct(_Tp1* __p, _Args&&... __args)
- {
- // _GLIBCXX_RESOLVE_LIB_DEFECTS
- // 2969. polymorphic_allocator::construct() shouldn't pass resource()
- using __use_tag
- = std::__uses_alloc_t<_Tp1, polymorphic_allocator, _Args...>;
- if constexpr (is_base_of_v<__uses_alloc0, __use_tag>)
- ::new(__p) _Tp1(std::forward<_Args>(__args)...);
- else if constexpr (is_base_of_v<__uses_alloc1_, __use_tag>)
- ::new(__p) _Tp1(allocator_arg, *this,
- std::forward<_Args>(__args)...);
- else
- ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this);
- }
-
- template<typename _Tp1, typename _Tp2,
- typename... _Args1, typename... _Args2>
- __attribute__((__nonnull__))
- void
- construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t,
- tuple<_Args1...> __x, tuple<_Args2...> __y)
- {
- auto __x_tag =
- __use_alloc<_Tp1, polymorphic_allocator, _Args1...>(*this);
- auto __y_tag =
- __use_alloc<_Tp2, polymorphic_allocator, _Args2...>(*this);
- index_sequence_for<_Args1...> __x_i;
- index_sequence_for<_Args2...> __y_i;
-
- ::new(__p) pair<_Tp1, _Tp2>(piecewise_construct,
- _S_construct_p(__x_tag, __x_i, __x),
- _S_construct_p(__y_tag, __y_i, __y));
- }
-
- template<typename _Tp1, typename _Tp2>
- __attribute__((__nonnull__))
- void
- construct(pair<_Tp1, _Tp2>* __p)
- { this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
-
- template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
- __attribute__((__nonnull__))
- void
- construct(pair<_Tp1, _Tp2>* __p, _Up&& __x, _Vp&& __y)
- {
- this->construct(__p, piecewise_construct,
- forward_as_tuple(std::forward<_Up>(__x)),
- forward_as_tuple(std::forward<_Vp>(__y)));
- }
-
- template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
- __attribute__((__nonnull__))
- void
- construct(pair<_Tp1, _Tp2>* __p, const std::pair<_Up, _Vp>& __pr)
- {
- this->construct(__p, piecewise_construct,
- forward_as_tuple(__pr.first),
- forward_as_tuple(__pr.second));
- }
-
- template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
- __attribute__((__nonnull__))
- void
- construct(pair<_Tp1, _Tp2>* __p, pair<_Up, _Vp>&& __pr)
- {
- this->construct(__p, piecewise_construct,
- forward_as_tuple(std::forward<_Up>(__pr.first)),
- forward_as_tuple(std::forward<_Vp>(__pr.second)));
- }
-#else
template<typename _Tp1, typename... _Args>
__attribute__((__nonnull__))
void
construct(_Tp1* __p, _Args&&... __args)
{
- std::uninitialized_construct_using_allocator(__p, *this,
- std::forward<_Args>(__args)...);
+ ::new ((void*)__p) _Tp1(std::make_from_tuple<_Tp1>(
+ std::__uses_alloc_args<_Tp1>(*this,
+ std::forward<_Args>(__args)...)));
}
-#endif
template<typename _Up>
__attribute__((__nonnull__))
@@ -270,30 +188,6 @@ namespace pmr
{ return _M_resource; }
private:
- using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>;
- using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>;
-
- template<typename _Ind, typename... _Args>
- static tuple<_Args&&...>
- _S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
- { return std::move(__t); }
-
- template<size_t... _Ind, typename... _Args>
- static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...>
- _S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>,
- tuple<_Args...>& __t)
- {
- return {
- allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))...
- };
- }
-
- template<size_t... _Ind, typename... _Args>
- static tuple<_Args&&..., polymorphic_allocator>
- _S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>,
- tuple<_Args...>& __t)
- { return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; }
-
memory_resource* _M_resource;
};