123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /*
- * Copyright 2011-present Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /**
- * Discriminated pointer: Type-safe pointer to one of several types.
- *
- * Similar to boost::variant, but has no space overhead over a raw pointer, as
- * it relies on the fact that (on x86_64) there are 16 unused bits in a
- * pointer.
- *
- * @author Tudor Bosman (tudorb@fb.com)
- */
- #pragma once
- #include <limits>
- #include <stdexcept>
- #include <glog/logging.h>
- #include <folly/Likely.h>
- #include <folly/Portability.h>
- #include <folly/detail/DiscriminatedPtrDetail.h>
- #if !FOLLY_X64 && !FOLLY_AARCH64 && !FOLLY_PPC64
- #error "DiscriminatedPtr is x64, arm64 and ppc64 specific code."
- #endif
- namespace folly {
- /**
- * Discriminated pointer.
- *
- * Given a list of types, a DiscriminatedPtr<Types...> may point to an object
- * of one of the given types, or may be empty. DiscriminatedPtr is type-safe:
- * you may only get a pointer to the type that you put in, otherwise get
- * throws an exception (and get_nothrow returns nullptr)
- *
- * This pointer does not do any kind of lifetime management -- it's not a
- * "smart" pointer. You are responsible for deallocating any memory used
- * to hold pointees, if necessary.
- */
- template <typename... Types>
- class DiscriminatedPtr {
- // <, not <=, as our indexes are 1-based (0 means "empty")
- static_assert(
- sizeof...(Types) < std::numeric_limits<uint16_t>::max(),
- "too many types");
- public:
- /**
- * Create an empty DiscriminatedPtr.
- */
- DiscriminatedPtr() : data_(0) {}
- /**
- * Create a DiscriminatedPtr that points to an object of type T.
- * Fails at compile time if T is not a valid type (listed in Types)
- */
- template <typename T>
- explicit DiscriminatedPtr(T* ptr) {
- set(ptr, typeIndex<T>());
- }
- /**
- * Set this DiscriminatedPtr to point to an object of type T.
- * Fails at compile time if T is not a valid type (listed in Types)
- */
- template <typename T>
- void set(T* ptr) {
- set(ptr, typeIndex<T>());
- }
- /**
- * Get a pointer to the object that this DiscriminatedPtr points to, if it is
- * of type T. Fails at compile time if T is not a valid type (listed in
- * Types), and returns nullptr if this DiscriminatedPtr is empty or points to
- * an object of a different type.
- */
- template <typename T>
- T* get_nothrow() noexcept {
- void* p = LIKELY(hasType<T>()) ? ptr() : nullptr;
- return static_cast<T*>(p);
- }
- template <typename T>
- const T* get_nothrow() const noexcept {
- const void* p = LIKELY(hasType<T>()) ? ptr() : nullptr;
- return static_cast<const T*>(p);
- }
- /**
- * Get a pointer to the object that this DiscriminatedPtr points to, if it is
- * of type T. Fails at compile time if T is not a valid type (listed in
- * Types), and throws std::invalid_argument if this DiscriminatedPtr is empty
- * or points to an object of a different type.
- */
- template <typename T>
- T* get() {
- if (UNLIKELY(!hasType<T>())) {
- throw std::invalid_argument("Invalid type");
- }
- return static_cast<T*>(ptr());
- }
- template <typename T>
- const T* get() const {
- if (UNLIKELY(!hasType<T>())) {
- throw std::invalid_argument("Invalid type");
- }
- return static_cast<const T*>(ptr());
- }
- /**
- * Return true iff this DiscriminatedPtr is empty.
- */
- bool empty() const {
- return index() == 0;
- }
- /**
- * Return true iff the object pointed by this DiscriminatedPtr has type T,
- * false otherwise. Fails at compile time if T is not a valid type (listed
- * in Types...)
- */
- template <typename T>
- bool hasType() const {
- return index() == typeIndex<T>();
- }
- /**
- * Clear this DiscriminatedPtr, making it empty.
- */
- void clear() {
- data_ = 0;
- }
- /**
- * Assignment operator from a pointer of type T.
- */
- template <typename T>
- DiscriminatedPtr& operator=(T* ptr) {
- set(ptr);
- return *this;
- }
- /**
- * Apply a visitor to this object, calling the appropriate overload for
- * the type currently stored in DiscriminatedPtr. Throws invalid_argument
- * if the DiscriminatedPtr is empty.
- *
- * The visitor must meet the following requirements:
- *
- * - The visitor must allow invocation as a function by overloading
- * operator(), unambiguously accepting all values of type T* (or const T*)
- * for all T in Types...
- * - All operations of the function object on T* (or const T*) must
- * return the same type (or a static_assert will fire).
- */
- template <typename V>
- typename dptr_detail::VisitorResult<V, Types...>::type apply(V&& visitor) {
- size_t n = index();
- if (n == 0) {
- throw std::invalid_argument("Empty DiscriminatedPtr");
- }
- return dptr_detail::ApplyVisitor<V, Types...>()(
- n, std::forward<V>(visitor), ptr());
- }
- template <typename V>
- typename dptr_detail::ConstVisitorResult<V, Types...>::type apply(
- V&& visitor) const {
- size_t n = index();
- if (n == 0) {
- throw std::invalid_argument("Empty DiscriminatedPtr");
- }
- return dptr_detail::ApplyConstVisitor<V, Types...>()(
- n, std::forward<V>(visitor), ptr());
- }
- private:
- /**
- * Get the 1-based type index of T in Types.
- */
- template <typename T>
- uint16_t typeIndex() const {
- return uint16_t(dptr_detail::GetTypeIndex<T, Types...>::value);
- }
- uint16_t index() const {
- return data_ >> 48;
- }
- void* ptr() const {
- return reinterpret_cast<void*>(data_ & ((1ULL << 48) - 1));
- }
- void set(void* p, uint16_t v) {
- uintptr_t ip = reinterpret_cast<uintptr_t>(p);
- CHECK(!(ip >> 48));
- ip |= static_cast<uintptr_t>(v) << 48;
- data_ = ip;
- }
- /**
- * We store a pointer in the least significant 48 bits of data_, and a type
- * index (0 = empty, or 1-based index in Types) in the most significant 16
- * bits. We rely on the fact that pointers have their most significant 16
- * bits clear on x86_64.
- */
- uintptr_t data_;
- };
- template <typename Visitor, typename... Args>
- decltype(auto) apply_visitor(
- Visitor&& visitor,
- const DiscriminatedPtr<Args...>& variant) {
- return variant.apply(std::forward<Visitor>(visitor));
- }
- template <typename Visitor, typename... Args>
- decltype(auto) apply_visitor(
- Visitor&& visitor,
- DiscriminatedPtr<Args...>& variant) {
- return variant.apply(std::forward<Visitor>(visitor));
- }
- template <typename Visitor, typename... Args>
- decltype(auto) apply_visitor(
- Visitor&& visitor,
- DiscriminatedPtr<Args...>&& variant) {
- return variant.apply(std::forward<Visitor>(visitor));
- }
- } // namespace folly
|