1 // -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=4 smarttab
5 * https://github.com/exclipy/inline_variant_visitor/blob/master/inline_variant.hpp
8 #ifndef INLINE_VARIANT_H
9 #define INLINE_VARIANT_H
11 #include <boost/utility/enable_if.hpp>
12 #include <boost/function_types/function_arity.hpp>
13 #include <boost/function_types/parameter_types.hpp>
14 #include <boost/function_types/result_type.hpp>
15 #include <boost/fusion/include/at_key.hpp>
16 #include <boost/fusion/include/has_key.hpp>
17 #include <boost/fusion/include/make_vector.hpp>
18 #include <boost/fusion/include/map.hpp>
19 #include <boost/fusion/algorithm/transformation/transform.hpp>
20 #include <boost/move/move.hpp>
21 #include <boost/mpl/contains.hpp>
22 #include <boost/mpl/equal_to.hpp>
23 #include <boost/mpl/front.hpp>
24 #include <boost/mpl/pop_front.hpp>
25 #include <boost/mpl/map.hpp>
26 #include <boost/mpl/void.hpp>
27 #include <boost/mpl/size.hpp>
28 #include <boost/mpl/transform.hpp>
29 #include <boost/mpl/unpack_args.hpp>
30 #include <boost/mpl/vector.hpp>
31 #include <boost/mpl/range_c.hpp>
32 #include <boost/static_assert.hpp>
33 #include <boost/type_traits/is_same.hpp>
34 #include <boost/type_traits/remove_reference.hpp>
35 #include <boost/utility.hpp>
37 #include "function_signature.h"
41 // A metafunction class for getting the argument type from a unary function or functor type
42 struct function_arg_extractor
44 // Function is either a function type like void(int const&), or a functor - eg. a class with void operator(int)
45 // Sets type to the argument type with the constness and referenceness stripped (eg. int)
46 template <typename Function
>
50 typedef typename
boost::remove_const
< typename
boost::remove_reference
<Function
>::type
>::type bare_type
;
51 typedef typename signature_of
<bare_type
>::type normalized_function_type
;
52 typedef typename
boost::function_types::function_arity
<normalized_function_type
>::type arity
;
53 typedef typename
boost::function_types::parameter_types
<normalized_function_type
>::type parameter_types
;
54 typedef typename
boost::function_types::result_type
<normalized_function_type
>::type result_type
;
56 BOOST_STATIC_ASSERT_MSG((arity::value
== 1), "make_visitor called with a non-unary function");
58 typedef typename
boost::mpl::front
<parameter_types
>::type parameter_type
;
60 typedef typename
boost::remove_const
< typename
boost::remove_reference
<parameter_type
>::type
>::type type
;
66 template <typename AType
, typename Ind
>
68 typedef boost::mpl::pair
<AType
, Ind
> type
;
72 // A metafunction class that asserts the second argument is in Allowed, and returns void
73 template<typename Allowed
>
76 template <typename Type1
, typename Type2
>
80 BOOST_STATIC_ASSERT_MSG((boost::mpl::contains
<Allowed
, typename
boost::mpl::first
<Type2
>::type
>::value
),
81 "make_visitor called with spurious handler functions");
87 template <typename Seq
>
91 struct insert_helper
{
92 template <typename M
, typename P
>
95 typedef typename
boost::mpl::insert
<
101 typedef typename
boost::mpl::fold
<Seq
, boost::mpl::map0
<>, insert_helper
>::type type
;
104 // A functor template suitable for passing into apply_visitor. The constructor accepts the list of handler functions,
105 // which are then exposed through a set of operator()s
106 template <typename Result
, typename Variant
, typename
... Functions
>
107 struct generic_visitor
: boost::static_visitor
<Result
>, boost::noncopyable
110 typedef generic_visitor
<Result
, Variant
, Functions
...> type
;
112 // Compute the function_map type
113 typedef boost::mpl::vector
<Functions
...> function_types
;
114 typedef typename
boost::mpl::transform
<function_types
, function_arg_extractor
>::type arg_types
;
115 typedef typename
boost::mpl::transform
<
117 boost::mpl::range_c
<int, 0, boost::mpl::size
<arg_types
>::value
>,
120 typedef typename as_map
<pair_list
>::type fmap
;
122 // Check that the argument types are unique
123 BOOST_STATIC_ASSERT_MSG((boost::mpl::size
<fmap
>::value
== boost::mpl::size
<arg_types
>::value
),
124 "make_visitor called with non-unique argument types for handler functions");
126 // Check that there aren't any argument types not in the variant types
127 typedef typename
boost::mpl::fold
<fmap
, void, check_in
<typename
Variant::types
> >::type dummy
;
129 boost::fusion::vector
<Functions
...> fvec
;
132 template <typename T
>
133 Result
apply_helper(const T
& object
, boost::mpl::true_
) const {
134 typedef typename
boost::mpl::at
<fmap
, T
>::type Ind
;
135 return boost::fusion::at
<Ind
>(fvec
)(object
);
138 template <typename T
>
139 Result
apply_helper(const T
& object
, boost::mpl::false_
) const {
143 BOOST_MOVABLE_BUT_NOT_COPYABLE(generic_visitor
)
146 generic_visitor(BOOST_RV_REF(type
) other
)
148 fvec(boost::move(other
.fvec
))
151 generic_visitor(Functions
&&... functions
)
153 fvec(std::forward
<Functions
>(functions
)...)
157 template <typename T
>
158 Result
operator()(const T
& object
) const {
159 typedef typename
boost::mpl::has_key
<fmap
, T
>::type correct_key
;
160 BOOST_STATIC_ASSERT_MSG(correct_key::value
,
161 "make_visitor called without specifying handlers for all required types");
162 return apply_helper(object
, correct_key());
166 // A metafunction class for getting the return type of a function
167 struct function_return_extractor
169 template <typename Function
>
170 struct apply
: boost::function_types::result_type
<typename signature_of
<Function
>::type
>
175 // A metafunction class that asserts the two arguments are the same and returns the first one
178 template <typename Type1
, typename Type2
>
182 BOOST_STATIC_ASSERT_MSG((boost::is_same
<Type1
, Type2
>::value
),
183 "make_visitor called with functions of differing return types");
189 // A metafunction for getting the required generic_visitor type for the set of Functions
190 template <typename Variant
, typename
... Functions
>
191 struct get_generic_visitor
194 typedef boost::mpl::vector
<Functions
...> function_types
;
195 typedef typename
boost::mpl::transform
<
197 boost::remove_const
< boost::remove_reference
<boost::mpl::_1
> >
198 >::type bare_function_types
;
199 typedef typename
boost::mpl::transform
<bare_function_types
, function_return_extractor
>::type return_types
;
202 // Set result_type to the return type of the first function
203 typedef typename
boost::mpl::front
<return_types
>::type result_type
;
204 typedef generic_visitor
<result_type
, Variant
, Functions
...> type
;
207 // Assert that every return type is the same as the first one
208 typedef typename
boost::mpl::fold
<return_types
, result_type
, check_same
>::type dummy
;
211 // Accepts a set of functions and returns an object suitable for apply_visitor
212 template <typename Variant
, typename
... Functions
>
213 auto make_visitor(BOOST_RV_REF(Functions
)... functions
)
214 -> typename
detail::get_generic_visitor
<Variant
, Functions
...>::type
216 return typename
detail::get_generic_visitor
<Variant
, Functions
...>::type(boost::forward
<Functions
>(functions
)...);
221 template <typename Variant
, typename
... Functions
>
222 auto match(Variant
const& variant
, BOOST_RV_REF(Functions
)... functions
)
223 -> typename
detail::get_generic_visitor
<Variant
, Functions
...>::result_type
225 return boost::apply_visitor(detail::make_visitor
<Variant
>(
226 boost::forward
<Functions
>(functions
)...), variant
);