variant
A type safe generalized union type
variant_storage.hpp
Go to the documentation of this file.
1 /*
2  * Boost Software License - Version 1.0 - August 17th, 2003
3  *
4  * Permission is hereby granted, free of charge, to any person or organization
5  * obtaining a copy of the software and accompanying documentation covered by
6  * this license (the "Software") to use, reproduce, display, distribute,
7  * execute, and transmit the Software, and to prepare derivative works of the
8  * Software, and to permit third-parties to whom the Software is furnished to
9  * do so, all subject to the following:
10  *
11  * The copyright notices in the Software and this entire statement, including
12  * the above license grant, this restriction and the following disclaimer,
13  * must be included in all copies of the Software, in whole or in part, and
14  * all derivative works of the Software, unless such copies or derivative
15  * works are solely in the form of machine-executable object code generated by
16  * a source language processor.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
21  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
22  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
23  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
28 #ifndef VARIANT_STORAGE_HPP
29 #define VARIANT_STORAGE_HPP
30 
38 #include <cassert>
39 #include <type_traits>
40 #include <utility>
41 
42 #include "relational.hpp"
43 
63 template<class ... Args>
65 
66 template<class ... Args>
67 union literal_variant_storage;
68 
69 template<class ... Args>
70 union nonliteral_variant_storage;
71 
75 template<std::size_t I, class VariantStorage>
77 
81 template<class VariantStorage>
83 
84 #ifndef VARIANT_IN_DOXYGEN_PARSING
85 
86 template<std::size_t I, class VariantStorage>
87 struct variant_storage_element<I, const VariantStorage> :
88  std::add_const< typename variant_storage_element<I, VariantStorage>::type >::type {};
89 
90 template<std::size_t I, class VariantStorage>
91 struct variant_storage_element<I, volatile VariantStorage> :
92  std::add_volatile< typename variant_storage_element<I, VariantStorage>::type >::type {};
93 
94 template<std::size_t I, class VariantStorage>
95 struct variant_storage_element<I, const volatile VariantStorage> :
96  std::add_cv< typename variant_storage_element<I, VariantStorage>::type >::type {};
97 
98 template<std::size_t I, class VariantStorage>
99 using variant_storage_element_t = typename variant_storage_element<I, VariantStorage>::type;
100 
101 template<std::size_t I, class Head, class ... Args>
102 struct variant_storage_element<I, variant_storage<Head, Args ... > >
103 {
104  typedef typename variant_storage_element<I-1, variant_storage<Args... > >::type type;
105 };
106 
107 template<class Head, class ... Args>
108 struct variant_storage_element<0, variant_storage<Head, Args ... > >
109 {
110  typedef Head type;
111 };
112 
113 
114 template<class ... Args>
115 struct variant_storage_size< variant_storage<Args...> > : std::integral_constant<std::size_t, sizeof ... (Args) > {};
116 
117 template<class ... Args>
118 struct variant_storage_size< const variant_storage<Args...> > : std::integral_constant<std::size_t, sizeof ... (Args) > {};
119 
120 template<class ... Args>
121 struct variant_storage_size< volatile variant_storage<Args...> > : std::integral_constant<std::size_t, sizeof ... (Args) > {};
122 
123 template<class ... Args>
124 struct variant_storage_size< const volatile variant_storage<Args...> > : std::integral_constant<std::size_t, sizeof ... (Args) > {};
125 
126 #endif //VARIANT_IN_DOXYGEN_PARSING
127 
128 template<class ... Args>
129 union literal_variant_storage;
130 
131 template<class Head, class ... Tail>
132 union literal_variant_storage<Head, Tail...>
133 {
134  constexpr literal_variant_storage() noexcept : empty{} {}
135 
136  template<std::size_t I, class ... Args>
137  constexpr literal_variant_storage( std::integral_constant<std::size_t, I>, Args && ... args)
138  noexcept( std::is_nothrow_constructible<
139  literal_variant_storage<Tail...>,
140  std::integral_constant<std::size_t, I-1>,
141  Args && ... >::value ) :
142 
143  tail( std::integral_constant<std::size_t, I-1>{},
144  std::forward<Args>(args) ... )
145  {}
146 
147  template<class ... Args>
148  constexpr literal_variant_storage( std::integral_constant<std::size_t, 0>, Args && ... args)
149  noexcept( std::is_nothrow_constructible< Head, Args && ... >::value ) :
150 
151  head( std::forward<Args>(args) ... )
152  {}
153 
154  template<std::size_t I>
155  constexpr variant_storage_element_t<I, variant_storage<Head, Tail...>> & get( std::integral_constant<std::size_t, I> ) & noexcept
156  {
157  return tail.get( std::integral_constant<std::size_t, I-1>() );
158  }
159 
160  constexpr Head& get( std::integral_constant<std::size_t, 0> ) & noexcept
161  {
162  return head;
163  }
164 
165  template<std::size_t I>
166  constexpr variant_storage_element_t<I, variant_storage<Head, Tail...>> && get( std::integral_constant<std::size_t, I> ) && noexcept
167  {
168  return std::move(tail).get( std::integral_constant<std::size_t, I-1>() );
169  }
170 
171  constexpr Head&& get( std::integral_constant<std::size_t, 0> ) && noexcept
172  {
173  return std::move(head);
174  }
175 
176  template<std::size_t I>
177  constexpr variant_storage_element_t<I, variant_storage<Head, Tail...>> const & get( std::integral_constant<std::size_t, I> ) const & noexcept
178  {
179  return tail.get( std::integral_constant<std::size_t, I-1>() );
180  }
181 
182  constexpr const Head& get( std::integral_constant<std::size_t, 0> ) const & noexcept
183  {
184  return head;
185  }
186 
187 private:
188  char empty;
189  Head head;
190  literal_variant_storage<Tail ... > tail;
191 };
192 
193 template<>
194 union literal_variant_storage<>
195 {
196 };
197 
198 template<class ... Args>
199 union nonliteral_variant_storage;
200 
201 template<class Head, class ... Tail>
202 union nonliteral_variant_storage<Head, Tail...>
203 {
204  nonliteral_variant_storage() noexcept : empty{} {}
205 
206  template<std::size_t I, class ... Args>
207  nonliteral_variant_storage( std::integral_constant<std::size_t, I>, Args && ... args)
208  noexcept( std::is_nothrow_constructible<
209  nonliteral_variant_storage<Tail ... >,
210  std::integral_constant<std::size_t, I-1>,
211  Args && ... >::value ) :
212 
213  tail( std::integral_constant<std::size_t, I-1>{},
214  std::forward<Args>(args) ... )
215  {}
216 
217  template<class ... Args>
218  nonliteral_variant_storage( std::integral_constant<std::size_t, 0>, Args && ... args)
219  noexcept( std::is_nothrow_constructible< Head, Args && ... >::value ) :
220 
221  head( std::forward<Args>(args) ... )
222  {}
223 
224  ~nonliteral_variant_storage() {}
225 
226  template<std::size_t I>
227  variant_storage_element_t<I, variant_storage<Head, Tail...>> & get( std::integral_constant<std::size_t, I> ) & noexcept
228  {
229  return tail.get( std::integral_constant<std::size_t, I-1>() );
230  }
231 
232  Head& get( std::integral_constant<std::size_t, 0> ) & noexcept
233  {
234  return head;
235  }
236 
237  template<std::size_t I>
238  variant_storage_element_t<I, variant_storage<Head, Tail...>> && get( std::integral_constant<std::size_t, I> ) && noexcept
239  {
240  return std::move(tail).get( std::integral_constant<std::size_t, I-1>() );
241  }
242 
243  Head&& get( std::integral_constant<std::size_t, 0> ) && noexcept
244  {
245  return std::move(head);
246  }
247 
248  template<std::size_t I>
249  variant_storage_element_t<I, variant_storage<Head, Tail...>> const & get( std::integral_constant<std::size_t, I> ) const & noexcept
250  {
251  return tail.get( std::integral_constant<std::size_t, I-1>() );
252  }
253 
254  const Head& get( std::integral_constant<std::size_t, 0> ) const & noexcept
255  {
256  return head;
257  }
258 
259 private:
260  char empty;
261  Head head;
262  nonliteral_variant_storage<Tail ... > tail;
263 };
264 
265 template<>
266 union nonliteral_variant_storage<>
267 {
268 };
269 
270 template<class ... Args>
271 class variant_storage
272 {
273  using storage_type = typename std::conditional<
274  detail::and_< std::is_literal_type<Args> ... >::value,
275  literal_variant_storage<Args...>,
276  nonliteral_variant_storage<Args...>
277  >::type;
278 
279 public:
280  constexpr variant_storage() noexcept(std::is_nothrow_constructible<storage_type>::value) : value{} {}
281 
282  template<std::size_t I, class ... CArgs>
283  constexpr variant_storage( std::integral_constant<std::size_t, I>, CArgs && ... args )
284  noexcept( std::is_nothrow_constructible< storage_type, std::integral_constant<std::size_t, I>, CArgs && ... >::value ) :
285 
286  value( std::integral_constant<std::size_t, I>{}, std::forward<CArgs>(args) ... )
287  {}
288 
289  template<std::size_t I, class ... OtherArgs>
290  constexpr friend variant_storage_element_t<I, variant_storage<OtherArgs...> > & get(variant_storage<OtherArgs...> & v) noexcept;
291 
292  template<std::size_t I, class ... OtherArgs>
293  constexpr friend variant_storage_element_t<I, variant_storage<OtherArgs...> > && get(variant_storage<OtherArgs...> && v) noexcept;
294 
295  template<std::size_t I, class ... OtherArgs>
296  constexpr friend variant_storage_element_t<I, variant_storage<OtherArgs...> > const & get(variant_storage<OtherArgs...> const & v) noexcept;
297 
298 private:
299  storage_type value;
300 };
301 
302 template<std::size_t I, class ... Args>
303 constexpr variant_storage_element_t<I, variant_storage<Args...> > & get(variant_storage<Args...> & v) noexcept
304 {
305  return v.value.get( std::integral_constant<std::size_t, I>() );
306 }
307 
308 template<std::size_t I, class ... Args>
309 constexpr variant_storage_element_t<I, variant_storage<Args...> > && get(variant_storage<Args...> && v) noexcept
310 {
311  return std::move(v.value).get( std::integral_constant<std::size_t, I>() );
312 }
313 
314 template<std::size_t I, class ... Args>
315 constexpr variant_storage_element_t<I, variant_storage<Args...> > const & get(variant_storage<Args...> const & v) noexcept
316 {
317  return v.value.get( std::integral_constant<std::size_t, I>() );
318 }
319 
320 class invoke_variant_storage_t
321 {
322  template<std::size_t I, class Callable, class VariantStorage>
323  static decltype(auto) invoke( Callable && c,
324  VariantStorage && v,
325  std::size_t which,
326  std::integral_constant<std::size_t, I> )
327  {
328  assert( which < I );
329  return which == (I-1) ?
330  std::forward<Callable>(c)( get<I-1>(std::forward<VariantStorage>(v)) ) :
331  invoke( std::forward<Callable>(c),
332  std::forward<VariantStorage>(v),
333  which,
334  std::integral_constant<std::size_t, I-1>{} );
335  }
336 
337  template<class Callable, class VariantStorage>
338  static decltype(auto) invoke( Callable && c, VariantStorage && v, std::size_t which, std::integral_constant<std::size_t, 1> )
339  {
340  assert( which == 0 );
341  return std::forward<Callable>(c)( get<0>(std::forward<VariantStorage>(v)) );
342  }
343 
344  template<class Callable,
345  class VariantStorage,
346  class Indices =
347  std::make_integer_sequence<
348  std::size_t,
350  >
351  >
352  struct is_nothrow_callable;
353 
354  template<class Callable, class VariantStorage, std::size_t ... Is>
355  struct is_nothrow_callable< Callable, VariantStorage, std::integer_sequence<std::size_t, Is...> > :
356  detail::and_<
357  std::integral_constant<
358  bool,
359  noexcept( std::declval<Callable&&>()( get<Is>(std::declval<VariantStorage>()) ) ) > ...
360  > {};
361 
362 public:
363  constexpr invoke_variant_storage_t() noexcept {}
364 
365  template<class VariantStorage, class Callable>
366  decltype(auto) operator()( Callable && c, VariantStorage && v, std::size_t which ) const
367  noexcept( is_nothrow_callable< Callable &&, VariantStorage && >::value )
368  {
369  return invoke( std::forward<Callable>(c),
370  std::forward<VariantStorage>(v),
371  which,
372  typename variant_storage_size<
373  typename std::remove_reference<VariantStorage>::type
374  >::type {} );
375  }
376 };
377 
385 template<class VariantStorage, class Callable>
386 decltype(auto) invoke( Callable && c, VariantStorage && v, std::size_t which )
387  noexcept(
388  noexcept(
389  invoke_variant_storage_t{}(
390  std::forward<Callable>(c),
391  std::forward<VariantStorage>(v),
392  which
393  ) ) )
394 {
395  return invoke_variant_storage_t{}(
396  std::forward<Callable>(c),
397  std::forward<VariantStorage>(v),
398  which);
399 }
400 
401 #endif // VARIANT_STORAGE_HPP
Provides access to the number of elements in a variant_storage as a compile-time constant expression...
Definition: variant_storage.hpp:82
A templated generalized union.
Definition: variant_storage.hpp:64
Defines the member type type to the I-th type of the variant_storage.
Definition: variant_storage.hpp:76
decltype(auto) invoke(Callable &&c, VariantStorage &&v, std::size_t which) noexcept(noexcept(invoke_variant_storage_t{}(std::forward< Callable >(c), std::forward< VariantStorage >(v), which)))
Calls the provided Callable with an element of the supplied variant storage.
Definition: variant_storage.hpp:386