1 //-----------------------------------------------------------------------------
2 // boost variant/polymorphic_get.hpp header file
3 // See http://www.boost.org for updates, documentation, and revision history.
4 //-----------------------------------------------------------------------------
5 //
6 // Copyright (c) 2013-2019 Antony Polukhin
7 //
8 // Distributed under the Boost Software License, Version 1.0. (See
9 // accompanying file LICENSE_1_0.txt or copy at
10 // http://www.boost.org/LICENSE_1_0.txt)
11 
12 #ifndef BOOST_VARIANT_POLYMORPHIC_GET_HPP
13 #define BOOST_VARIANT_POLYMORPHIC_GET_HPP
14 
15 #include <exception>
16 
17 #include <boost/config.hpp>
18 #include <boost/detail/workaround.hpp>
19 #include <boost/static_assert.hpp>
20 #include <boost/throw_exception.hpp>
21 #include <boost/utility/addressof.hpp>
22 #include <boost/variant/variant_fwd.hpp>
23 #include <boost/variant/get.hpp>
24 
25 #include <boost/type_traits/add_reference.hpp>
26 #include <boost/type_traits/add_pointer.hpp>
27 #include <boost/type_traits/is_base_of.hpp>
28 #include <boost/type_traits/is_const.hpp>
29 
30 namespace boost {
31 
32 //////////////////////////////////////////////////////////////////////////
33 // class bad_polymorphic_get
34 //
35 // The exception thrown in the event of a failed get of a value.
36 //
37 class BOOST_SYMBOL_VISIBLE bad_polymorphic_get
38     : public bad_get
39 {
40 public: // std::exception implementation
41 
what() const42     virtual const char * what() const BOOST_NOEXCEPT_OR_NOTHROW
43     {
44         return "boost::bad_polymorphic_get: "
45                "failed value get using boost::polymorphic_get";
46     }
47 
48 };
49 
50 //////////////////////////////////////////////////////////////////////////
51 // function template get<T>
52 //
53 // Retrieves content of given variant object if content is of type T.
54 // Otherwise: pointer ver. returns 0; reference ver. throws bad_get.
55 //
56 
57 namespace detail { namespace variant {
58 
59 
60 ///////////////////////////////////////////////////////////////////////////////////////////////////
61 // polymorphic metafunctions to detect index of a value
62 //
63 
64 template <class Types, class T>
65 struct element_polymorphic_iterator_impl :
66     boost::mpl::find_if<
67         Types,
68         boost::mpl::or_<
69             variant_element_functor<boost::mpl::_1, T>,
70             variant_element_functor<boost::mpl::_1, typename boost::remove_cv<T>::type >,
71             boost::is_base_of<T, boost::mpl::_1>
72         >
73     >
74 {};
75 
76 template <class Variant, class T>
77 struct holds_element_polymorphic :
78     boost::mpl::not_<
79         boost::is_same<
80             typename boost::mpl::end<typename Variant::types>::type,
81             typename element_polymorphic_iterator_impl<typename Variant::types, typename boost::remove_reference<T>::type >::type
82         >
83     >
84 {};
85 
86 // (detail) class template get_polymorphic_visitor
87 //
88 // Generic static visitor that: if the value is of the specified
89 // type or of a type derived from specified, returns a pointer
90 // to the value it visits; else a null pointer.
91 //
92 template <typename Base>
93 struct get_polymorphic_visitor
94 {
95 private: // private typedefs
96     typedef get_polymorphic_visitor<Base>       this_type;
97     typedef typename add_pointer<Base>::type    pointer;
98     typedef typename add_reference<Base>::type  reference;
99 
getboost::detail::variant::get_polymorphic_visitor100     pointer get(reference operand, boost::true_type) const BOOST_NOEXCEPT
101     {
102         return boost::addressof(operand);
103     }
104 
105     template <class T>
getboost::detail::variant::get_polymorphic_visitor106     pointer get(T&, boost::false_type) const BOOST_NOEXCEPT
107     {
108         return static_cast<pointer>(0);
109     }
110 
111 public: // visitor interfaces
112     typedef pointer result_type;
113 
114     template <typename U>
operator ()boost::detail::variant::get_polymorphic_visitor115     pointer operator()(U& operand) const BOOST_NOEXCEPT
116     {
117         typedef typename boost::remove_reference<Base>::type base_t;
118         typedef boost::integral_constant<
119             bool,
120             (
121                 boost::is_base_of<base_t, U>::value &&
122                 (boost::is_const<base_t>::value || !boost::is_const<U>::value)
123             )
124             || boost::is_same<base_t, U>::value
125             || boost::is_same<typename boost::remove_cv<base_t>::type, U >::value
126         > tag_t;
127 
128         return this_type::get(operand, tag_t());
129     }
130 };
131 
132 }} // namespace detail::variant
133 
134 #ifndef BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE
135 #   if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0551))
136 #       define BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(t)
137 #   else
138 #       define BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(t)  \
139         , t* = 0
140 #   endif
141 #endif
142 
143 //////////////////////////////////////////////////////////////////////////////////////////////////////////
144 // polymorphic_relaxed_get
145 //
146 
147 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
148 inline
149     typename add_pointer<U>::type
polymorphic_relaxed_get(boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> * operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))150 polymorphic_relaxed_get(
151       boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
152       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
153     ) BOOST_NOEXCEPT
154 {
155     typedef typename add_pointer<U>::type U_ptr;
156     if (!operand) return static_cast<U_ptr>(0);
157 
158     detail::variant::get_polymorphic_visitor<U> v;
159     return operand->apply_visitor(v);
160 }
161 
162 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
163 inline
164     typename add_pointer<const U>::type
polymorphic_relaxed_get(const boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> * operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))165 polymorphic_relaxed_get(
166       const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
167       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
168     ) BOOST_NOEXCEPT
169 {
170     typedef typename add_pointer<const U>::type U_ptr;
171     if (!operand) return static_cast<U_ptr>(0);
172 
173     detail::variant::get_polymorphic_visitor<const U> v;
174     return operand->apply_visitor(v);
175 }
176 
177 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
178 inline
179     typename add_reference<U>::type
polymorphic_relaxed_get(boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> & operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))180 polymorphic_relaxed_get(
181       boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
182       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
183     )
184 {
185     typedef typename add_pointer<U>::type U_ptr;
186     U_ptr result = polymorphic_relaxed_get<U>(&operand);
187 
188     if (!result)
189         boost::throw_exception(bad_polymorphic_get());
190     return *result;
191 }
192 
193 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
194 inline
195     typename add_reference<const U>::type
polymorphic_relaxed_get(const boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> & operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))196 polymorphic_relaxed_get(
197       const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
198       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
199     )
200 {
201     typedef typename add_pointer<const U>::type U_ptr;
202     U_ptr result = polymorphic_relaxed_get<const U>(&operand);
203 
204     if (!result)
205         boost::throw_exception(bad_polymorphic_get());
206     return *result;
207 }
208 
209 //////////////////////////////////////////////////////////////////////////////////////////////////////////
210 // polymorphic_strict_get
211 //
212 
213 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
214 inline
215     typename add_pointer<U>::type
polymorphic_strict_get(boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> * operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))216 polymorphic_strict_get(
217       boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
218       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
219     ) BOOST_NOEXCEPT
220 {
221     BOOST_STATIC_ASSERT_MSG(
222         (boost::detail::variant::holds_element_polymorphic<boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >, U >::value),
223         "boost::variant does not contain specified type U, "
224         "call to boost::polymorphic_get<U>(boost::variant<T...>*) will always return NULL"
225     );
226 
227     return polymorphic_relaxed_get<U>(operand);
228 }
229 
230 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
231 inline
232     typename add_pointer<const U>::type
polymorphic_strict_get(const boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> * operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))233 polymorphic_strict_get(
234       const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
235       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
236     ) BOOST_NOEXCEPT
237 {
238     BOOST_STATIC_ASSERT_MSG(
239         (boost::detail::variant::holds_element_polymorphic<boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >, U >::value),
240         "boost::variant does not contain specified type U, "
241         "call to boost::polymorphic_get<U>(const boost::variant<T...>*) will always return NULL"
242     );
243 
244     return polymorphic_relaxed_get<U>(operand);
245 }
246 
247 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
248 inline
249     typename add_reference<U>::type
polymorphic_strict_get(boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> & operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))250 polymorphic_strict_get(
251       boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
252       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
253     )
254 {
255     BOOST_STATIC_ASSERT_MSG(
256         (boost::detail::variant::holds_element_polymorphic<boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >, U >::value),
257         "boost::variant does not contain specified type U, "
258         "call to boost::polymorphic_get<U>(boost::variant<T...>&) will always throw boost::bad_polymorphic_get exception"
259     );
260 
261     return polymorphic_relaxed_get<U>(operand);
262 }
263 
264 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
265 inline
266     typename add_reference<const U>::type
polymorphic_strict_get(const boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> & operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))267 polymorphic_strict_get(
268       const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
269       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
270     )
271 {
272     BOOST_STATIC_ASSERT_MSG(
273         (boost::detail::variant::holds_element_polymorphic<boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >, U >::value),
274         "boost::variant does not contain specified type U, "
275         "call to boost::polymorphic_get<U>(const boost::variant<T...>&) will always throw boost::bad_polymorphic_get exception"
276     );
277 
278     return polymorphic_relaxed_get<U>(operand);
279 }
280 
281 /////////////////////////////////////////////////////////////////////////////////////////////////////////////
282 // polymorphic_get<U>(variant) methods
283 //
284 
285 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
286 inline
287     typename add_pointer<U>::type
polymorphic_get(boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> * operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))288 polymorphic_get(
289       boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
290       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
291     ) BOOST_NOEXCEPT
292 {
293 #ifdef BOOST_VARIANT_USE_RELAXED_GET_BY_DEFAULT
294     return polymorphic_relaxed_get<U>(operand);
295 #else
296     return polymorphic_strict_get<U>(operand);
297 #endif
298 
299 }
300 
301 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
302 inline
303     typename add_pointer<const U>::type
polymorphic_get(const boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> * operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))304 polymorphic_get(
305       const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
306       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
307     ) BOOST_NOEXCEPT
308 {
309 #ifdef BOOST_VARIANT_USE_RELAXED_GET_BY_DEFAULT
310     return polymorphic_relaxed_get<U>(operand);
311 #else
312     return polymorphic_strict_get<U>(operand);
313 #endif
314 }
315 
316 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
317 inline
318     typename add_reference<U>::type
polymorphic_get(boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> & operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))319 polymorphic_get(
320       boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
321       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
322     )
323 {
324 #ifdef BOOST_VARIANT_USE_RELAXED_GET_BY_DEFAULT
325     return polymorphic_relaxed_get<U>(operand);
326 #else
327     return polymorphic_strict_get<U>(operand);
328 #endif
329 }
330 
331 template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
332 inline
333     typename add_reference<const U>::type
polymorphic_get(const boost::variant<BOOST_VARIANT_ENUM_PARAMS (T)> & operand BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE (U))334 polymorphic_get(
335       const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
336       BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
337     )
338 {
339 #ifdef BOOST_VARIANT_USE_RELAXED_GET_BY_DEFAULT
340     return polymorphic_relaxed_get<U>(operand);
341 #else
342     return polymorphic_strict_get<U>(operand);
343 #endif
344 }
345 } // namespace boost
346 
347 #endif // BOOST_VARIANT_POLYMORPHIC_GET_HPP
348