1 /*============================================================================= 2 Copyright (c) 2001-2011 Hartmut Kaiser 3 Copyright (c) 2001-2011 Joel de Guzman 4 5 Distributed under the Boost Software License, Version 1.0. (See accompanying 6 file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 =============================================================================*/ 8 #ifndef BOOST_SPIRIT_KARMA_DETAIL_PASS_CONTAINER_HPP 9 #define BOOST_SPIRIT_KARMA_DETAIL_PASS_CONTAINER_HPP 10 11 #if defined(_MSC_VER) 12 #pragma once 13 #endif 14 15 #include <boost/spirit/home/karma/detail/attributes.hpp> 16 #include <boost/spirit/home/support/container.hpp> 17 #include <boost/spirit/home/support/handles_container.hpp> 18 #include <boost/spirit/home/support/detail/hold_any.hpp> 19 #include <boost/type_traits/is_base_of.hpp> 20 #include <boost/type_traits/is_convertible.hpp> 21 #include <boost/mpl/bool.hpp> 22 #include <boost/mpl/and.hpp> 23 #include <boost/mpl/or.hpp> 24 #include <boost/preprocessor/cat.hpp> 25 #include <boost/preprocessor/repetition/repeat.hpp> 26 #include <boost/range/iterator_range_core.hpp> 27 #include <boost/fusion/include/deduce_sequence.hpp> 28 29 #include <boost/mpl/print.hpp> 30 31 namespace boost { namespace spirit { namespace karma { namespace detail 32 { 33 // Helper meta-function allowing to evaluate weak substitutability and 34 // negate the result if the predicate (Sequence) is not true 35 template <typename Sequence, typename Attribute, typename ValueType> 36 struct negate_weak_substitute_if_not 37 : mpl::if_< 38 Sequence 39 , typename traits::is_weak_substitute<Attribute, ValueType>::type 40 , typename mpl::not_< 41 traits::is_weak_substitute<Attribute, ValueType> 42 >::type> 43 {}; 44 45 // pass_through_container: utility to check decide whether a provided 46 // container attribute needs to be passed through to the current component 47 // or of we need to split the container by passing along instances of its 48 // value type 49 50 // if the expected attribute of the current component is neither a Fusion 51 // sequence nor a container, we will pass through the provided container 52 // only if its value type is not compatible with the component 53 template <typename Container, typename ValueType, typename Attribute 54 , typename Sequence, typename Enable = void> 55 struct pass_through_container_base 56 : negate_weak_substitute_if_not<Sequence, ValueType, Attribute> 57 {}; 58 59 // Specialization for fusion sequences, in this case we check whether all 60 // the types in the sequence are convertible to the lhs attribute. 61 // 62 // We return false if the rhs attribute itself is a fusion sequence, which 63 // is compatible with the LHS sequence (we want to pass through this 64 // attribute without it being split apart). 65 template <typename Container, typename ValueType, typename Attribute 66 , typename Sequence = mpl::true_> 67 struct not_compatible_element 68 : mpl::and_< 69 negate_weak_substitute_if_not<Sequence, Container, Attribute> 70 , negate_weak_substitute_if_not<Sequence, ValueType, Attribute> > 71 {}; 72 73 // If the value type of the container is not a Fusion sequence, we pass 74 // through the container if each of the elements of the Attribute 75 // sequence is compatible with either the container or its value type. 76 template <typename Container, typename ValueType, typename Attribute 77 , typename Sequence 78 , bool IsSequence = fusion::traits::is_sequence<ValueType>::value> 79 struct pass_through_container_fusion_sequence 80 { 81 typedef typename mpl::find_if< 82 Attribute, not_compatible_element<Container, ValueType, mpl::_1> 83 >::type iter; 84 typedef typename mpl::end<Attribute>::type end; 85 86 typedef typename is_same<iter, end>::type type; 87 }; 88 89 // If both, the Attribute and the value type of the provided container 90 // are Fusion sequences, we pass the container only if the two 91 // sequences are not compatible. 92 template <typename Container, typename ValueType, typename Attribute 93 , typename Sequence> 94 struct pass_through_container_fusion_sequence< 95 Container, ValueType, Attribute, Sequence, true> 96 { 97 typedef typename mpl::find_if< 98 Attribute 99 , not_compatible_element<Container, ValueType, mpl::_1, Sequence> 100 >::type iter; 101 typedef typename mpl::end<Attribute>::type end; 102 103 typedef typename is_same<iter, end>::type type; 104 }; 105 106 template <typename Container, typename ValueType, typename Attribute 107 , typename Sequence> 108 struct pass_through_container_base<Container, ValueType, Attribute 109 , Sequence 110 , typename enable_if<fusion::traits::is_sequence<Attribute> >::type> 111 : pass_through_container_fusion_sequence< 112 Container, ValueType, Attribute, Sequence> 113 {}; 114 115 // Specialization for containers 116 // 117 // If the value type of the attribute of the current component is not 118 // a Fusion sequence, we have to pass through the provided container if 119 // both are compatible. 120 template <typename Container, typename ValueType, typename Attribute 121 , typename Sequence, typename AttributeValueType 122 , bool IsSequence = fusion::traits::is_sequence<AttributeValueType>::value> 123 struct pass_through_container_container 124 : mpl::or_< 125 traits::is_weak_substitute<Container, Attribute> 126 , traits::is_weak_substitute<Container, AttributeValueType> > 127 {}; 128 129 // If the value type of the exposed container attribute is a Fusion 130 // sequence, we use the already existing logic for those. 131 template <typename Container, typename ValueType, typename Attribute 132 , typename Sequence, typename AttributeValueType> 133 struct pass_through_container_container< 134 Container, ValueType, Attribute, Sequence, AttributeValueType, true> 135 : pass_through_container_fusion_sequence< 136 Container, ValueType, AttributeValueType, Sequence> 137 {}; 138 139 template <typename Container, typename ValueType, typename Attribute 140 , typename Sequence> 141 struct pass_through_container_base<Container, ValueType, Attribute 142 , Sequence 143 , typename enable_if<traits::is_container<Attribute> >::type> 144 : detail::pass_through_container_container< 145 Container, ValueType, Attribute, Sequence 146 , typename traits::container_value<Attribute>::type> 147 {}; 148 149 // Specialization for exposed optional attributes 150 // 151 // If the type embedded in the exposed optional is not a Fusion 152 // sequence we pass through the container attribute if it is compatible 153 // either to the optionals embedded type or to the containers value 154 // type. 155 template <typename Container, typename ValueType, typename Attribute 156 , typename Sequence 157 , bool IsSequence = fusion::traits::is_sequence<Attribute>::value> 158 struct pass_through_container_optional 159 : mpl::or_< 160 traits::is_weak_substitute<Container, Attribute> 161 , traits::is_weak_substitute<ValueType, Attribute> > 162 {}; 163 164 // If the embedded type of the exposed optional attribute is a Fusion 165 // sequence, we use the already existing logic for those. 166 template <typename Container, typename ValueType, typename Attribute 167 , typename Sequence> 168 struct pass_through_container_optional< 169 Container, ValueType, Attribute, Sequence, true> 170 : pass_through_container_fusion_sequence< 171 Container, ValueType, Attribute, Sequence> 172 {}; 173 174 /////////////////////////////////////////////////////////////////////////// 175 template <typename Container, typename ValueType, typename Attribute 176 , typename Sequence> 177 struct pass_through_container 178 : pass_through_container_base<Container, ValueType, Attribute, Sequence> 179 {}; 180 181 // Handle optional attributes 182 template <typename Container, typename ValueType, typename Attribute 183 , typename Sequence> 184 struct pass_through_container< 185 Container, ValueType, boost::optional<Attribute>, Sequence> 186 : pass_through_container_optional< 187 Container, ValueType, Attribute, Sequence> 188 {}; 189 190 // If both, the containers value type and the exposed attribute type are 191 // optionals we are allowed to pass through the container only if the 192 // embedded types of those optionals are not compatible. 193 template <typename Container, typename ValueType, typename Attribute 194 , typename Sequence> 195 struct pass_through_container< 196 Container, boost::optional<ValueType>, boost::optional<Attribute> 197 , Sequence> 198 : mpl::not_<traits::is_weak_substitute<ValueType, Attribute> > 199 {}; 200 201 // Specialization for exposed variant attributes 202 // 203 // We pass through the container attribute if at least one of the embedded 204 // types in the variant requires to pass through the attribute 205 206 #if !defined(BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES) 207 template <typename Container, typename ValueType, typename Sequence 208 , typename T> 209 struct pass_through_container<Container, ValueType, boost::variant<T> 210 , Sequence> 211 : pass_through_container<Container, ValueType, T, Sequence> 212 {}; 213 214 template <typename Container, typename ValueType, typename Sequence 215 , typename T0, typename ...TN> 216 struct pass_through_container<Container, ValueType 217 , boost::variant<T0, TN...>, Sequence> 218 : mpl::bool_<pass_through_container< 219 Container, ValueType, T0, Sequence 220 >::type::value || pass_through_container< 221 Container, ValueType, boost::variant<TN...>, Sequence 222 >::type::value> 223 {}; 224 #else 225 #define BOOST_SPIRIT_PASS_THROUGH_CONTAINER(z, N, _) \ 226 pass_through_container<Container, ValueType, \ 227 BOOST_PP_CAT(T, N), Sequence>::type::value || \ 228 /***/ 229 230 // make sure unused variant parameters do not affect the outcome 231 template <typename Container, typename ValueType, typename Sequence> 232 struct pass_through_container<Container, ValueType 233 , boost::detail::variant::void_, Sequence> 234 : mpl::false_ 235 {}; 236 237 template <typename Container, typename ValueType, typename Sequence 238 , BOOST_VARIANT_ENUM_PARAMS(typename T)> 239 struct pass_through_container<Container, ValueType 240 , boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>, Sequence> 241 : mpl::bool_<BOOST_PP_REPEAT(BOOST_VARIANT_LIMIT_TYPES 242 , BOOST_SPIRIT_PASS_THROUGH_CONTAINER, _) false> 243 {}; 244 245 #undef BOOST_SPIRIT_PASS_THROUGH_CONTAINER 246 #endif 247 }}}} 248 249 /////////////////////////////////////////////////////////////////////////////// 250 namespace boost { namespace spirit { namespace traits 251 { 252 /////////////////////////////////////////////////////////////////////////// 253 // forwarding customization point for domain karma::domain 254 template <typename Container, typename ValueType, typename Attribute 255 , typename Sequence> 256 struct pass_through_container< 257 Container, ValueType, Attribute, Sequence, karma::domain> 258 : karma::detail::pass_through_container< 259 Container, ValueType, Attribute, Sequence> 260 {}; 261 }}} 262 263 namespace boost { namespace spirit { namespace karma { namespace detail 264 { 265 template <typename Iterator> 266 struct pass_container_base 267 { pass_container_baseboost::spirit::karma::detail::pass_container_base268 pass_container_base(Iterator begin, Iterator end) 269 : iter(begin), end(end) 270 {} 271 272 mutable Iterator iter; 273 mutable Iterator end; 274 }; 275 276 template <typename Iterator> 277 struct pass_container_base<Iterator&> 278 { pass_container_baseboost::spirit::karma::detail::pass_container_base279 pass_container_base(Iterator& begin, Iterator& end) 280 : iter(begin), end(end) 281 {} 282 283 Iterator& iter; 284 Iterator& end; 285 }; 286 287 /////////////////////////////////////////////////////////////////////////// 288 // This function handles the case where the attribute (Attr) given 289 // to the sequence is an STL container. This is a wrapper around F. 290 // The function F does the actual generating. 291 template <typename F, typename Attr, typename Iterator, typename Sequence> 292 struct pass_container : pass_container_base<Iterator> 293 { 294 typedef pass_container_base<Iterator> base_type; 295 typedef typename F::context_type context_type; 296 pass_containerboost::spirit::karma::detail::pass_container297 pass_container(F const& f, Iterator begin, Iterator end) 298 : base_type(begin, end) 299 , f(f) 300 {} 301 is_at_endboost::spirit::karma::detail::pass_container302 bool is_at_end() const 303 { 304 return traits::compare(this->iter, this->end); 305 } 306 nextboost::spirit::karma::detail::pass_container307 void next() 308 { 309 traits::next(this->iter); 310 } 311 312 // this is for the case when the current element expects an attribute 313 // which is taken from the next entry in the container 314 template <typename Component> dispatch_containerboost::spirit::karma::detail::pass_container315 bool dispatch_container(Component const& component, mpl::false_) const 316 { 317 // get the next value to generate from container 318 if (!is_at_end() && !f(component, traits::deref(this->iter))) 319 { 320 // needs to return false as long as everything is ok 321 traits::next(this->iter); 322 return false; 323 } 324 325 // either no elements available any more or generation failed 326 return true; 327 } 328 329 // this is for the case when the current element is able to handle an 330 // attribute which is a container itself, this element will push its 331 // data directly into the attribute container 332 template <typename Component> dispatch_containerboost::spirit::karma::detail::pass_container333 bool dispatch_container(Component const& component, mpl::true_) const 334 { 335 return f(component, make_iterator_range(this->iter, this->end)); 336 } 337 338 /////////////////////////////////////////////////////////////////////// 339 // this is for the case when the current element doesn't expect an 340 // attribute 341 template <typename Component> dispatch_attributeboost::spirit::karma::detail::pass_container342 bool dispatch_attribute(Component const& component, mpl::false_) const 343 { 344 return f(component, unused); 345 } 346 347 // the current element expects an attribute 348 template <typename Component> dispatch_attributeboost::spirit::karma::detail::pass_container349 bool dispatch_attribute(Component const& component, mpl::true_) const 350 { 351 typedef typename traits::container_value<Attr>::type value_type; 352 typedef typename 353 traits::attribute_of<Component, context_type>::type 354 lhs_attribute; 355 356 // this predicate detects, whether the value type of the container 357 // attribute is a substitute for the attribute of the current 358 // element 359 typedef mpl::and_< 360 traits::handles_container<Component, Attr, context_type> 361 , traits::pass_through_container< 362 Attr, value_type, lhs_attribute, Sequence, karma::domain> 363 > predicate; 364 365 return dispatch_container(component, predicate()); 366 } 367 368 // Dispatches to dispatch_main depending on the attribute type 369 // of the Component 370 template <typename Component> operator ()boost::spirit::karma::detail::pass_container371 bool operator()(Component const& component) const 372 { 373 // we need to dispatch depending on the type of the attribute 374 // of the current element (component). If this is has no attribute 375 // we shouldn't use an element of the container but unused_type 376 // instead 377 typedef traits::not_is_unused< 378 typename traits::attribute_of<Component, context_type>::type 379 > predicate; 380 381 return dispatch_attribute(component, predicate()); 382 } 383 384 F f; 385 386 // silence MSVC warning C4512: assignment operator could not be generated 387 BOOST_DELETED_FUNCTION(pass_container& operator= (pass_container const&)) 388 }; 389 }}}} 390 391 #endif 392