2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Discriminated pointer: Type-safe pointer to one of several types.
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
24 * @author Tudor Bosman (tudorb@fb.com)
31 #include <glog/logging.h>
32 #include <folly/Likely.h>
33 #include <folly/Portability.h>
34 #include <folly/detail/DiscriminatedPtrDetail.h>
36 #if !FOLLY_X64 && !FOLLY_A64 && !FOLLY_PPC64
37 # error "DiscriminatedPtr is x64, arm64 and ppc64 specific code."
43 * Discriminated pointer.
45 * Given a list of types, a DiscriminatedPtr<Types...> may point to an object
46 * of one of the given types, or may be empty. DiscriminatedPtr is type-safe:
47 * you may only get a pointer to the type that you put in, otherwise get
48 * throws an exception (and get_nothrow returns nullptr)
50 * This pointer does not do any kind of lifetime management -- it's not a
51 * "smart" pointer. You are responsible for deallocating any memory used
52 * to hold pointees, if necessary.
54 template <typename... Types>
55 class DiscriminatedPtr {
56 // <, not <=, as our indexes are 1-based (0 means "empty")
57 static_assert(sizeof...(Types) < std::numeric_limits<uint16_t>::max(),
62 * Create an empty DiscriminatedPtr.
64 DiscriminatedPtr() : data_(0) {
68 * Create a DiscriminatedPtr that points to an object of type T.
69 * Fails at compile time if T is not a valid type (listed in Types)
72 explicit DiscriminatedPtr(T* ptr) {
73 set(ptr, typeIndex<T>());
77 * Set this DiscriminatedPtr to point to an object of type T.
78 * Fails at compile time if T is not a valid type (listed in Types)
82 set(ptr, typeIndex<T>());
86 * Get a pointer to the object that this DiscriminatedPtr points to, if it is
87 * of type T. Fails at compile time if T is not a valid type (listed in
88 * Types), and returns nullptr if this DiscriminatedPtr is empty or points to
89 * an object of a different type.
92 T* get_nothrow() noexcept {
93 void* p = LIKELY(hasType<T>()) ? ptr() : nullptr;
94 return static_cast<T*>(p);
98 const T* get_nothrow() const noexcept {
99 const void* p = LIKELY(hasType<T>()) ? ptr() : nullptr;
100 return static_cast<const T*>(p);
104 * Get a pointer to the object that this DiscriminatedPtr points to, if it is
105 * of type T. Fails at compile time if T is not a valid type (listed in
106 * Types), and throws std::invalid_argument if this DiscriminatedPtr is empty
107 * or points to an object of a different type.
109 template <typename T>
111 if (UNLIKELY(!hasType<T>())) {
112 throw std::invalid_argument("Invalid type");
114 return static_cast<T*>(ptr());
117 template <typename T>
118 const T* get() const {
119 if (UNLIKELY(!hasType<T>())) {
120 throw std::invalid_argument("Invalid type");
122 return static_cast<const T*>(ptr());
126 * Return true iff this DiscriminatedPtr is empty.
133 * Return true iff the object pointed by this DiscriminatedPtr has type T,
134 * false otherwise. Fails at compile time if T is not a valid type (listed
137 template <typename T>
138 bool hasType() const {
139 return index() == typeIndex<T>();
143 * Clear this DiscriminatedPtr, making it empty.
150 * Assignment operator from a pointer of type T.
152 template <typename T>
153 DiscriminatedPtr& operator=(T* ptr) {
159 * Apply a visitor to this object, calling the appropriate overload for
160 * the type currently stored in DiscriminatedPtr. Throws invalid_argument
161 * if the DiscriminatedPtr is empty.
163 * The visitor must meet the following requirements:
165 * - The visitor must allow invocation as a function by overloading
166 * operator(), unambiguously accepting all values of type T* (or const T*)
167 * for all T in Types...
168 * - All operations of the function object on T* (or const T*) must
169 * return the same type (or a static_assert will fire).
171 template <typename V>
172 typename dptr_detail::VisitorResult<V, Types...>::type apply(V&& visitor) {
174 if (n == 0) throw std::invalid_argument("Empty DiscriminatedPtr");
175 return dptr_detail::ApplyVisitor<V, Types...>()(
176 n, std::forward<V>(visitor), ptr());
179 template <typename V>
180 typename dptr_detail::ConstVisitorResult<V, Types...>::type apply(V&& visitor)
183 if (n == 0) throw std::invalid_argument("Empty DiscriminatedPtr");
184 return dptr_detail::ApplyConstVisitor<V, Types...>()(
185 n, std::forward<V>(visitor), ptr());
190 * Get the 1-based type index of T in Types.
192 template <typename T>
193 uint16_t typeIndex() const {
194 return uint16_t(dptr_detail::GetTypeIndex<T, Types...>::value);
197 uint16_t index() const { return data_ >> 48; }
199 return reinterpret_cast<void*>(data_ & ((1ULL << 48) - 1));
202 void set(void* p, uint16_t v) {
203 uintptr_t ip = reinterpret_cast<uintptr_t>(p);
205 ip |= static_cast<uintptr_t>(v) << 48;
210 * We store a pointer in the least significant 48 bits of data_, and a type
211 * index (0 = empty, or 1-based index in Types) in the most significant 16
212 * bits. We rely on the fact that pointers have their most significant 16
213 * bits clear on x86_64.
218 template <typename Visitor, typename... Args>
219 decltype(auto) apply_visitor(
221 const DiscriminatedPtr<Args...>& variant) {
222 return variant.apply(std::forward<Visitor>(visitor));
225 template <typename Visitor, typename... Args>
226 decltype(auto) apply_visitor(
228 DiscriminatedPtr<Args...>& variant) {
229 return variant.apply(std::forward<Visitor>(visitor));
232 template <typename Visitor, typename... Args>
233 decltype(auto) apply_visitor(
235 DiscriminatedPtr<Args...>&& variant) {
236 return variant.apply(std::forward<Visitor>(visitor));