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