xref: /aosp_15_r20/external/abseil-cpp/absl/container/internal/common_policy_traits.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
1 // Copyright 2022 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_
16 #define ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_
17 
18 #include <cstddef>
19 #include <cstring>
20 #include <memory>
21 #include <new>
22 #include <type_traits>
23 #include <utility>
24 
25 #include "absl/meta/type_traits.h"
26 
27 namespace absl {
28 ABSL_NAMESPACE_BEGIN
29 namespace container_internal {
30 
31 // Defines how slots are initialized/destroyed/moved.
32 template <class Policy, class = void>
33 struct common_policy_traits {
34   // The actual object stored in the container.
35   using slot_type = typename Policy::slot_type;
36   using reference = decltype(Policy::element(std::declval<slot_type*>()));
37   using value_type = typename std::remove_reference<reference>::type;
38 
39   // PRECONDITION: `slot` is UNINITIALIZED
40   // POSTCONDITION: `slot` is INITIALIZED
41   template <class Alloc, class... Args>
constructcommon_policy_traits42   static void construct(Alloc* alloc, slot_type* slot, Args&&... args) {
43     Policy::construct(alloc, slot, std::forward<Args>(args)...);
44   }
45 
46   // PRECONDITION: `slot` is INITIALIZED
47   // POSTCONDITION: `slot` is UNINITIALIZED
48   // Returns std::true_type in case destroy is trivial.
49   template <class Alloc>
destroycommon_policy_traits50   static auto destroy(Alloc* alloc, slot_type* slot) {
51     return Policy::destroy(alloc, slot);
52   }
53 
54   // Transfers the `old_slot` to `new_slot`. Any memory allocated by the
55   // allocator inside `old_slot` to `new_slot` can be transferred.
56   //
57   // OPTIONAL: defaults to:
58   //
59   //     clone(new_slot, std::move(*old_slot));
60   //     destroy(old_slot);
61   //
62   // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED
63   // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is
64   //                UNINITIALIZED
65   template <class Alloc>
transfercommon_policy_traits66   static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) {
67     transfer_impl(alloc, new_slot, old_slot, Rank2{});
68   }
69 
70   // PRECONDITION: `slot` is INITIALIZED
71   // POSTCONDITION: `slot` is INITIALIZED
72   // Note: we use remove_const_t so that the two overloads have different args
73   // in the case of sets with explicitly const value_types.
74   template <class P = Policy>
75   static auto element(absl::remove_const_t<slot_type>* slot)
76       -> decltype(P::element(slot)) {
77     return P::element(slot);
78   }
79   template <class P = Policy>
80   static auto element(const slot_type* slot) -> decltype(P::element(slot)) {
81     return P::element(slot);
82   }
83 
transfer_uses_memcpycommon_policy_traits84   static constexpr bool transfer_uses_memcpy() {
85     return std::is_same<decltype(transfer_impl<std::allocator<char>>(
86                             nullptr, nullptr, nullptr, Rank2{})),
87                         std::true_type>::value;
88   }
89 
90   // Returns true if destroy is trivial and can be omitted.
91   template <class Alloc>
destroy_is_trivialcommon_policy_traits92   static constexpr bool destroy_is_trivial() {
93     return std::is_same<decltype(destroy<Alloc>(nullptr, nullptr)),
94                         std::true_type>::value;
95   }
96 
97  private:
98   // Use go/ranked-overloads for dispatching.
99   struct Rank0 {};
100   struct Rank1 : Rank0 {};
101   struct Rank2 : Rank1 {};
102 
103   // Use auto -> decltype as an enabler.
104   // P::transfer returns std::true_type if transfer uses memcpy (e.g. in
105   // node_slot_policy).
106   template <class Alloc, class P = Policy>
107   static auto transfer_impl(Alloc* alloc, slot_type* new_slot,
108                             slot_type* old_slot,
109                             Rank2) -> decltype(P::transfer(alloc, new_slot,
110                                                            old_slot)) {
111     return P::transfer(alloc, new_slot, old_slot);
112   }
113 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
114   // This overload returns true_type for the trait below.
115   // The conditional_t is to make the enabler type dependent.
116   template <class Alloc,
117             typename = std::enable_if_t<absl::is_trivially_relocatable<
118                 std::conditional_t<false, Alloc, value_type>>::value>>
transfer_implcommon_policy_traits119   static std::true_type transfer_impl(Alloc*, slot_type* new_slot,
120                                       slot_type* old_slot, Rank1) {
121     // TODO(b/247130232): remove casts after fixing warnings.
122     // TODO(b/251814870): remove casts after fixing warnings.
123     std::memcpy(
124         static_cast<void*>(std::launder(
125             const_cast<std::remove_const_t<value_type>*>(&element(new_slot)))),
126         static_cast<const void*>(&element(old_slot)), sizeof(value_type));
127     return {};
128   }
129 #endif
130 
131   template <class Alloc>
transfer_implcommon_policy_traits132   static void transfer_impl(Alloc* alloc, slot_type* new_slot,
133                             slot_type* old_slot, Rank0) {
134     construct(alloc, new_slot, std::move(element(old_slot)));
135     destroy(alloc, old_slot);
136   }
137 };
138 
139 }  // namespace container_internal
140 ABSL_NAMESPACE_END
141 }  // namespace absl
142 
143 #endif  // ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_
144