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