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