1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4 
5 #pragma once
6 
7 #include "caf/detail/type_list.hpp"
8 #include "caf/detail/type_traits.hpp"
9 #include "caf/sum_type_access.hpp"
10 #include "caf/sum_type_token.hpp"
11 
12 namespace caf {
13 
14 /// @defgroup SumType Sum Types
15 /// Opt-in sum type concept for `variant`-style types.
16 /// @{
17 
18 /// Concept for checking whether `T` supports the sum type API by specializing
19 /// `sum_type_access`.
20 template <class T>
SumType()21 constexpr bool SumType() {
22   return has_sum_type_access<typename std::decay<T>::type>::value;
23 }
24 
25 /// Concept for checking whether all `Ts` support the sum type API by
26 /// specializing `sum_type_access`.
27 template <class... Ts>
SumTypes()28 constexpr bool SumTypes() {
29   using namespace detail;
30   using types = type_list<decay_t<Ts>...>;
31   return tl_forall<types, has_sum_type_access>::value;
32 }
33 
34 template <class Trait, class T, bool = Trait::specialized>
35 struct sum_type_index {
36   static constexpr int value = -1;
37 };
38 
39 template <class Trait, class T>
40 struct sum_type_index<Trait, T, true> {
41   static constexpr int value =
42     detail::tl_index_of<typename Trait::types, T>::value;
43 };
44 
45 template <class Trait, class T>
46 constexpr sum_type_token<T, sum_type_index<Trait, T>::value>
make_sum_type_token()47 make_sum_type_token() {
48   return {};
49 }
50 
51 
52 /// Returns a reference to the value of a sum type.
53 /// @pre `holds_alternative<T>(x)`
54 template <class T, class U, class Trait = sum_type_access<U>>
get(U & x)55 auto get(U& x) -> decltype(Trait::get(x, make_sum_type_token<Trait, T>())) {
56   return Trait::get(x, make_sum_type_token<Trait, T>());
57 }
58 
59 /// Returns a reference to the value of a sum type.
60 /// @pre `holds_alternative<T>(x)`
61 template <class T, class U, class Trait = sum_type_access<U>>
get(const U & x)62 auto get(const U& x)
63 -> decltype(Trait::get(x, make_sum_type_token<Trait, T>())) {
64   return Trait::get(x, make_sum_type_token<Trait, T>());
65 }
66 
67 /// Returns a pointer to the value of a sum type if it is of type `T`,
68 /// `nullptr` otherwise.
69 template <class T, class U, class Trait = sum_type_access<U>>
get_if(U * x)70 auto get_if(U* x)
71 -> decltype(Trait::get_if(x, make_sum_type_token<Trait, T>())) {
72   return Trait::get_if(x, make_sum_type_token<Trait, T>());
73 }
74 
75 /// Returns a pointer to the value of a sum type if it is of type `T`,
76 /// `nullptr` otherwise.
77 template <class T, class U, class Trait = sum_type_access<U>>
get_if(const U * x)78 auto get_if(const U* x)
79 -> decltype(Trait::get_if(x, make_sum_type_token<Trait, T>())) {
80   return Trait::get_if(x, make_sum_type_token<Trait, T>());
81 }
82 
83 /// Returns whether a sum type has a value of type `T`.
84 template <class T, class U>
holds_alternative(const U & x)85 bool holds_alternative(const U& x) {
86   using namespace detail;
87   using trait = sum_type_access<U>;
88   return trait::is(x, make_sum_type_token<trait, T>());
89 }
90 
91 template <bool Valid, class F, class... Ts>
92 struct sum_type_visit_result_impl {
93   using type = decltype((std::declval<F&>())(
94     std::declval<typename sum_type_access<Ts>::type0&>()...));
95 };
96 
97 template <class F, class... Ts>
98 struct sum_type_visit_result_impl<false, F, Ts...> {};
99 
100 template <class F, class... Ts>
101 struct sum_type_visit_result
102     : sum_type_visit_result_impl<
103         detail::conjunction<SumType<Ts>()...>::value, F, Ts...> {};
104 
105 template <class F, class... Ts>
106 using sum_type_visit_result_t =
107   typename sum_type_visit_result<detail::decay_t<F>,
108                                  detail::decay_t<Ts>...>::type;
109 
110 template <class Result, size_t I, class Visitor>
111 struct visit_impl_continuation;
112 
113 template <class Result, size_t I>
114 struct visit_impl {
115   template <class Visitor, class T, class... Ts>
applycaf::visit_impl116   static Result apply(Visitor&& f, T&& x, Ts&&... xs) {
117     visit_impl_continuation<Result, I - 1, Visitor> continuation{f};
118     using trait = sum_type_access<detail::decay_t<T>>;
119     return trait::template apply<Result>(x, continuation,
120                                          std::forward<Ts>(xs)...);
121   }
122 };
123 
124 template <class Result>
125 struct visit_impl<Result, 0> {
126   template <class Visitor, class... Ts>
applycaf::visit_impl127   static Result apply(Visitor&& f, Ts&&... xs) {
128     return f(std::forward<Ts>(xs)...);
129   }
130 };
131 
132 
133 template <class Result, size_t I, class Visitor>
134 struct visit_impl_continuation {
135   Visitor& f;
136   template <class... Ts>
operator ()caf::visit_impl_continuation137   Result operator()(Ts&&... xs) {
138     return visit_impl<Result, I>::apply(f, std::forward<Ts>(xs)...);
139   }
140 };
141 
142 /// Applies the values of any number of sum types to the visitor.
143 template <class Visitor, class T, class... Ts,
144           class Result = sum_type_visit_result_t<Visitor, T, Ts...>>
145 detail::enable_if_t<SumTypes<T, Ts...>(), Result>
visit(Visitor && f,T && x,Ts &&...xs)146 visit(Visitor&& f, T&& x, Ts&&... xs) {
147   return visit_impl<Result, sizeof...(Ts) + 1>::apply(std::forward<Visitor>(f),
148                                                       std::forward<T>(x),
149                                                       std::forward<Ts>(xs)...);
150 }
151 
152 /// @}
153 
154 } // namespace caf
155