]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/inline_variant.h
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / common / inline_variant.h
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
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>
36
37 #include "function_signature.h"
38
39 namespace detail {
40
41 // A metafunction class for getting the argument type from a unary function or functor type
42 struct function_arg_extractor
43 {
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>
47 struct apply
48 {
49 private:
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;
55
56 BOOST_STATIC_ASSERT_MSG((arity::value == 1), "make_visitor called with a non-unary function");
57
58 typedef typename boost::mpl::front<parameter_types>::type parameter_type;
59 public:
60 typedef typename boost::remove_const< typename boost::remove_reference<parameter_type>::type >::type type;
61 };
62 };
63
64 struct make_pair
65 {
66 template <typename AType, typename Ind>
67 struct apply {
68 typedef boost::mpl::pair<AType, Ind> type;
69 };
70 };
71
72 // A metafunction class that asserts the second argument is in Allowed, and returns void
73 template<typename Allowed>
74 struct check_in
75 {
76 template <typename Type1, typename Type2>
77 struct apply
78 {
79 private:
80 BOOST_STATIC_ASSERT_MSG((boost::mpl::contains<Allowed, typename boost::mpl::first<Type2>::type>::value),
81 "make_visitor called with spurious handler functions");
82 public:
83 typedef void type;
84 };
85 };
86
87 template <typename Seq>
88 struct as_map
89 {
90 private:
91 struct insert_helper {
92 template <typename M, typename P>
93 struct apply
94 {
95 typedef typename boost::mpl::insert<
96 M,
97 P>::type type;
98 };
99 };
100 public:
101 typedef typename boost::mpl::fold<Seq, boost::mpl::map0<>, insert_helper>::type type;
102 };
103
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
108 {
109 private:
110 typedef generic_visitor<Result, Variant, Functions...> type;
111
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<
116 arg_types,
117 boost::mpl::range_c<int, 0, boost::mpl::size<arg_types>::value>,
118 make_pair
119 >::type pair_list;
120 typedef typename as_map<pair_list>::type fmap;
121
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");
125
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;
128
129 boost::fusion::vector<Functions...> fvec;
130
131
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);
136 }
137
138 template <typename T>
139 Result apply_helper(const T& object, boost::mpl::false_) const {
140 return Result();
141 }
142
143 BOOST_MOVABLE_BUT_NOT_COPYABLE(generic_visitor)
144
145 public:
146 generic_visitor(BOOST_RV_REF(type) other)
147 :
148 fvec(boost::move(other.fvec))
149 {
150 }
151 generic_visitor(Functions&&... functions)
152 :
153 fvec(std::forward<Functions>(functions)...)
154 {
155 }
156
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());
163 }
164 };
165
166 // A metafunction class for getting the return type of a function
167 struct function_return_extractor
168 {
169 template <typename Function>
170 struct apply : boost::function_types::result_type<typename signature_of<Function>::type>
171 {
172 };
173 };
174
175 // A metafunction class that asserts the two arguments are the same and returns the first one
176 struct check_same
177 {
178 template <typename Type1, typename Type2>
179 struct apply
180 {
181 private:
182 BOOST_STATIC_ASSERT_MSG((boost::is_same<Type1, Type2>::value),
183 "make_visitor called with functions of differing return types");
184 public:
185 typedef Type1 type;
186 };
187 };
188
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
192 {
193 private:
194 typedef boost::mpl::vector<Functions...> function_types;
195 typedef typename boost::mpl::transform<
196 function_types,
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;
200
201 public:
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;
205
206 private:
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;
209 };
210
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
215 {
216 return typename detail::get_generic_visitor<Variant, Functions...>::type(boost::forward<Functions>(functions)...);
217 }
218
219 }
220
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
224 {
225 return boost::apply_visitor(detail::make_visitor<Variant>(
226 boost::forward<Functions>(functions)...), variant);
227 }
228
229 #endif