siplasplas
variant.hpp
1 #ifndef SIPLASPLAS_VARIANT_VARIANT_HPP
2 #define SIPLASPLAS_VARIANT_VARIANT_HPP
3 
4 #include <type_traits>
5 #include <ctti/type_id.hpp>
6 #include <ostream>
7 #include <sstream>
8 
9 #include "visitor.hpp"
10 
11 namespace cpp
12 {
13  template<typename... Ts>
14  class Variant
15  {
16  private:
17  std::string print_variant(const ctti::unnamed_type_id_t& id) const
18  {
19  std::ostringstream os;
20 
21  if(id != ctti::unnamed_type_id<void>())
22  os << "Variant{" << id.hash() << "}";
23  else
24  os << "Variant{[Empty]}";
25 
26  return os.str();
27  }
28  void Throw(const std::string& operation, const ctti::unnamed_type_id_t& tag)
29  {
30  throw std::runtime_error{print_variant(this->tag()) +
31  ": Cannot " + operation + " " + print_variant(tag)};
32  }
33 
34  template<typename...>
35  struct typelist {};
36 
37  template<typename>
38  struct VariantExecutorImpl;
39 
40  template<typename... Args>
41  using VariantExecutor = VariantExecutorImpl<typelist<Args...>>;
42 
43  template<typename Head, typename... Tail>
44  using NextVariantExecutor = VariantExecutorImpl<typelist<Tail...>>;
45 
46  template<typename Head>
47  struct VariantExecutorImpl<typelist<Head>>
48  {
49  template<typename T>
50  struct has_type_static : public std::is_same<T, Head>
51  {};
52 
53  static constexpr bool has_type(ctti::unnamed_type_id_t id)
54  {
55  return id == ctti::unnamed_type_id<Head>();
56  }
57 
58  template<typename... Args>
59  static bool construct(Variant& v, Args&&... args)
60  {
61  if(v.tag() == ctti::unnamed_type_id<Head>())
62  {
63  new (v.rawStorage()) Head(std::forward<Args>(args)...);
64  return !v.empty();
65  }
66  else
67  {
68  return false;
69  }
70  }
71 
72  static bool destroy(Variant& v)
73  {
74  // Take care of empty variants, destroying them is fine
75  // (Actually a nop)
76  if(v.empty())
77  return true;
78 
79  if(v.tag() == ctti::unnamed_type_id<Head>())
80  {
81  destructor_call<Head>::apply(v.storageAs<Head>());
82  return true;
83  }
84  else
85  {
86  return false;
87  }
88  }
89 
90  template<typename T, bool isClass = std::is_class<T>::value>
91  struct destructor_call
92  {
93  static void apply(T* obj){}
94  };
95 
96  template<typename T>
97  struct destructor_call<T, true>
98  {
99  static void apply(T* obj)
100  {
101  obj->~T();
102  }
103  };
104 
105  static bool move(Variant& v, Variant&& other)
106  {
107  if(other.tag() == ctti::unnamed_type_id<Head>())
108  {
109  new(v.rawStorage()) Head(std::move(other.get<Head>()));
110 
111  v.tag() = other.tag();
112  other.tag() = ctti::unnamed_type_id<void>();
113 
114  return !v.empty() && other.empty();
115  }
116  else
117  {
118  return false;
119  }
120  }
121 
122  static bool copy(Variant& v, const Variant& other)
123  {
124  if(other.tag() == ctti::unnamed_type_id<Head>())
125  {
126  new(v.rawStorage()) Head(other.get<Head>());
127  v.tag() = other.tag();
128 
129  return !v.empty();
130  }
131  else
132  {
133  return false;
134  }
135  }
136 
137  static bool move_assign(Variant& v, Variant&& other)
138  {
139  if(other.tag() == ctti::unnamed_type_id<Head>())
140  {
141  if(v.tag() == other.tag())
142  {
143  v.get<Head>() = std::move(other.get<Head>());
144  return true;
145  }
146  else
147  {
148  if(VariantExecutor<Ts...>::destroy(v))
149  {
150  new (v.rawStorage()) Head( std::move(other.get<Head>()) );
151  v.tag() = ctti::unnamed_type_id<Head>();
152  other.tag() = ctti::unnamed_type_id<void>();
153 
154  return !v.empty() && other.empty();
155  }
156  else
157  {
158  return false;
159  }
160  }
161  }
162  else
163  {
164  return false;
165  }
166  }
167 
168  static bool copy_assign(Variant& v, const Variant& other)
169  {
170  if(other.tag() == ctti::unnamed_type_id<Head>())
171  {
172  if(v.tag() == other.tag())
173  {
174  v.get<Head>() = other.get<Head>();
175  return true;
176  }
177  else
178  {
179  if(VariantExecutor<Ts...>::destroy(v))
180  {
181  return VariantExecutor<Ts...>::copy(v, other);
182  }
183  else
184  {
185  return false;
186  }
187  }
188  }
189  else
190  {
191  return false;
192  }
193  }
194 
195  template<typename F>
196  static typename F::ResultType const_visit(const Variant& v, F f)
197  {
198  if(v.tag() == ctti::unnamed_type_id<Head>())
199  {
200  return f(v.get<Head>());
201  }
202  else
203  {
204  throw std::runtime_error{"Cannot visit " + v.print_variant(v.tag())};
205  }
206  }
207 
208  template<typename F>
209  static typename F::ResultType visit(Variant& v, F f)
210  {
211  if(v.tag() == ctti::unnamed_type_id<Head>())
212  {
213  return f(v.get<Head>());
214  }
215  else
216  {
217  throw std::runtime_error{ "Cannot visit " + v.print_variant(v.tag())};
218  }
219  }
220  };
221 
222  template<typename Head, typename Second, typename... Tail>
223  struct VariantExecutorImpl<typelist<Head, Second, Tail...>>
224  {
225  template<typename T>
226  struct has_type_static : public std::integral_constant<bool,
227  std::is_same<T, Head>::value || VariantExecutor<Second, Tail...>::template has_type_static<T>::value
228  >
229  {};
230 
231  static constexpr bool has_type(ctti::unnamed_type_id_t id)
232  {
233  return VariantExecutor<Head>::has_type(id) && VariantExecutor<Second, Tail...>::has_type(id);
234  }
235 
236  template<typename... Args>
237  static bool construct(Variant& v, Args&&... args)
238  {
239  return VariantExecutor<Head>::construct(v, std::forward<Args>(args)...) ||
240  VariantExecutor<Second, Tail...>::construct(v, std::forward<Args>(args)...);
241  }
242 
243  static bool destroy(Variant& v)
244  {
246  }
247 
248  static bool move(Variant& v, Variant&& other)
249  {
250  return VariantExecutor<Head>::move(v, std::move(other)) ||
251  VariantExecutor<Second, Tail...>::move(v, std::move(other));
252  }
253 
254  static bool copy(Variant& v, const Variant& other)
255  {
256  return VariantExecutor<Head>::copy(v, other) ||
257  VariantExecutor<Second, Tail...>::copy(v, other);
258  }
259 
260  static bool move_assign(Variant& v, Variant&& other)
261  {
262  return VariantExecutor<Head>::move_assign(v, std::move(other)) ||
263  VariantExecutor<Second, Tail...>::move_assign(v, std::move(other));
264  }
265 
266  static bool copy_assign(Variant& v, const Variant& other)
267  {
268  return VariantExecutor<Head>::copy_assign(v, other) ||
269  VariantExecutor<Second, Tail...>::copy_assign(v, other);
270  }
271 
272  template<typename F>
273  static typename F::ResultType const_visit(const Variant& v, F f)
274  {
275  if(v.tag() == ctti::unnamed_type_id<Head>())
276  {
277  return f(v.get<Head>());
278  }
279  else
280  {
281  return NextVariantExecutor<Head, Second, Tail...>::const_visit(v, f);
282  }
283  }
284 
285  template<typename F>
286  static typename F::ResultType visit(Variant& v, F f)
287  {
288  if(v.tag() == ctti::unnamed_type_id<Head>())
289  {
290  return f(v.get<Head>());
291  }
292  else
293  {
294  return NextVariantExecutor<Head, Second, Tail...>::visit(v, f);
295  }
296  }
297  };
298 
299  public:
300  using tag_t = ctti::unnamed_type_id_t;
301 
302  const tag_t& tag() const
303  {
304  return _tag;
305  }
306 
307  tag_t& tag()
308  {
309  return _tag;
310  }
311 
312  std::string to_string() const
313  {
314  return print_variant(tag());
315  }
316 
317  bool empty() const
318  {
319  return _tag == ctti::unnamed_type_id<void>();
320  }
321 
322  explicit operator bool() const
323  {
324  return !empty();
325  }
326 
327  void clear()
328  {
330  _tag = ctti::unnamed_type_id<void>();
331  }
332 
333  template<typename T>
334  const T& get() const
335  {
336  return *storageAs<T>();
337  }
338 
339  template<typename T>
340  T& get()
341  {
342  return *storageAs<T>();
343  }
344 
345  Variant() = default;
346 
347  Variant(const Variant& other)
348  {
349  auto tag = other.tag();
350 
351  if(!other.empty())
352  {
353  if(!VariantExecutor<Ts...>::copy(*this, other))
354  Throw("copy construct from", tag);
355  }
356  }
357 
358  Variant(Variant&& other) :
359  _tag{other.tag()}
360  {
361  auto tag = other.tag();
362 
363  if(!other.empty())
364  {
365  if(!VariantExecutor<Ts...>::move(*this, std::move(other)))
366  Throw("move-construct from", tag);
367  }
368  }
369 
370  template<typename T, typename = std::enable_if_t<VariantExecutor<Ts...>::template has_type_static<std::decay_t<T>>::value>>
371  Variant(T&& value) :
372  _tag{ctti::unnamed_type_id<typename std::decay<T>::type>()}
373  {
374  if(VariantExecutor<Ts...>::template has_type_static<std::decay_t<T>>::value)
375  new (rawStorage()) std::decay_t<T>{std::forward<T>(value)};
376  else
377  Throw("construct from", _tag);
378  }
379 
380  ~Variant()
381  {
382  auto tag = this->tag();
383 
384  if(!empty())
385  {
386  if(!VariantExecutor<Ts...>::destroy(*this))
387  Throw("destroy", tag);
388  }
389  }
390 
391  template<typename T, typename = std::enable_if_t<VariantExecutor<Ts...>::template has_type_static<std::decay_t<T>>::value>>
392  Variant& operator=(T&& value)
393  {
394  Variant variant{std::forward<T>(value)};
395  return (*this) = std::move(variant);
396  }
397 
398  Variant& operator=(const Variant& other)
399  {
400  auto tag = other.tag();
401 
402  if(other.empty())
403  clear();
404  else
405  if(!VariantExecutor<Ts...>::copy_assign(*this, other))
406  Throw("copy-assign from", tag);
407 
408  return *this;
409  }
410 
411  Variant& operator=(Variant&& other)
412  {
413  auto tag = other.tag();
414 
415  if(other.empty())
416  {
417  clear();
418  }
419  else
420  {
421  if(!VariantExecutor<Ts...>::move_assign(*this, std::move(other)))
422  Throw("move-assign from", tag);
423  }
424 
425  return *this;
426  }
427 
428  template<typename Result, typename... Fs>
429  Result visit(Fs... fs)
430  {
431  return VariantExecutor<Ts...>::visit(*this, ::cpp::visitor<Result>(fs...));
432  }
433 
434  template<typename Result, typename... Fs>
435  Result visit(Fs... fs) const
436  {
437  return VariantExecutor<Ts...>::const_visit(*this, ::cpp::visitor<Result>(fs...));
438  }
439 
440  template<typename F>
441  typename F::ResultType visit(F f)
442  {
443  return VariantExecutor<Ts...>::visit(*this, f);
444  }
445 
446  template<typename F>
447  typename F::ResultType visit(F f) const
448  {
449  return VariantExecutor<Ts...>::const_visit(*this, f);
450  }
451 
452  friend bool operator!=(const Variant& lhs, const Variant& rhs)
453  {
454  return !(lhs == rhs);
455  }
456 
457  friend std::ostream& operator<<(std::ostream& os, const Variant& variant)
458  {
459  return variant.visit<std::ostream&>(
460  [&os](const auto& value) -> std::ostream&
461  {
462  return os << value;
463  }
464  );
465  }
466 
467  private:
468  using storage_t = typename std::aligned_union<0, Ts...>::type;
469 
470  tag_t _tag = ctti::unnamed_type_id<void>();
471  storage_t _storage;
472 
473  void* rawStorage()
474  {
475  return std::addressof(_storage);
476  }
477 
478  const void* rawStorage() const
479  {
480  return std::addressof(_storage);
481  }
482 
483  template<typename T>
484  T* storageAs()
485  {
486  return reinterpret_cast<T*>(rawStorage());
487  }
488 
489  template<typename T>
490  const T* storageAs() const
491  {
492  return reinterpret_cast<const T*>(rawStorage());
493  }
494  };
495 }
496 
497 #include <functional>
498 
499 // std::hash specialization
500 namespace std
501 {
502  template<typename... Ts>
503  struct hash<::cpp::Variant<Ts...>>
504  {
505  std::size_t operator()(const ::cpp::Variant<Ts...>& v) const
506  {
507  return v.template visit<std::size_t>(
508  [](const auto& elem)
509  {
510  return std::hash<std::decay_t<decltype(elem)>>{}(elem);
511  }
512  );
513  }
514  };
515 }
516 #endif // SIPLASPLAS_VARIANT_VARIANT_HPP
Definition: meta.hpp:31
Definition: messaging.hpp:8
Definition: variant.hpp:500
void destroy(T &object)
Invokes the destructor of an object.
Definition: destroy.hpp:16
void construct(void *where, Args &&... args)
constructs an object of type T on the specified address
Definition: destroy.hpp:56
Definition: variant.hpp:14
constexpr std::size_t hash(const T &value)
Implements a hash function for values of type T.
Definition: hash.hpp:180