]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=4 smarttab | |
3 | /* | |
4 | * Copied from: | |
5 | * https://github.com/exclipy/inline_variant_visitor/blob/master/inline_variant.hpp | |
6 | */ | |
7 | ||
8 | #ifndef INLINE_VARIANT_H | |
9 | #define INLINE_VARIANT_H | |
10 | ||
7c673cae | 11 | #include <boost/function_types/function_arity.hpp> |
7c673cae | 12 | #include <boost/fusion/algorithm/transformation/transform.hpp> |
7c673cae | 13 | #include <boost/mpl/contains.hpp> |
7c673cae | 14 | #include <boost/mpl/map.hpp> |
7c673cae FG |
15 | #include <boost/mpl/vector.hpp> |
16 | #include <boost/mpl/range_c.hpp> | |
11fdf7f2 | 17 | #include <boost/noncopyable.hpp> |
7c673cae FG |
18 | |
19 | #include "function_signature.h" | |
20 | ||
21 | namespace detail { | |
22 | ||
23 | // A metafunction class for getting the argument type from a unary function or functor type | |
24 | struct function_arg_extractor | |
25 | { | |
26 | // Function is either a function type like void(int const&), or a functor - eg. a class with void operator(int) | |
27 | // Sets type to the argument type with the constness and referenceness stripped (eg. int) | |
28 | template <typename Function> | |
29 | struct apply | |
30 | { | |
31 | private: | |
32 | typedef typename boost::remove_const< typename boost::remove_reference<Function>::type >::type bare_type; | |
33 | typedef typename signature_of<bare_type>::type normalized_function_type; | |
34 | typedef typename boost::function_types::function_arity<normalized_function_type>::type arity; | |
35 | typedef typename boost::function_types::parameter_types<normalized_function_type>::type parameter_types; | |
36 | typedef typename boost::function_types::result_type<normalized_function_type>::type result_type; | |
37 | ||
38 | BOOST_STATIC_ASSERT_MSG((arity::value == 1), "make_visitor called with a non-unary function"); | |
39 | ||
40 | typedef typename boost::mpl::front<parameter_types>::type parameter_type; | |
41 | public: | |
42 | typedef typename boost::remove_const< typename boost::remove_reference<parameter_type>::type >::type type; | |
43 | }; | |
44 | }; | |
45 | ||
46 | struct make_pair | |
47 | { | |
48 | template <typename AType, typename Ind> | |
49 | struct apply { | |
50 | typedef boost::mpl::pair<AType, Ind> type; | |
51 | }; | |
52 | }; | |
53 | ||
54 | // A metafunction class that asserts the second argument is in Allowed, and returns void | |
55 | template<typename Allowed> | |
56 | struct check_in | |
57 | { | |
58 | template <typename Type1, typename Type2> | |
59 | struct apply | |
60 | { | |
61 | private: | |
62 | BOOST_STATIC_ASSERT_MSG((boost::mpl::contains<Allowed, typename boost::mpl::first<Type2>::type>::value), | |
63 | "make_visitor called with spurious handler functions"); | |
64 | public: | |
65 | typedef void type; | |
66 | }; | |
67 | }; | |
68 | ||
69 | template <typename Seq> | |
70 | struct as_map | |
71 | { | |
72 | private: | |
73 | struct insert_helper { | |
74 | template <typename M, typename P> | |
75 | struct apply | |
76 | { | |
77 | typedef typename boost::mpl::insert< | |
78 | M, | |
79 | P>::type type; | |
80 | }; | |
81 | }; | |
82 | public: | |
83 | typedef typename boost::mpl::fold<Seq, boost::mpl::map0<>, insert_helper>::type type; | |
84 | }; | |
85 | ||
86 | // A functor template suitable for passing into apply_visitor. The constructor accepts the list of handler functions, | |
87 | // which are then exposed through a set of operator()s | |
88 | template <typename Result, typename Variant, typename... Functions> | |
89 | struct generic_visitor : boost::static_visitor<Result>, boost::noncopyable | |
90 | { | |
91 | private: | |
92 | typedef generic_visitor<Result, Variant, Functions...> type; | |
93 | ||
94 | // Compute the function_map type | |
95 | typedef boost::mpl::vector<Functions...> function_types; | |
96 | typedef typename boost::mpl::transform<function_types, function_arg_extractor>::type arg_types; | |
97 | typedef typename boost::mpl::transform< | |
98 | arg_types, | |
99 | boost::mpl::range_c<int, 0, boost::mpl::size<arg_types>::value>, | |
100 | make_pair | |
101 | >::type pair_list; | |
102 | typedef typename as_map<pair_list>::type fmap; | |
103 | ||
104 | // Check that the argument types are unique | |
105 | BOOST_STATIC_ASSERT_MSG((boost::mpl::size<fmap>::value == boost::mpl::size<arg_types>::value), | |
106 | "make_visitor called with non-unique argument types for handler functions"); | |
107 | ||
108 | // Check that there aren't any argument types not in the variant types | |
109 | typedef typename boost::mpl::fold<fmap, void, check_in<typename Variant::types> >::type dummy; | |
110 | ||
111 | boost::fusion::vector<Functions...> fvec; | |
112 | ||
113 | ||
114 | template <typename T> | |
115 | Result apply_helper(const T& object, boost::mpl::true_) const { | |
116 | typedef typename boost::mpl::at<fmap, T>::type Ind; | |
117 | return boost::fusion::at<Ind>(fvec)(object); | |
118 | } | |
119 | ||
120 | template <typename T> | |
121 | Result apply_helper(const T& object, boost::mpl::false_) const { | |
122 | return Result(); | |
123 | } | |
124 | ||
125 | BOOST_MOVABLE_BUT_NOT_COPYABLE(generic_visitor) | |
126 | ||
127 | public: | |
128 | generic_visitor(BOOST_RV_REF(type) other) | |
129 | : | |
130 | fvec(boost::move(other.fvec)) | |
131 | { | |
132 | } | |
133 | generic_visitor(Functions&&... functions) | |
134 | : | |
135 | fvec(std::forward<Functions>(functions)...) | |
136 | { | |
137 | } | |
138 | ||
139 | template <typename T> | |
140 | Result operator()(const T& object) const { | |
141 | typedef typename boost::mpl::has_key<fmap, T>::type correct_key; | |
142 | BOOST_STATIC_ASSERT_MSG(correct_key::value, | |
143 | "make_visitor called without specifying handlers for all required types"); | |
144 | return apply_helper(object, correct_key()); | |
145 | } | |
146 | }; | |
147 | ||
148 | // A metafunction class for getting the return type of a function | |
149 | struct function_return_extractor | |
150 | { | |
151 | template <typename Function> | |
152 | struct apply : boost::function_types::result_type<typename signature_of<Function>::type> | |
153 | { | |
154 | }; | |
155 | }; | |
156 | ||
157 | // A metafunction class that asserts the two arguments are the same and returns the first one | |
158 | struct check_same | |
159 | { | |
160 | template <typename Type1, typename Type2> | |
161 | struct apply | |
162 | { | |
163 | private: | |
164 | BOOST_STATIC_ASSERT_MSG((boost::is_same<Type1, Type2>::value), | |
165 | "make_visitor called with functions of differing return types"); | |
166 | public: | |
167 | typedef Type1 type; | |
168 | }; | |
169 | }; | |
170 | ||
171 | // A metafunction for getting the required generic_visitor type for the set of Functions | |
172 | template <typename Variant, typename... Functions> | |
173 | struct get_generic_visitor | |
174 | { | |
175 | private: | |
176 | typedef boost::mpl::vector<Functions...> function_types; | |
177 | typedef typename boost::mpl::transform< | |
178 | function_types, | |
179 | boost::remove_const< boost::remove_reference<boost::mpl::_1> > | |
180 | >::type bare_function_types; | |
181 | typedef typename boost::mpl::transform<bare_function_types, function_return_extractor>::type return_types; | |
182 | ||
183 | public: | |
184 | // Set result_type to the return type of the first function | |
185 | typedef typename boost::mpl::front<return_types>::type result_type; | |
186 | typedef generic_visitor<result_type, Variant, Functions...> type; | |
187 | ||
188 | private: | |
189 | // Assert that every return type is the same as the first one | |
190 | typedef typename boost::mpl::fold<return_types, result_type, check_same>::type dummy; | |
191 | }; | |
192 | ||
193 | // Accepts a set of functions and returns an object suitable for apply_visitor | |
194 | template <typename Variant, typename... Functions> | |
195 | auto make_visitor(BOOST_RV_REF(Functions)... functions) | |
196 | -> typename detail::get_generic_visitor<Variant, Functions...>::type | |
197 | { | |
198 | return typename detail::get_generic_visitor<Variant, Functions...>::type(boost::forward<Functions>(functions)...); | |
199 | } | |
200 | ||
201 | } | |
202 | ||
203 | template <typename Variant, typename... Functions> | |
204 | auto match(Variant const& variant, BOOST_RV_REF(Functions)... functions) | |
205 | -> typename detail::get_generic_visitor<Variant, Functions...>::result_type | |
206 | { | |
207 | return boost::apply_visitor(detail::make_visitor<Variant>( | |
208 | boost::forward<Functions>(functions)...), variant); | |
209 | } | |
210 | ||
211 | #endif |