Doxygen
variant.h
浏览该文件的文档.
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2021 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15 
16 #ifndef VARIANT_H
17 #define VARIANT_H
18 
19 #include <string>
20 #include <utility>
21 
22 namespace details
23 {
24 
25 //----- template helper class to compute the maximum over a set of template arguments
26 
27 //! generic declaration of a template to compute the maximum size of a set of template parameters.
28 template <size_t arg1, size_t ... others>
29 struct TMax;
30 
31 //! specialization to stop the recursion when arrived at a single argument.
32 template <size_t arg>
33 struct TMax<arg>
34 {
35  //! the compile time computed maximum value
36  static const size_t value = arg;
37 };
38 
39 //! recursive definition for multiple arguments
40 template <size_t arg1, size_t arg2, size_t ... others>
41 struct TMax<arg1, arg2, others...>
42 {
43  //! the compile time computed maximum value
44  static const size_t value = arg1 >= arg2 ?
45  TMax<arg1, others...>::value :
46  TMax<arg2, others...>::value ;
47 };
48 
49 //------ helper class to deal with memory management of an array of template types
50 
51 //! generic declaration of a template to handle copying, moving, and deleting
52 //! type that matches a given index in the list of variant parameters.
53 template<uint8_t n,typename... Ts>
54 struct HelperRecT;
55 
56 //! Recursive template definition for multiple arguments.
57 //! Each function checks for a matching index, and if found does the action.
58 //! If not, the same function is called with the next index and one less type argument.
59 template<uint8_t n, typename F, typename... Ts>
60 struct HelperRecT<n, F, Ts...>
61 {
62  //! Helper function to copy an instance of a type by performing a placement new.
63  //! @param id The id if the type to search for in the template parameter list
64  //! @param src_v A pointer to the value of the type to copy from.
65  //! @param dst_v A pointer to the variable to copy to.
66  inline static void copy(uint8_t id, const void * src_v, void * dst_v)
67  {
68  if (n==id) // found it
69  {
70  new (dst_v) F(*reinterpret_cast<const F*>(src_v));
71  }
72  else // continue searching
73  {
74  HelperRecT<n+1,Ts...>::copy(id, src_v, dst_v);
75  }
76  }
77 
78  //! Helper function to move an instance of a type by calling the move constructor on it.
79  //! @param id The id if the type to search for in the template parameter list
80  //! @param src_v A pointer to the value of the type to copy from.
81  //! @param dst_v A pointer to the variable to copy to.
82  inline static void move(uint8_t id, void * src_v, void * dst_v)
83  {
84  if (n==id) // found it
85  {
86  new (dst_v) F(std::move(*reinterpret_cast<F*>(src_v)));
87  }
88  else // continue searching
89  {
90  HelperRecT<n+1,Ts...>::move(id, src_v, dst_v);
91  }
92  }
93 
94  //! Helper function to destroy an object of a given type by calling its destructor.
95  //! @param id The id if the type to search for in the template parameter list
96  //! @param data A pointer to the object to destroy
97  inline static void destroy(uint8_t id, void * data)
98  {
99  if (n==id) // found it
100  {
101  reinterpret_cast<F*>(data)->~F();
102  }
103  else // continue searching
104  {
106  }
107  }
108 
109 };
110 
111 //! Specialization to stop the recursion when the end of the list has reached
112 template<uint8_t n>
113 struct HelperRecT<n>
114 {
115  inline static void copy(uint8_t id, const void * src_v, void * dst_v) { }
116  inline static void move(uint8_t id, void * src_v, void * dst_v) { }
117  inline static void destroy(uint8_t id, void * data) { }
118 };
119 
120 //! Helper to kickstart the recursive search
121 template<typename ...Ts>
122 struct HelperT
123 {
124  inline static void copy(uint8_t id, const void *src_v, void *dst_v)
125  { HelperRecT<0, Ts...>::copy(id, src_v, dst_v); }
126  inline static void move(uint8_t id, void *src_v, void *dst_v)
127  { HelperRecT<0, Ts...>::move(id, src_v, dst_v); }
128  inline static void destroy(uint8_t id,void *data)
130 };
131 
132 //! Specialization to end the recursion
133 template<>
134 struct HelperT<>
135 {
136  inline static void copy(uint8_t id, const void * src_v, void * dst_v) { }
137  inline static void move(uint8_t id, void * src_v, void * dst_v) { }
138  inline static void destroy(uint8_t id, void * data) { }
139 };
140 
141 } // namespace details
142 
143 //! Generic declaration of a template type wrapper where VariantType<index,...>::type
144 //! represents the type of the variant at the given index.
145 //! The first type of the variant has index 0, the second has
146 //! index 1, etc.
147 template<uint8_t index, typename... Items>
148 struct VariantType;
149 
150 //! specialization to stop the recursion when arrived at index 0 and type F
151 template<typename F,typename...Ts>
152 struct VariantType<0, F, Ts...>
153 {
154  using type = F;
155 };
156 
157 //! recursive definition of the type wrapper
158 template<uint8_t index, typename F, typename... Ts>
159 struct VariantType<index, F, Ts...>
160 {
161  using type = typename VariantType<index-1,Ts...>::type;
162 };
163 
164 //------------------------------------------------------------------
165 
166 //! Implementation of a variant container (similar to C++17's std::variant).
167 //! It can hold either no instances (e.g. initially or after calling invalidate()),
168 //! or hold exactly one instance of an object (after calling set())
169 //! whose type is one of the variant's template parameters.
170 //! Each parameter has an index, the first parameter has index 0.
171 //! It behaves similar to a C union, in that the memory of all
172 //! possible object types is shared, but unlike a C union
173 //! it does allow C++ objects with constructors and destructors to be stored and
174 //! knows what type is stored.
175 template<typename... Ts>
176 struct Variant {
177  private:
178  //! constant respresenting the maximum size that can hold all types in the template list
179  static const size_t data_size = details::TMax<sizeof(Ts)...>::value;
180  //! constant respresenting the maximum alignment requirement for all types in the template list
181  static const size_t data_align = details::TMax<alignof(Ts)...>::value;
182 
183  //! the data type for the Variant's internal memory
184  using Data = typename std::aligned_storage<data_size, data_align>::type;
185 
186  //! a short hand name for the helper class
187  using HelperT = details::HelperT<Ts...>;
188 
189  template<uint8_t index>
190  using Type = typename VariantType<index,Ts...>::type;
191 
192  //! The id that represents an invalid type
193  static inline uint8_t invalid_id() { return 255; }
194 
195  //! the actual data
197  //! a unique identifier for the type held by this variant
198  uint8_t m_id;
199 
200  public:
201  //! The default constructor
203  {
204  }
205 
206  //! The copy constructor
207  Variant(const Variant<Ts...>& src) : m_id(src.m_id)
208  {
209  HelperT::copy(src.m_id, &src.m_data, &m_data);
210  }
211 
212  //! The move constructor
214  {
215  HelperT::move(src.m_id, &src.m_data, &m_data);
216  }
217 
218  //! The copy assignment operator
219  Variant<Ts...>& operator= (const Variant<Ts...> &src)
220  {
221  if (this!=&src) // prevent self assignment
222  {
223  // destroy the old value
225  // and copy over the new one
226  m_id = src.m_id;
227  HelperT::copy(src.m_id, &src.m_data, &m_data);
228  }
229  return *this;
230  }
231 
232  //! The move assignment operator
234  {
235  // destroy the old value
237  // and move in the new one
238  m_id = src.m_id;
239  HelperT::move(src.m_id, &src.m_data, &m_data);
240  return *this;
241  }
242 
243  //! The destructor
245  {
247  }
248 
249  //! Returns true iff the variant container holds a specific type.
250  //! @tparam T the type to search for.
251  template<uint8_t index>
252  constexpr bool is() const { return m_id==index; }
253 
254  //! Returns true iff the Variant holds a valid type.
255  constexpr bool valid() const { return m_id!=invalid_id(); }
256 
257  //! Invalidate the variant. Will destroy any object that is held.
258  void invalidate()
259  {
261  m_id = invalid_id();
262  }
263 
264  //! Returns the index of the type held by this variant, or invalid_id() if the
265  //! variant does not hold any type (i.e. valid() returns false).
266  constexpr uint8_t index() const { return m_id; }
267 
268  //! Replaces the contents of the variant container by constructing a type T calling
269  //! the constructor with Args
270  //! @tparam index the type to make the variant hold an instance of.
271  //! @tparam Args The arguments types to pass to the constructor of T.
272  //! @param args The argument values
273  template<uint8_t index, typename... Args>
274  void set(Args&&... args)
275  {
277  m_id = index;
278  new (&m_data) Type<index>(std::forward<Args>(args)...);
279  }
280 
281  //! Return a non-constant reference to the value held by the variant container.
282  //! @throw std::bad_cast() if called on a variant container that does not hold
283  //! an instance of the type of the variant at index.
284  template<uint8_t index>
286  {
287  if (m_id != index) throw std::bad_cast();
288  return *reinterpret_cast<Type<index>*>(&m_data);
289  }
290 
291  //! Returns a constant reference to the value held by the variant container.
292  //! @throw std::bad_cast() if called on a variant container that does not hold
293  //! an instance of the type of the variant at index.
294  template<uint8_t index>
295  const Type<index>& get() const
296  {
297  if (m_id != index) throw std::bad_cast();
298  return *reinterpret_cast<const Type<index>*>(&m_data);
299  }
300 
301 };
302 
303 #endif
VariantType
Generic declaration of a template type wrapper where VariantType<index,...>::type represents the type...
Definition: variant.h:148
Variant< bool, int, QCString, TemplateStructIntfPtr, TemplateListIntfPtr, FunctionDelegate, TemplateStructIntfWeakPtr >::Type
typename VariantType< index, Ts... >::type Type
Definition: variant.h:190
VariantType< 0, F, Ts... >::type
F type
Definition: variant.h:154
Variant::Variant
Variant()
The default constructor
Definition: variant.h:202
Variant
Implementation of a variant container (similar to C++17's std::variant).
Definition: variant.h:176
details::HelperT
Helper to kickstart the recursive search
Definition: variant.h:135
Variant::operator=
Variant< Ts... > & operator=(const Variant< Ts... > &src)
The copy assignment operator
Definition: variant.h:219
details::TMax
generic declaration of a template to compute the maximum size of a set of template parameters.
Definition: variant.h:42
Variant< bool, int, QCString, TemplateStructIntfPtr, TemplateListIntfPtr, FunctionDelegate, TemplateStructIntfWeakPtr >::Data
typename std::aligned_storage< data_size, data_align >::type Data
the data type for the Variant's internal memory
Definition: variant.h:184
Variant::Variant
Variant(const Variant< Ts... > &src)
The copy constructor
Definition: variant.h:207
Variant::invalidate
void invalidate()
Invalidate the variant. Will destroy any object that is held.
Definition: variant.h:258
Variant::index
constexpr uint8_t index() const
Returns the index of the type held by this variant, or invalid_id() if the variant does not hold any ...
Definition: variant.h:266
Variant::get
Type< index > & get()
Return a non-constant reference to the value held by the variant container.
Definition: variant.h:285
Variant::valid
constexpr bool valid() const
Returns true iff the Variant holds a valid type.
Definition: variant.h:255
Variant::get
const Type< index > & get() const
Returns a constant reference to the value held by the variant container.
Definition: variant.h:295
VariantType< index, F, Ts... >::type
typename VariantType< index-1, Ts... >::type type
Definition: variant.h:161
details
Definition: variant.h:22
Variant::set
void set(Args &&... args)
Replaces the contents of the variant container by constructing a type T calling the constructor with ...
Definition: variant.h:274
details::HelperRecT
generic declaration of a template to handle copying, moving, and deleting type that matches a given i...
Definition: variant.h:67
Variant::invalid_id
static uint8_t invalid_id()
The id that represents an invalid type
Definition: variant.h:193
Variant::data_align
static const size_t data_align
constant respresenting the maximum alignment requirement for all types in the template list
Definition: variant.h:181
Variant::is
constexpr bool is() const
Returns true iff the variant container holds a specific type.
Definition: variant.h:252
details::HelperT::copy
static void copy(uint8_t id, const void *src_v, void *dst_v)
Definition: variant.h:137
Variant::m_id
uint8_t m_id
a unique identifier for the type held by this variant
Definition: variant.h:198
details::HelperT::move
static void move(uint8_t id, void *src_v, void *dst_v)
Definition: variant.h:139
details::HelperT::destroy
static void destroy(uint8_t id, void *data)
Definition: variant.h:141
Variant::~Variant
~Variant()
The destructor
Definition: variant.h:244
Variant::data_size
static const size_t data_size
constant respresenting the maximum size that can hold all types in the template list
Definition: variant.h:179
Variant::Variant
Variant(Variant< Ts... > &&src)
The move constructor
Definition: variant.h:213
Variant::m_data
Data m_data
the actual data
Definition: variant.h:196