summaryrefslogtreecommitdiff
path: root/boost/spirit/home/karma/detail/pass_container.hpp
blob: 07f2dad3c1e96e4061e1e7cd4023b422da22fe9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*=============================================================================
    Copyright (c) 2001-2011 Hartmut Kaiser
    Copyright (c) 2001-2011 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#if !defined(SPIRIT_PASS_CONTAINER_MAR_15_2009_0114PM)
#define SPIRIT_PASS_CONTAINER_MAR_15_2009_0114PM

#if defined(_MSC_VER)
#pragma once
#endif

#include <boost/spirit/home/karma/detail/attributes.hpp>
#include <boost/spirit/home/support/container.hpp>
#include <boost/spirit/home/support/handles_container.hpp>
#include <boost/spirit/home/support/detail/hold_any.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/or.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/fusion/include/deduce_sequence.hpp>

#include <boost/mpl/print.hpp>

namespace boost { namespace spirit { namespace karma { namespace detail
{
    // Helper meta-function allowing to evaluate weak substitutability and
    // negate the result if the predicate (Sequence) is not true
    template <typename Sequence, typename Attribute, typename ValueType>
    struct negate_weak_substitute_if_not
      : mpl::if_<
            Sequence
          , typename traits::is_weak_substitute<Attribute, ValueType>::type
          , typename mpl::not_<
                traits::is_weak_substitute<Attribute, ValueType>
            >::type>
    {};

    // pass_through_container: utility to check decide whether a provided
    // container attribute needs to be passed through to the current component
    // or of we need to split the container by passing along instances of its
    // value type

    // if the expected attribute of the current component is neither a Fusion
    // sequence nor a container, we will pass through the provided container
    // only if its value type is not compatible with the component
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence, typename Enable = void>
    struct pass_through_container_base
      : negate_weak_substitute_if_not<Sequence, ValueType, Attribute>
    {};

    // Specialization for fusion sequences, in this case we check whether all
    // the types in the sequence are convertible to the lhs attribute.
    //
    // We return false if the rhs attribute itself is a fusion sequence, which
    // is compatible with the LHS sequence (we want to pass through this
    // attribute without it being split apart).
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence = mpl::true_>
    struct not_compatible_element
      : mpl::and_<
            negate_weak_substitute_if_not<Sequence, Container, Attribute>
          , negate_weak_substitute_if_not<Sequence, ValueType, Attribute> >
    {};

    // If the value type of the container is not a Fusion sequence, we pass
    // through the container if each of the elements of the Attribute
    // sequence is compatible with either the container or its value type.
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence
      , bool IsSequence = fusion::traits::is_sequence<ValueType>::value>
    struct pass_through_container_fusion_sequence
    {
        typedef typename mpl::find_if<
            Attribute, not_compatible_element<Container, ValueType, mpl::_1>
        >::type iter;
        typedef typename mpl::end<Attribute>::type end;

        typedef typename is_same<iter, end>::type type;
    };

    // If both, the Attribute and the value type of the provided container
    // are Fusion sequences, we pass the container only if the two
    // sequences are not compatible.
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence>
    struct pass_through_container_fusion_sequence<
            Container, ValueType, Attribute, Sequence, true>
    {
        typedef typename mpl::find_if<
            Attribute
          , not_compatible_element<Container, ValueType, mpl::_1, Sequence>
        >::type iter;
        typedef typename mpl::end<Attribute>::type end;

        typedef typename is_same<iter, end>::type type;
    };

    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence>
    struct pass_through_container_base<Container, ValueType, Attribute
          , Sequence
          , typename enable_if<fusion::traits::is_sequence<Attribute> >::type>
      : pass_through_container_fusion_sequence<
            Container, ValueType, Attribute, Sequence>
    {};

    // Specialization for containers
    //
    // If the value type of the attribute of the current component is not
    // a Fusion sequence, we have to pass through the provided container if
    // both are compatible.
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence, typename AttributeValueType
      , bool IsSequence = fusion::traits::is_sequence<AttributeValueType>::value>
    struct pass_through_container_container
      : mpl::or_<
            traits::is_weak_substitute<Container, Attribute>
          , traits::is_weak_substitute<Container, AttributeValueType> >
    {};

    // If the value type of the exposed container attribute is a Fusion
    // sequence, we use the already existing logic for those.
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence, typename AttributeValueType>
    struct pass_through_container_container<
            Container, ValueType, Attribute, Sequence, AttributeValueType, true>
      : pass_through_container_fusion_sequence<
            Container, ValueType, AttributeValueType, Sequence>
    {};

    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence>
    struct pass_through_container_base<Container, ValueType, Attribute
          , Sequence
          , typename enable_if<traits::is_container<Attribute> >::type>
      : detail::pass_through_container_container<
          Container, ValueType, Attribute, Sequence
        , typename traits::container_value<Attribute>::type>
    {};

    // Specialization for exposed optional attributes
    //
    // If the type embedded in the exposed optional is not a Fusion
    // sequence we pass through the container attribute if it is compatible
    // either to the optionals embedded type or to the containers value
    // type.
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence
      , bool IsSequence = fusion::traits::is_sequence<Attribute>::value>
    struct pass_through_container_optional
      : mpl::or_<
            traits::is_weak_substitute<Container, Attribute>
          , traits::is_weak_substitute<ValueType, Attribute> >
    {};

