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/function_types/function_arity.hpp>
12 #include <boost/fusion/algorithm/transformation/transform.hpp>
13 #include <boost/mpl/contains.hpp>
14 #include <boost/mpl/map.hpp>
15 #include <boost/mpl/vector.hpp>
16 #include <boost/mpl/range_c.hpp>
17 #include <boost/noncopyable.hpp>
19 #include "function_signature.h"
23 // A metafunction class for getting the argument type from a unary function or functor type
24 struct function_arg_extractor
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
>
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
;
38 BOOST_STATIC_ASSERT_MSG((arity::value
== 1), "make_visitor called with a non-unary function");
40 typedef typename
boost::mpl::front
<parameter_types
>::type parameter_type
;
42 typedef typename
boost::remove_const
< typename
boost::remove_reference
<parameter_type
>::type
>::type type
;
48 template <typename AType
, typename Ind
>
50 typedef boost::mpl::pair
<AType
, Ind
> type
;
54 // A metafunction class that asserts the second argument is in Allowed, and returns void
55 template<typename Allowed
>
58 template <typename Type1
, typename Type2
>
62 BOOST_STATIC_ASSERT_MSG((boost::mpl::contains
<Allowed
, typename
boost::mpl::first
<Type2
>::type
>::value
),
63 "make_visitor called with spurious handler functions");
69 template <typename Seq
>
73 struct insert_helper
{
74 template <typename M
, typename P
>
77 typedef typename
boost::mpl::insert
<
83 typedef typename
boost::mpl::fold
<Seq
, boost::mpl::map0
<>, insert_helper
>::type type
;
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
92 typedef generic_visitor
<Result
, Variant
, Functions
...> type
;
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
<
99 boost::mpl::range_c
<int, 0, boost::mpl::size
<arg_types
>::value
>,
102 typedef typename as_map
<pair_list
>::type fmap
;
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");
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
;
111 boost::fusion::vector
<Functions
...> fvec
;
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
);
120 template <typename T
>
121 Result
apply_helper(const T
& object
, boost::mpl::false_
) const {
125 BOOST_MOVABLE_BUT_NOT_COPYABLE(generic_visitor
)
128 generic_visitor(BOOST_RV_REF(type
) other
)
130 fvec(boost::move(other
.fvec
))
133 generic_visitor(Functions
&&... functions
)
135 fvec(std::forward
<Functions
>(functions
)...)
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());
148 // A metafunction class for getting the return type of a function
149 struct function_return_extractor
151 template <typename Function
>
152 struct apply
: boost::function_types::result_type
<typename signature_of
<Function
>::type
>
157 // A metafunction class that asserts the two arguments are the same and returns the first one
160 template <typename Type1
, typename Type2
>
164 BOOST_STATIC_ASSERT_MSG((boost::is_same
<Type1
, Type2
>::value
),
165 "make_visitor called with functions of differing return types");
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
176 typedef boost::mpl::vector
<Functions
...> function_types
;
177 typedef typename
boost::mpl::transform
<
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
;
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
;
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
;
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
198 return typename
detail::get_generic_visitor
<Variant
, Functions
...>::type(boost::forward
<Functions
>(functions
)...);
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
207 return boost::apply_visitor(detail::make_visitor
<Variant
>(
208 boost::forward
<Functions
>(functions
)...), variant
);