1 //  Copyright (c) 2001-2011 Hartmut Kaiser
2 //
3 //  Distributed under the Boost Software License, Version 1.0. (See accompanying
4 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #if !defined(BOOST_SPIRIT_KARMA_EXTRACT_FROM_SEP_30_2009_0732AM)
7 #define BOOST_SPIRIT_KARMA_EXTRACT_FROM_SEP_30_2009_0732AM
8 
9 #if defined(_MSC_VER)
10 #pragma once
11 #endif
12 
13 #include <boost/spirit/include/phoenix_core.hpp>
14 #include <boost/spirit/home/support/unused.hpp>
15 #include <boost/spirit/home/support/attributes_fwd.hpp>
16 #include <boost/spirit/home/karma/detail/attributes.hpp>
17 #include <boost/spirit/home/support/container.hpp>
18 
19 #include <boost/ref.hpp>
20 #include <boost/optional.hpp>
21 
22 ///////////////////////////////////////////////////////////////////////////////
23 namespace boost { namespace spirit { namespace traits
24 {
25     ///////////////////////////////////////////////////////////////////////////
26     //  This file contains attribute extraction utilities. The utilities
27     //  provided also accept spirit's unused_type; all no-ops. Compiler
28     //  optimization will easily strip these away.
29     ///////////////////////////////////////////////////////////////////////////
30 
31     namespace detail
32     {
33         ///////////////////////////////////////////////////////////////////////
34         // extract first and second element of a fusion sequence
35         template <typename T>
36         struct add_const_ref
37           : add_reference<typename add_const<T>::type>
38         {};
39 
40         template <typename T, int N>
41         struct value_at_c
42           : add_const_ref<typename fusion::result_of::value_at_c<T, N>::type>
43         {};
44     }
45 
46     // This is the default case: the plain attribute values
47     template <typename Attribute, typename Exposed, typename Enable/*= void*/>
48     struct extract_from_attribute
49     {
50         typedef typename traits::one_element_sequence<Attribute>::type
51             is_one_element_sequence;
52 
53         typedef typename mpl::eval_if<
54             is_one_element_sequence
55           , detail::value_at_c<Attribute, 0>
56           , mpl::identity<Attribute const&>
57         >::type type;
58 
59         template <typename Context>
callboost::spirit::traits::extract_from_attribute60         static type call(Attribute const& attr, Context&, mpl::false_)
61         {
62             return attr;
63         }
64 
65         // This handles the case where the attribute is a single element fusion
66         // sequence. We silently extract the only element and treat it as the
67         // attribute to generate output from.
68         template <typename Context>
callboost::spirit::traits::extract_from_attribute69         static type call(Attribute const& attr, Context& ctx, mpl::true_)
70         {
71             return extract_from<Exposed>(fusion::at_c<0>(attr), ctx);
72         }
73 
74         template <typename Context>
callboost::spirit::traits::extract_from_attribute75         static type call(Attribute const& attr, Context& ctx)
76         {
77             return call(attr, ctx, is_one_element_sequence());
78         }
79     };
80 
81     // This handles optional attributes.
82     template <typename Attribute, typename Exposed>
83     struct extract_from_attribute<boost::optional<Attribute>, Exposed>
84     {
85         typedef Attribute const& type;
86 
87         template <typename Context>
callboost::spirit::traits::extract_from_attribute88         static type call(boost::optional<Attribute> const& attr, Context& ctx)
89         {
90             return extract_from<Exposed>(boost::get<Attribute>(attr), ctx);
91         }
92     };
93 
94     template <typename Attribute, typename Exposed>
95     struct extract_from_attribute<boost::optional<Attribute const>, Exposed>
96     {
97         typedef Attribute const& type;
98 
99         template <typename Context>
callboost::spirit::traits::extract_from_attribute100         static type call(boost::optional<Attribute const> const& attr, Context& ctx)
101         {
102             return extract_from<Exposed>(boost::get<Attribute const>(attr), ctx);
103         }
104     };
105 
106     // This handles attributes wrapped inside a boost::ref().
107     template <typename Attribute, typename Exposed>
108     struct extract_from_attribute<reference_wrapper<Attribute>, Exposed>
109     {
110         typedef Attribute const& type;
111 
112         template <typename Context>
callboost::spirit::traits::extract_from_attribute113         static type call(reference_wrapper<Attribute> const& attr, Context& ctx)
114         {
115             return extract_from<Exposed>(attr.get(), ctx);
116         }
117     };
118 
119     ///////////////////////////////////////////////////////////////////////////
120     template <typename Attribute, typename Exposed, typename Enable>
121     struct extract_from_container
122     {
123         typedef typename traits::container_value<Attribute const>::type
124             value_type;
125         typedef typename is_convertible<value_type, Exposed>::type
126             is_convertible_to_value_type;
127 
128         typedef typename mpl::if_<
129            mpl::or_<
130                 is_same<value_type, Exposed>, is_same<Attribute, Exposed> >
131           , Exposed const&, Exposed
132         >::type type;
133 
134         // handle case where container value type is convertible to result type
135         // we simply return the front element of the container
136         template <typename Context, typename Pred>
callboost::spirit::traits::extract_from_container137         static type call(Attribute const& attr, Context&, mpl::true_, Pred)
138         {
139             // return first element from container
140             typedef typename traits::container_iterator<Attribute const>::type
141                 iterator_type;
142 
143             iterator_type it = traits::begin(attr);
144             type result = *it;
145             ++it;
146             return result;
147         }
148 
149         // handle strings
150         template <typename Iterator>
append_to_stringboost::spirit::traits::extract_from_container151         static void append_to_string(Exposed& result, Iterator begin, Iterator end)
152         {
153             for (Iterator i = begin; i != end; ++i)
154                 push_back(result, *i);
155         }
156 
157         template <typename Context>
callboost::spirit::traits::extract_from_container158         static type call(Attribute const& attr, Context&, mpl::false_, mpl::true_)
159         {
160             typedef typename char_type_of<Attribute>::type char_type;
161 
162             Exposed result;
163             append_to_string(result, traits::get_begin<char_type>(attr)
164               , traits::get_end<char_type>(attr));
165             return result;
166         }
167 
168         // everything else gets just passed through
169         template <typename Context>
callboost::spirit::traits::extract_from_container170         static type call(Attribute const& attr, Context&, mpl::false_, mpl::false_)
171         {
172             return type(attr);
173         }
174 
175         template <typename Context>
callboost::spirit::traits::extract_from_container176         static type call(Attribute const& attr, Context& ctx)
177         {
178             typedef typename mpl::and_<
179                 traits::is_string<Exposed>, traits::is_string<Attribute>
180             >::type handle_strings;
181 
182             // return first element from container
183             return call(attr, ctx, is_convertible_to_value_type()
184               , handle_strings());
185         }
186     };
187 
188     template <typename Attribute>
189     struct extract_from_container<Attribute, Attribute>
190     {
191         typedef Attribute const& type;
192 
193         template <typename Context>
callboost::spirit::traits::extract_from_container194         static type call(Attribute const& attr, Context&)
195         {
196             return attr;
197         }
198     };
199 
200     ///////////////////////////////////////////////////////////////////////////
201     namespace detail
202     {
203         // overload for non-container attributes
204         template <typename Exposed, typename Attribute, typename Context>
205         inline typename spirit::result_of::extract_from<Exposed, Attribute>::type
extract_from(Attribute const & attr,Context & ctx,mpl::false_)206         extract_from(Attribute const& attr, Context& ctx, mpl::false_)
207         {
208             return extract_from_attribute<Attribute, Exposed>::call(attr, ctx);
209         }
210 
211         // overload for containers (but not for variants or optionals
212         // holding containers)
213         template <typename Exposed, typename Attribute, typename Context>
214         inline typename spirit::result_of::extract_from<Exposed, Attribute>::type
extract_from(Attribute const & attr,Context & ctx,mpl::true_)215         extract_from(Attribute const& attr, Context& ctx, mpl::true_)
216         {
217             return extract_from_container<Attribute, Exposed>::call(attr, ctx);
218         }
219     }
220 
221     template <typename Exposed, typename Attribute, typename Context>
222     inline typename spirit::result_of::extract_from<Exposed, Attribute>::type
extract_from(Attribute const & attr,Context & ctx,typename enable_if<traits::not_is_unused<Attribute>>::type *)223     extract_from(Attribute const& attr, Context& ctx
224 #if (defined(__GNUC__) && (__GNUC__ < 4)) || \
225     (defined(__APPLE__) && defined(__INTEL_COMPILER))
226       , typename enable_if<traits::not_is_unused<Attribute> >::type*
227 #endif
228     )
229     {
230         typedef typename mpl::and_<
231             traits::is_container<Attribute>
232           , traits::not_is_variant<Attribute>
233           , traits::not_is_optional<Attribute>
234         >::type is_not_wrapped_container;
235 
236         return detail::extract_from<Exposed>(attr, ctx
237           , is_not_wrapped_container());
238     }
239 
240     template <typename Exposed, typename Context>
extract_from(unused_type,Context &)241     inline unused_type extract_from(unused_type, Context&)
242     {
243         return unused;
244     }
245 }}}
246 
247 ///////////////////////////////////////////////////////////////////////////////
248 namespace boost { namespace spirit { namespace result_of
249 {
250     template <typename Exposed, typename Attribute>
251     struct extract_from
252       : mpl::if_<
253             mpl::and_<
254                 traits::is_container<Attribute>
255               , traits::not_is_variant<Attribute>
256               , traits::not_is_optional<Attribute> >
257           , traits::extract_from_container<Attribute, Exposed>
258           , traits::extract_from_attribute<Attribute, Exposed> >::type
259     {};
260 
261     template <typename Exposed>
262     struct extract_from<Exposed, unused_type>
263     {
264         typedef unused_type type;
265     };
266 
267     template <typename Exposed>
268     struct extract_from<Exposed, unused_type const>
269     {
270         typedef unused_type type;
271     };
272 }}}
273 
274 #endif
275