1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 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 #pragma once 18 19 #include <fmt/format.h> 20 #include <folly/Demangle.h> 21 22 namespace folly { 23 namespace channels { 24 namespace detail { 25 26 /** 27 * A PointerVariant stores a pointer of one of two possible types. 28 */ 29 template <typename FirstType, typename SecondType> 30 class PointerVariant { 31 public: 32 template <typename T> PointerVariant(T * pointer)33 explicit PointerVariant(T* pointer) { 34 set(pointer); 35 } 36 PointerVariant(PointerVariant && other)37 PointerVariant(PointerVariant&& other) noexcept 38 : storage_(std::exchange(other.storage_, 0)) {} 39 40 PointerVariant& operator=(PointerVariant&& other) noexcept { 41 storage_ = std::exchange(other.storage_, 0); 42 return *this; 43 } 44 45 /** 46 * Returns the zero-based index of the type that is currently held. 47 */ index()48 size_t index() const { return static_cast<size_t>(storage_ & kTypeMask); } 49 50 /** 51 * Returns the pointer stored in the PointerVariant, if the type matches the 52 * first type. If the stored type does not match the first type, an exception 53 * will be thrown. 54 */ get(folly::tag_t<FirstType>)55 inline FirstType* get(folly::tag_t<FirstType>) const { 56 ensureCorrectType(false /* secondType */); 57 return reinterpret_cast<FirstType*>(storage_ & kPointerMask); 58 } 59 60 /** 61 * Returns the pointer stored in the PointerVariant, if the type matches the 62 * second type. If the stored type does not match the second type, an 63 * exception will be thrown. 64 */ get(folly::tag_t<SecondType>)65 inline SecondType* get(folly::tag_t<SecondType>) const { 66 ensureCorrectType(true /* secondType */); 67 return reinterpret_cast<SecondType*>(storage_ & kPointerMask); 68 } 69 70 /** 71 * Store a new pointer of type FirstType in the PointerVariant. 72 */ set(FirstType * pointer)73 void set(FirstType* pointer) { 74 storage_ = reinterpret_cast<intptr_t>(pointer); 75 } 76 77 /** 78 * Store a new pointer of type SecondType in the PointerVariant. 79 */ set(SecondType * pointer)80 void set(SecondType* pointer) { 81 storage_ = reinterpret_cast<intptr_t>(pointer) | kTypeMask; 82 } 83 84 private: ensureCorrectType(bool secondType)85 void ensureCorrectType(bool secondType) const { 86 if (secondType != !!(storage_ & kTypeMask)) { 87 throw std::runtime_error(fmt::format( 88 "Incorrect type specified. Given: {}, Stored: {}", 89 secondType ? folly::demangle(typeid(SecondType).name()) 90 : folly::demangle(typeid(FirstType).name()), 91 storage_ & kTypeMask ? folly::demangle(typeid(SecondType).name()) 92 : folly::demangle(typeid(FirstType).name()))); 93 } 94 } 95 96 static constexpr intptr_t kTypeMask = 1; 97 static constexpr intptr_t kPointerMask = ~kTypeMask; 98 99 intptr_t storage_; 100 }; 101 } // namespace detail 102 } // namespace channels 103 } // namespace folly 104