• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 template <class Policy, class = void>
32 struct policy_trait_element_is_owner : std::false_type {};
33 
34 template <class Policy>
35 struct policy_trait_element_is_owner<
36     Policy,
37     std::enable_if_t<!std::is_void<typename Policy::element_is_owner>::value>>
38     : Policy::element_is_owner {};
39 
40 // Defines how slots are initialized/destroyed/moved.
41 template <class Policy, class = void>
42 struct common_policy_traits {
43   // The actual object stored in the container.
44   using slot_type = typename Policy::slot_type;
45   using reference = decltype(Policy::element(std::declval<slot_type*>()));
46   using value_type = typename std::remove_reference<reference>::type;
47 
48   // PRECONDITION: `slot` is UNINITIALIZED
49   // POSTCONDITION: `slot` is INITIALIZED
50   template <class Alloc, class... Args>
51   static void construct(Alloc* alloc, slot_type* slot, Args&&... args) {
52     Policy::construct(alloc, slot, std::forward<Args>(args)...);
53   }
54 
55   // PRECONDITION: `slot` is INITIALIZED
56   // POSTCONDITION: `slot` is UNINITIALIZED
57   // Returns std::true_type in case destroy is trivial.
58   template <class Alloc>
59   static auto destroy(Alloc* alloc, slot_type* slot) {
60     return Policy::destroy(alloc, slot);
61   }
62 
63   // Transfers the `old_slot` to `new_slot`. Any memory allocated by the
64   // allocator inside `old_slot` to `new_slot` can be transferred.
65   //
66   // OPTIONAL: defaults to:
67   //
68   //     clone(new_slot, std::move(*old_slot));
69   //     destroy(old_slot);
70   //
71   // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED
72   // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is
73   //                UNINITIALIZED
74   template <class Alloc>
75   static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) {
76     transfer_impl(alloc, new_slot, old_slot, Rank2{});
77   }
78 
79   // PRECONDITION: `slot` is INITIALIZED
80   // POSTCONDITION: `slot` is INITIALIZED
81   // Note: we use remove_const_t so that the two overloads have different args
82   // in the case of sets with explicitly const value_types.
83   template <class P = Policy>
84   static auto element(absl::remove_const_t<slot_type>* slot)
85       -> decltype(P::element(slot)) {
86     return P::element(slot);
87   }
88   template <class P = Policy>
89   static auto element(const slot_type* slot) -> decltype(P::element(slot)) {
90     return P::element(slot);
91   }
92 
93   static constexpr bool transfer_uses_memcpy() {
94     return std::is_same<decltype(transfer_impl<std::allocator<char>>(
95                             nullptr, nullptr, nullptr, Rank2{})),
96                         std::true_type>::value;
97   }
98 
99   // Returns true if destroy is trivial and can be omitted.
100   template <class Alloc>
101   static constexpr bool destroy_is_trivial() {
102     return std::is_same<decltype(destroy<Alloc>(nullptr, nullptr)),
103                         std::true_type>::value;
104   }
105 
106  private:
107   // Use go/ranked-overloads for dispatching.
108   struct Rank0 {};
109   struct Rank1 : Rank0 {};
110   struct Rank2 : Rank1 {};
111 
112   // Use auto -> decltype as an enabler.
113   // P::transfer returns std::true_type if transfer uses memcpy (e.g. in
114   // node_slot_policy).
115   template <class Alloc, class P = Policy>
116   static auto transfer_impl(Alloc* alloc, slot_type* new_slot,
117                             slot_type* old_slot,
118                             Rank2) -> decltype(P::transfer(alloc, new_slot,
119                                                            old_slot)) {
120     return P::transfer(alloc, new_slot, old_slot);
121   }
122 
123   // This overload returns true_type for the trait below.
124   // The conditional_t is to make the enabler type dependent.
125   template <class Alloc,
126             typename = std::enable_if_t<absl::is_trivially_relocatable<
127                 std::conditional_t<false, Alloc, value_type>>::value>>
128   static std::true_type transfer_impl(Alloc*, slot_type* new_slot,
129                                       slot_type* old_slot, Rank1) {
130     // TODO(b/247130232): remove casts after fixing warnings.
131     // TODO(b/251814870): remove casts after fixing warnings.
132     std::memcpy(
133         static_cast<void*>(std::launder(
134             const_cast<std::remove_const_t<value_type>*>(&element(new_slot)))),
135         static_cast<const void*>(&element(old_slot)), sizeof(value_type));
136     return {};
137   }
138 
139   template <class Alloc>
140   static void transfer_impl(Alloc* alloc, slot_type* new_slot,
141                             slot_type* old_slot, Rank0) {
142     construct(alloc, new_slot, std::move(element(old_slot)));
143     destroy(alloc, old_slot);
144   }
145 };
146 
147 }  // namespace container_internal
148 ABSL_NAMESPACE_END
149 }  // namespace absl
150 
151 #endif  // ABSL_CONTAINER_INTERNAL_COMMON_POLICY_TRAITS_H_
152