1 //  Copyright (c) 2001-2011 Hartmut Kaiser
2 //  Copyright (c) 2001-2011 Joel de Guzman
3 //
4 //  Distributed under the Boost Software License, Version 1.0. (See accompanying
5 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 
7 #ifndef BOOST_SPIRIT_KARMA_DETAIL_ALTERNATIVE_FUNCTION_HPP
8 #define BOOST_SPIRIT_KARMA_DETAIL_ALTERNATIVE_FUNCTION_HPP
9 
10 #if defined(_MSC_VER)
11 #pragma once
12 #endif
13 
14 #include <boost/spirit/home/karma/domain.hpp>
15 #include <boost/spirit/home/karma/directive/buffer.hpp>
16 #include <boost/spirit/home/support/unused.hpp>
17 #include <boost/spirit/home/support/utree/utree_traits_fwd.hpp>
18 #include <boost/spirit/home/karma/detail/attributes.hpp>
19 #include <boost/spirit/home/support/detail/hold_any.hpp>
20 #include <boost/spirit/home/karma/detail/output_iterator.hpp>
21 #include <boost/spirit/home/support/container.hpp>
22 #include <boost/utility/enable_if.hpp>
23 #include <boost/variant.hpp>
24 #include <boost/detail/workaround.hpp>
25 
26 ///////////////////////////////////////////////////////////////////////////////
27 namespace boost { namespace spirit { namespace karma { namespace detail
28 {
29     ///////////////////////////////////////////////////////////////////////////
30     //  execute a generator if the given Attribute type is compatible
31     ///////////////////////////////////////////////////////////////////////////
32 
33     //  this gets instantiated if the Attribute type is _not_ compatible with
34     //  the generator
35     template <typename Component, typename Attribute, typename Expected
36       , typename Enable = void>
37     struct alternative_generate
38     {
39         template <typename OutputIterator, typename Context, typename Delimiter>
40         static bool
callboost::spirit::karma::detail::alternative_generate41         call(Component const&, OutputIterator&, Context&, Delimiter const&
42           , Attribute const&, bool& failed)
43         {
44             failed = true;
45             return false;
46         }
47     };
48 
49     template <typename Component>
50     struct alternative_generate<Component, unused_type, unused_type>
51     {
52         template <typename OutputIterator, typename Context, typename Delimiter>
53         static bool
callboost::spirit::karma::detail::alternative_generate54         call(Component const& component, OutputIterator& sink, Context& ctx
55           , Delimiter const& d, unused_type, bool&)
56         {
57 #if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
58             component; // suppresses warning: C4100: 'component' : unreferenced formal parameter
59 #endif
60             // return true if any of the generators succeed
61             return component.generate(sink, ctx, d, unused);
62         }
63     };
64 
65     //  this gets instantiated if there is no Attribute given for the
66     //  alternative generator
67     template <typename Component, typename Expected>
68     struct alternative_generate<Component, unused_type, Expected>
69       : alternative_generate<Component, unused_type, unused_type> {};
70 
71     //  this gets instantiated if the generator does not expect to receive an
72     //  Attribute (the generator is self contained).
73     template <typename Component, typename Attribute>
74     struct alternative_generate<Component, Attribute, unused_type>
75       : alternative_generate<Component, unused_type, unused_type> {};
76 
77     //  this gets instantiated if the Attribute type is compatible to the
78     //  generator
79     template <typename Component, typename Attribute, typename Expected>
80     struct alternative_generate<Component, Attribute, Expected
81       , typename enable_if<
82             traits::compute_compatible_component<Expected, Attribute, karma::domain> >::type>
83     {
84         template <typename OutputIterator, typename Context, typename Delimiter>
85         static bool
callboost::spirit::karma::detail::alternative_generate86         call(Component const& component, OutputIterator& sink
87           , Context& ctx, Delimiter const& d, Attribute const& attr, bool&)
88         {
89 #if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
90             component; // suppresses warning: C4100: 'component' : unreferenced formal parameter
91 #endif
92             return call(component, sink, ctx, d, attr
93               , spirit::traits::not_is_variant_or_variant_in_optional<Attribute, karma::domain>());
94         }
95 
96         template <typename OutputIterator, typename Context, typename Delimiter>
97         static bool
callboost::spirit::karma::detail::alternative_generate98         call(Component const& component, OutputIterator& sink
99           , Context& ctx, Delimiter const& d, Attribute const& attr, mpl::true_)
100         {
101 #if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
102             component; // suppresses warning: C4100: 'component' : unreferenced formal parameter
103 #endif
104             return component.generate(sink, ctx, d, attr);
105         }
106 
107         template <typename OutputIterator, typename Context, typename Delimiter>
108         static bool
callboost::spirit::karma::detail::alternative_generate109         call(Component const& component, OutputIterator& sink
110           , Context& ctx, Delimiter const& d, Attribute const& attr, mpl::false_)
111         {
112 #if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
113             component; // suppresses warning: C4100: 'component' : unreferenced formal parameter
114 #endif
115             typedef
116                 traits::compute_compatible_component<Expected, Attribute, domain>
117             component_type;
118 
119             // if we got passed an empty optional, just fail generation
120             if (!traits::has_optional_value(attr))
121                 return false;
122 
123             // make sure, the content of the passed variant matches our
124             // expectations
125             typename traits::optional_attribute<Attribute>::type attr_ =
126                 traits::optional_value(attr);
127             if (!component_type::is_compatible(spirit::traits::which(attr_)))
128                 return false;
129 
130             // returns true if any of the generators succeed
131             typedef typename component_type::compatible_type compatible_type;
132             return component.generate(sink, ctx, d
133               , boost::get<compatible_type>(attr_));
134         }
135     };
136 
137     ///////////////////////////////////////////////////////////////////////////
138     //  alternative_generate_function: a functor supplied to fusion::any which
139     //  will be executed for every generator in a given alternative generator
140     //  expression
141     ///////////////////////////////////////////////////////////////////////////
142     template <typename OutputIterator, typename Context, typename Delimiter,
143         typename Attribute, typename Strict>
144     struct alternative_generate_function
145     {
alternative_generate_functionboost::spirit::karma::detail::alternative_generate_function146         alternative_generate_function(OutputIterator& sink_, Context& ctx_
147               , Delimiter const& d, Attribute const& attr_)
148           : sink(sink_), ctx(ctx_), delim(d), attr(attr_) {}
149 
150         template <typename Component>
operator ()boost::spirit::karma::detail::alternative_generate_function151         bool operator()(Component const& component)
152         {
153             typedef
154                 typename traits::attribute_of<Component, Context>::type
155             expected_type;
156             typedef
157                 alternative_generate<Component, Attribute, expected_type>
158             generate;
159 
160             // wrap the given output iterator avoid output as long as one
161             // component fails
162             detail::enable_buffering<OutputIterator> buffering(sink);
163             bool r = false;
164             bool failed = false;    // will be ignored
165             {
166                 detail::disable_counting<OutputIterator> nocounting(sink);
167                 r = generate::call(component, sink, ctx, delim, attr, failed);
168             }
169             if (r)
170                 buffering.buffer_copy();
171             return r;
172         }
173 
174         // avoid double buffering
175         template <typename Component>
operator ()boost::spirit::karma::detail::alternative_generate_function176         bool operator()(buffer_directive<Component> const& component)
177         {
178             typedef typename
179                 traits::attribute_of<Component, Context>::type
180             expected_type;
181             typedef alternative_generate<
182                 buffer_directive<Component>, Attribute, expected_type>
183             generate;
184 
185             bool failed = false;    // will be ignored
186             return generate::call(component, sink, ctx, delim, attr, failed);
187         }
188 
189         OutputIterator& sink;
190         Context& ctx;
191         Delimiter const& delim;
192         Attribute const& attr;
193 
194         // silence MSVC warning C4512: assignment operator could not be generated
195         BOOST_DELETED_FUNCTION(alternative_generate_function& operator= (alternative_generate_function const&))
196     };
197 
198     // specialization for strict alternatives
199     template <typename OutputIterator, typename Context, typename Delimiter,
200         typename Attribute>
201     struct alternative_generate_function<
202         OutputIterator, Context, Delimiter, Attribute, mpl::true_>
203     {
alternative_generate_functionboost::spirit::karma::detail::alternative_generate_function204         alternative_generate_function(OutputIterator& sink_, Context& ctx_
205               , Delimiter const& d, Attribute const& attr_)
206           : sink(sink_), ctx(ctx_), delim(d), attr(attr_), failed(false) {}
207 
208         template <typename Component>
operator ()boost::spirit::karma::detail::alternative_generate_function209         bool operator()(Component const& component)
210         {
211             typedef
212                 typename traits::attribute_of<Component, Context>::type
213             expected_type;
214             typedef
215                 alternative_generate<Component, Attribute, expected_type>
216             generate;
217 
218             if (failed)
219                 return false;     // give up when already failed
220 
221             // wrap the given output iterator avoid output as long as one
222             // component fails
223             detail::enable_buffering<OutputIterator> buffering(sink);
224             bool r = false;
225             {
226                 detail::disable_counting<OutputIterator> nocounting(sink);
227                 r = generate::call(component, sink, ctx, delim, attr, failed);
228             }
229             if (r && !failed)
230             {
231                 buffering.buffer_copy();
232                 return true;
233             }
234             return false;
235         }
236 
237         OutputIterator& sink;
238         Context& ctx;
239         Delimiter const& delim;
240         Attribute const& attr;
241         bool failed;
242 
243         // silence MSVC warning C4512: assignment operator could not be generated
244         BOOST_DELETED_FUNCTION(alternative_generate_function& operator= (alternative_generate_function const&))
245     };
246 }}}}
247 
248 #endif
249