DiscriminatedPtr.h 6.9 KB

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