1 #ifndef SIPLASPLAS_VARIANT_VARIANT_HPP 2 #define SIPLASPLAS_VARIANT_VARIANT_HPP 5 #include <ctti/type_id.hpp> 13 template<
typename... Ts>
17 std::string print_variant(
const ctti::unnamed_type_id_t&
id)
const 19 std::ostringstream os;
21 if(
id != ctti::unnamed_type_id<void>())
22 os <<
"Variant{" <<
id.hash() <<
"}";
24 os <<
"Variant{[Empty]}";
28 void Throw(
const std::string& operation,
const ctti::unnamed_type_id_t& tag)
30 throw std::runtime_error{print_variant(this->tag()) +
31 ": Cannot " + operation +
" " + print_variant(tag)};
38 struct VariantExecutorImpl;
40 template<
typename... Args>
41 using VariantExecutor = VariantExecutorImpl<typelist<Args...>>;
43 template<
typename Head,
typename... Tail>
44 using NextVariantExecutor = VariantExecutorImpl<typelist<Tail...>>;
46 template<
typename Head>
47 struct VariantExecutorImpl<typelist<Head>>
50 struct has_type_static :
public std::is_same<T, Head>
53 static constexpr
bool has_type(ctti::unnamed_type_id_t
id)
55 return id == ctti::unnamed_type_id<Head>();
58 template<
typename... Args>
61 if(v.tag() == ctti::unnamed_type_id<Head>())
63 new (v.rawStorage()) Head(std::forward<Args>(args)...);
79 if(v.tag() == ctti::unnamed_type_id<Head>())
81 destructor_call<Head>::apply(v.storageAs<Head>());
90 template<typename T, bool isClass = std::is_class<T>::value>
91 struct destructor_call
93 static void apply(T* obj){}
97 struct destructor_call<T, true>
99 static void apply(T* obj)
107 if(other.tag() == ctti::unnamed_type_id<Head>())
109 new(v.rawStorage()) Head(std::move(other.get<Head>()));
111 v.tag() = other.tag();
112 other.tag() = ctti::unnamed_type_id<void>();
114 return !v.empty() && other.empty();
124 if(other.tag() == ctti::unnamed_type_id<Head>())
126 new(v.rawStorage()) Head(other.get<Head>());
127 v.tag() = other.tag();
139 if(other.tag() == ctti::unnamed_type_id<Head>())
141 if(v.tag() == other.tag())
143 v.get<Head>() = std::move(other.get<Head>());
148 if(VariantExecutor<Ts...>::destroy(v))
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>();
154 return !v.empty() && other.empty();
170 if(other.tag() == ctti::unnamed_type_id<Head>())
172 if(v.tag() == other.tag())
174 v.get<Head>() = other.get<Head>();
179 if(VariantExecutor<Ts...>::destroy(v))
181 return VariantExecutor<Ts...>::copy(v, other);
196 static typename F::ResultType const_visit(
const Variant& v, F f)
198 if(v.tag() == ctti::unnamed_type_id<Head>())
200 return f(v.get<Head>());
204 throw std::runtime_error{
"Cannot visit " + v.print_variant(v.tag())};
209 static typename F::ResultType visit(
Variant& v, F f)
211 if(v.tag() == ctti::unnamed_type_id<Head>())
213 return f(v.get<Head>());
217 throw std::runtime_error{
"Cannot visit " + v.print_variant(v.tag())};
222 template<
typename Head,
typename Second,
typename... Tail>
223 struct VariantExecutorImpl<typelist<Head, Second, Tail...>>
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
231 static constexpr
bool has_type(ctti::unnamed_type_id_t
id)
233 return VariantExecutor<Head>::has_type(
id) && VariantExecutor<Second, Tail...>::has_type(
id);
236 template<
typename... Args>
250 return VariantExecutor<Head>::move(v, std::move(other)) ||
251 VariantExecutor<Second, Tail...>::move(v, std::move(other));
256 return VariantExecutor<Head>::copy(v, other) ||
257 VariantExecutor<Second, Tail...>::copy(v, other);
262 return VariantExecutor<Head>::move_assign(v, std::move(other)) ||
263 VariantExecutor<Second, Tail...>::move_assign(v, std::move(other));
268 return VariantExecutor<Head>::copy_assign(v, other) ||
269 VariantExecutor<Second, Tail...>::copy_assign(v, other);
273 static typename F::ResultType const_visit(
const Variant& v, F f)
275 if(v.tag() == ctti::unnamed_type_id<Head>())
277 return f(v.get<Head>());
281 return NextVariantExecutor<Head, Second, Tail...>::const_visit(v, f);
286 static typename F::ResultType visit(
Variant& v, F f)
288 if(v.tag() == ctti::unnamed_type_id<Head>())
290 return f(v.get<Head>());
294 return NextVariantExecutor<Head, Second, Tail...>::visit(v, f);
300 using tag_t = ctti::unnamed_type_id_t;
302 const tag_t& tag()
const 312 std::string to_string()
const 314 return print_variant(tag());
319 return _tag == ctti::unnamed_type_id<void>();
322 explicit operator bool()
const 330 _tag = ctti::unnamed_type_id<void>();
336 return *storageAs<T>();
342 return *storageAs<T>();
349 auto tag = other.tag();
353 if(!VariantExecutor<Ts...>::copy(*
this, other))
354 Throw(
"copy construct from", tag);
361 auto tag = other.tag();
365 if(!VariantExecutor<Ts...>::move(*
this, std::move(other)))
366 Throw(
"move-construct from", tag);
370 template<
typename T,
typename = std::enable_if_t<VariantExecutor<Ts...>::template has_type_static<std::decay_t<T>>::value>>
372 _tag{ctti::unnamed_type_id<typename std::decay<T>::type>()}
374 if(VariantExecutor<Ts...>::template has_type_static<std::decay_t<T>>::value)
375 new (rawStorage()) std::decay_t<T>{std::forward<T>(value)};
377 Throw(
"construct from", _tag);
382 auto tag = this->tag();
386 if(!VariantExecutor<Ts...>::destroy(*
this))
387 Throw(
"destroy", tag);
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)
394 Variant variant{std::forward<T>(value)};
395 return (*
this) = std::move(variant);
398 Variant& operator=(
const Variant& other)
400 auto tag = other.tag();
405 if(!VariantExecutor<Ts...>::copy_assign(*
this, other))
406 Throw(
"copy-assign from", tag);
411 Variant& operator=(Variant&& other)
413 auto tag = other.tag();
421 if(!VariantExecutor<Ts...>::move_assign(*
this, std::move(other)))
422 Throw(
"move-assign from", tag);
428 template<
typename Result,
typename... Fs>
429 Result visit(Fs... fs)
431 return VariantExecutor<Ts...>::visit(*
this, ::cpp::visitor<Result>(fs...));
434 template<
typename Result,
typename... Fs>
435 Result visit(Fs... fs)
const 437 return VariantExecutor<Ts...>::const_visit(*
this, ::cpp::visitor<Result>(fs...));
441 typename F::ResultType visit(F f)
443 return VariantExecutor<Ts...>::visit(*
this, f);
447 typename F::ResultType visit(F f)
const 449 return VariantExecutor<Ts...>::const_visit(*
this, f);
452 friend bool operator!=(
const Variant& lhs,
const Variant& rhs)
454 return !(lhs == rhs);
457 friend std::ostream& operator<<(std::ostream& os,
const Variant& variant)
459 return variant.visit<std::ostream&>(
460 [&os](
const auto& value) -> std::ostream&
468 using storage_t =
typename std::aligned_union<0, Ts...>
::type;
470 tag_t _tag = ctti::unnamed_type_id<void>();
475 return std::addressof(_storage);
478 const void* rawStorage()
const 480 return std::addressof(_storage);
486 return reinterpret_cast<T*
>(rawStorage());
490 const T* storageAs()
const 492 return reinterpret_cast<const T*
>(rawStorage());
497 #include <functional> 502 template<
typename... Ts>
505 std::size_t operator()(const ::cpp::Variant<Ts...>& v)
const 507 return v.template visit<std::size_t>(
510 return std::hash<std::decay_t<decltype(elem)>>{}(elem);
516 #endif // SIPLASPLAS_VARIANT_VARIANT_HPP
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