1 | /* |
2 | * Copyright 2011-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | /** |
18 | * Discriminated pointer: Type-safe pointer to one of several types. |
19 | * |
20 | * Similar to boost::variant, but has no space overhead over a raw pointer, as |
21 | * it relies on the fact that (on x86_64) there are 16 unused bits in a |
22 | * pointer. |
23 | * |
24 | * @author Tudor Bosman (tudorb@fb.com) |
25 | */ |
26 | |
27 | #pragma once |
28 | |
29 | #include <limits> |
30 | #include <stdexcept> |
31 | |
32 | #include <glog/logging.h> |
33 | |
34 | #include <folly/Likely.h> |
35 | #include <folly/Portability.h> |
36 | #include <folly/detail/DiscriminatedPtrDetail.h> |
37 | |
38 | #if !FOLLY_X64 && !FOLLY_AARCH64 && !FOLLY_PPC64 |
39 | #error "DiscriminatedPtr is x64, arm64 and ppc64 specific code." |
40 | #endif |
41 | |
42 | namespace folly { |
43 | |
44 | /** |
45 | * Discriminated pointer. |
46 | * |
47 | * Given a list of types, a DiscriminatedPtr<Types...> may point to an object |
48 | * of one of the given types, or may be empty. DiscriminatedPtr is type-safe: |
49 | * you may only get a pointer to the type that you put in, otherwise get |
50 | * throws an exception (and get_nothrow returns nullptr) |
51 | * |
52 | * This pointer does not do any kind of lifetime management -- it's not a |
53 | * "smart" pointer. You are responsible for deallocating any memory used |
54 | * to hold pointees, if necessary. |
55 | */ |
56 | template <typename... Types> |
57 | class DiscriminatedPtr { |
58 | // <, not <=, as our indexes are 1-based (0 means "empty") |
59 | static_assert( |
60 | sizeof...(Types) < std::numeric_limits<uint16_t>::max(), |
61 | "too many types" ); |
62 | |
63 | public: |
64 | /** |
65 | * Create an empty DiscriminatedPtr. |
66 | */ |
67 | DiscriminatedPtr() : data_(0) {} |
68 | |
69 | /** |
70 | * Create a DiscriminatedPtr that points to an object of type T. |
71 | * Fails at compile time if T is not a valid type (listed in Types) |
72 | */ |
73 | template <typename T> |
74 | explicit DiscriminatedPtr(T* ptr) { |
75 | set(ptr, typeIndex<T>()); |
76 | } |
77 | |
78 | /** |
79 | * Set this DiscriminatedPtr to point to an object of type T. |
80 | * Fails at compile time if T is not a valid type (listed in Types) |
81 | */ |
82 | template <typename T> |
83 | void set(T* ptr) { |
84 | set(ptr, typeIndex<T>()); |
85 | } |
86 | |
87 | /** |
88 | * Get a pointer to the object that this DiscriminatedPtr points to, if it is |
89 | * of type T. Fails at compile time if T is not a valid type (listed in |
90 | * Types), and returns nullptr if this DiscriminatedPtr is empty or points to |
91 | * an object of a different type. |
92 | */ |
93 | template <typename T> |
94 | T* get_nothrow() noexcept { |
95 | void* p = LIKELY(hasType<T>()) ? ptr() : nullptr; |
96 | return static_cast<T*>(p); |
97 | } |
98 | |
99 | template <typename T> |
100 | const T* get_nothrow() const noexcept { |
101 | const void* p = LIKELY(hasType<T>()) ? ptr() : nullptr; |
102 | return static_cast<const T*>(p); |
103 | } |
104 | |
105 | /** |
106 | * Get a pointer to the object that this DiscriminatedPtr points to, if it is |
107 | * of type T. Fails at compile time if T is not a valid type (listed in |
108 | * Types), and throws std::invalid_argument if this DiscriminatedPtr is empty |
109 | * or points to an object of a different type. |
110 | */ |
111 | template <typename T> |
112 | T* get() { |
113 | if (UNLIKELY(!hasType<T>())) { |
114 | throw std::invalid_argument("Invalid type" ); |
115 | } |
116 | return static_cast<T*>(ptr()); |
117 | } |
118 | |
119 | template <typename T> |
120 | const T* get() const { |
121 | if (UNLIKELY(!hasType<T>())) { |
122 | throw std::invalid_argument("Invalid type" ); |
123 | } |
124 | return static_cast<const T*>(ptr()); |
125 | } |
126 | |
127 | /** |
128 | * Return true iff this DiscriminatedPtr is empty. |
129 | */ |
130 | bool empty() const { |
131 | return index() == 0; |
132 | } |
133 | |
134 | /** |
135 | * Return true iff the object pointed by this DiscriminatedPtr has type T, |
136 | * false otherwise. Fails at compile time if T is not a valid type (listed |
137 | * in Types...) |
138 | */ |
139 | template <typename T> |
140 | bool hasType() const { |
141 | return index() == typeIndex<T>(); |
142 | } |
143 | |
144 | /** |
145 | * Clear this DiscriminatedPtr, making it empty. |
146 | */ |
147 | void clear() { |
148 | data_ = 0; |
149 | } |
150 | |
151 | /** |
152 | * Assignment operator from a pointer of type T. |
153 | */ |
154 | template <typename T> |
155 | DiscriminatedPtr& operator=(T* ptr) { |
156 | set(ptr); |
157 | return *this; |
158 | } |
159 | |
160 | /** |
161 | * Apply a visitor to this object, calling the appropriate overload for |
162 | * the type currently stored in DiscriminatedPtr. Throws invalid_argument |
163 | * if the DiscriminatedPtr is empty. |
164 | * |
165 | * The visitor must meet the following requirements: |
166 | * |
167 | * - The visitor must allow invocation as a function by overloading |
168 | * operator(), unambiguously accepting all values of type T* (or const T*) |
169 | * for all T in Types... |
170 | * - All operations of the function object on T* (or const T*) must |
171 | * return the same type (or a static_assert will fire). |
172 | */ |
173 | template <typename V> |
174 | typename dptr_detail::VisitorResult<V, Types...>::type apply(V&& visitor) { |
175 | size_t n = index(); |
176 | if (n == 0) { |
177 | throw std::invalid_argument("Empty DiscriminatedPtr" ); |
178 | } |
179 | return dptr_detail::ApplyVisitor<V, Types...>()( |
180 | n, std::forward<V>(visitor), ptr()); |
181 | } |
182 | |
183 | template <typename V> |
184 | typename dptr_detail::ConstVisitorResult<V, Types...>::type apply( |
185 | V&& visitor) const { |
186 | size_t n = index(); |
187 | if (n == 0) { |
188 | throw std::invalid_argument("Empty DiscriminatedPtr" ); |
189 | } |
190 | return dptr_detail::ApplyConstVisitor<V, Types...>()( |
191 | n, std::forward<V>(visitor), ptr()); |
192 | } |
193 | |
194 | private: |
195 | /** |
196 | * Get the 1-based type index of T in Types. |
197 | */ |
198 | template <typename T> |
199 | uint16_t typeIndex() const { |
200 | return uint16_t(dptr_detail::GetTypeIndex<T, Types...>::value); |
201 | } |
202 | |
203 | uint16_t index() const { |
204 | return data_ >> 48; |
205 | } |
206 | void* ptr() const { |
207 | return reinterpret_cast<void*>(data_ & ((1ULL << 48) - 1)); |
208 | } |
209 | |
210 | void set(void* p, uint16_t v) { |
211 | uintptr_t ip = reinterpret_cast<uintptr_t>(p); |
212 | CHECK(!(ip >> 48)); |
213 | ip |= static_cast<uintptr_t>(v) << 48; |
214 | data_ = ip; |
215 | } |
216 | |
217 | /** |
218 | * We store a pointer in the least significant 48 bits of data_, and a type |
219 | * index (0 = empty, or 1-based index in Types) in the most significant 16 |
220 | * bits. We rely on the fact that pointers have their most significant 16 |
221 | * bits clear on x86_64. |
222 | */ |
223 | uintptr_t data_; |
224 | }; |
225 | |
226 | template <typename Visitor, typename... Args> |
227 | decltype(auto) apply_visitor( |
228 | Visitor&& visitor, |
229 | const DiscriminatedPtr<Args...>& variant) { |
230 | return variant.apply(std::forward<Visitor>(visitor)); |
231 | } |
232 | |
233 | template <typename Visitor, typename... Args> |
234 | decltype(auto) apply_visitor( |
235 | Visitor&& visitor, |
236 | DiscriminatedPtr<Args...>& variant) { |
237 | return variant.apply(std::forward<Visitor>(visitor)); |
238 | } |
239 | |
240 | template <typename Visitor, typename... Args> |
241 | decltype(auto) apply_visitor( |
242 | Visitor&& visitor, |
243 | DiscriminatedPtr<Args...>&& variant) { |
244 | return variant.apply(std::forward<Visitor>(visitor)); |
245 | } |
246 | |
247 | } // namespace folly |
248 | |