    // If the embedded type of the exposed optional attribute is a Fusion
    // sequence, we use the already existing logic for those.
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence>
    struct pass_through_container_optional<
            Container, ValueType, Attribute, Sequence, true>
      : pass_through_container_fusion_sequence<
            Container, ValueType, Attribute, Sequence>
    {};

    ///////////////////////////////////////////////////////////////////////////
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence>
    struct pass_through_container
      : pass_through_container_base<Container, ValueType, Attribute, Sequence>
    {};

    // Handle optional attributes
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence>
    struct pass_through_container<
            Container, ValueType, boost::optional<Attribute>, Sequence>
      : pass_through_container_optional<
            Container, ValueType, Attribute, Sequence>
    {};

    // If both, the containers value type and the exposed attribute type are
    // optionals we are allowed to pass through the the container only if the
    // embedded types of those optionals are not compatible.
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence>
    struct pass_through_container<
            Container, boost::optional<ValueType>, boost::optional<Attribute>
          , Sequence>
      : mpl::not_<traits::is_weak_substitute<ValueType, Attribute> >
    {};

    // Specialization for exposed variant attributes
    //
    // We pass through the container attribute if at least one of the embedded
    // types in the variant requires to pass through the attribute

#define BOOST_SPIRIT_PASS_THROUGH_CONTAINER(z, N, _)                          \
    pass_through_container<Container, ValueType,                              \
        BOOST_PP_CAT(T, N), Sequence>::type::value ||                         \
    /***/

    // make sure unused variant parameters do not affect the outcome
    template <typename Container, typename ValueType, typename Sequence>
    struct pass_through_container<Container, ValueType
          , boost::detail::variant::void_, Sequence>
      : mpl::false_
    {};

    template <typename Container, typename ValueType, typename Sequence
      , BOOST_VARIANT_ENUM_PARAMS(typename T)>
    struct pass_through_container<Container, ValueType
          , boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>, Sequence>
      : mpl::bool_<BOOST_PP_REPEAT(BOOST_VARIANT_LIMIT_TYPES
          , BOOST_SPIRIT_PASS_THROUGH_CONTAINER, _) false>
    {};

#undef BOOST_SPIRIT_PASS_THROUGH_CONTAINER
}}}}

///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit { namespace traits
{
    ///////////////////////////////////////////////////////////////////////////
    // forwarding customization point for domain karma::domain
    template <typename Container, typename ValueType, typename Attribute
      , typename Sequence>
    struct pass_through_container<
            Container, ValueType, Attribute, Sequence, karma::domain>
      : karma::detail::pass_through_container<
            Container, ValueType, Attribute, Sequence>
    {};
}}}

namespace boost { namespace spirit { namespace karma { namespace detail
{
    ///////////////////////////////////////////////////////////////////////////
    // This function handles the case where the attribute (Attr) given
    // to the sequence is an STL container. This is a wrapper around F.
    // The function F does the actual generating.
    template <typename F, typename Attr, typename Iterator, typename Sequence>
    struct pass_container
    {
        typedef typename F::context_type context_type;

        pass_container(F const& f, Iterator begin, Iterator end)
          : f(f), iter(begin), end(end)
        {}

        bool is_at_end() const
        {
            return traits::compare(iter, end);
        }

        void next()
        {
            traits::next(iter);
        }

        // this is for the case when the current element expects an attribute
        // which is taken from the next entry in the container
        template <typename Component>
        bool dispatch_container(Component const& component, mpl::false_) const
        {
            // get the next value to generate from container
            if (!is_at_end() && !f(component, traits::deref(iter)))
            {
                // needs to return false as long as everything is ok
                traits::next(iter);
                return false;
            }

            // either no elements available any more or generation failed
            return true;
        }

        // this is for the case when the current element is able to handle an
        // attribute which is a container itself, this element will push its
        // data directly into the attribute container
        template <typename Component>
        bool dispatch_container(Component const& component, mpl::true_) const
        {
            return f(component, make_iterator_range(iter, end));
        }

        ///////////////////////////////////////////////////////////////////////
        // this is for the case when the current element doesn't expect an
        // attribute
        template <typename Component>
        bool dispatch_attribute(Component const& component, mpl::false_) const
        {
            return f(component, unused);
        }

        // the current element expects an attribute
        template <typename Component>
        bool dispatch_attribute(Component const& component, mpl::true_) const
        {
            typedef typename traits::container_value<Attr>::type value_type;
            typedef typename
                traits::attribute_of<Component, context_type>::type
            lhs_attribute;

            // this predicate detects, whether the value type of the container
            // attribute is a substitute for the attribute of the current
            // element
            typedef mpl::and_<
                traits::handles_container<Component, Attr, context_type>
              , traits::pass_through_container<
                    Attr, value_type, lhs_attribute, Sequence, karma::domain>
            > predicate;

            return dispatch_container(component, predicate());
        }

        // Dispatches to dispatch_main depending on the attribute type
        // of the Component
        template <typename Component>
        bool operator()(Component const& component) const
        {
            // we need to dispatch depending on the type of the attribute
            // of the current element (component). If this is has no attribute
            // we shouldn't use an element of the container but unused_type
            // instead
            typedef traits::not_is_unused<
                typename traits::attribute_of<Component, context_type>::type
            > predicate;

            return dispatch_attribute(component, predicate());
        }

        F f;
        mutable Iterator iter;
        mutable Iterator end;

    private:
        // silence MSVC warning C4512: assignment operator could not be generated
        pass_container& operator= (pass_container const&);
    };
}}}}

#endif