summaryrefslogtreecommitdiff
path: root/boost/type_erasure/binding.hpp
blob: c360baad9af0075b713d501e62ddbdf6825cdb08 (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
// Boost.TypeErasure library
//
// Copyright 2011 Steven Watanabe
//
// 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)
//
// $Id$

#ifndef BOOST_TYPE_ERASURE_BINDING_HPP_INCLUDED
#define BOOST_TYPE_ERASURE_BINDING_HPP_INCLUDED

#include <boost/config.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/find_if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_erasure/static_binding.hpp>
#include <boost/type_erasure/is_subconcept.hpp>
#include <boost/type_erasure/detail/adapt_to_vtable.hpp>
#include <boost/type_erasure/detail/null.hpp>
#include <boost/type_erasure/detail/rebind_placeholders.hpp>
#include <boost/type_erasure/detail/vtable.hpp>
#include <boost/type_erasure/detail/normalize.hpp>
#include <boost/type_erasure/detail/instantiate.hpp>
#include <boost/type_erasure/detail/check_map.hpp>

namespace boost {
namespace type_erasure {

template<class P>
class dynamic_binding;

namespace detail {

template<class Source, class Dest, class Map>
struct can_optimize_conversion : ::boost::mpl::and_<
    ::boost::is_same<Source, Dest>,
        ::boost::is_same<
            typename ::boost::mpl::find_if<
                Map,
                ::boost::mpl::not_<
                    ::boost::is_same<
                        ::boost::mpl::first< ::boost::mpl::_1>,
                        ::boost::mpl::second< ::boost::mpl::_1>
                    >
                >
            >::type,
            typename ::boost::mpl::end<Map>::type
        >
    >::type
{};

}

/**
 * Stores the binding of a @c Concept to a set of actual types.
 * @c Concept is interpreted in the same way as with @ref any.
 */
template<class Concept>
class binding
{
    typedef typename ::boost::type_erasure::detail::normalize_concept<
        Concept>::type normalized;
    typedef typename ::boost::mpl::transform<normalized,
        ::boost::type_erasure::detail::maybe_adapt_to_vtable< ::boost::mpl::_1>
    >::type actual_concept;
    typedef typename ::boost::type_erasure::detail::make_vtable<
        actual_concept>::type table_type;
    typedef typename ::boost::type_erasure::detail::get_placeholder_normalization_map<
        Concept
    >::type placeholder_subs;
public:

    /**
     * \pre @ref relaxed must be in @c Concept.
     *
     * \throws Nothing.
     */
    binding() { BOOST_MPL_ASSERT((::boost::type_erasure::is_relaxed<Concept>)); }
    
    /**
     * \pre @c Map must be an MPL map with an entry for each placeholder
     *      referred to by @c Concept.
     *
     * \throws Nothing.
     */
    template<class Map>
    explicit binding(const Map&)
      : impl((
            BOOST_TYPE_ERASURE_INSTANTIATE(Concept, Map),
            static_binding<Map>()
        ))
    {}
    
    /**
     * \pre @c Map must be an MPL map with an entry for each placeholder
     *      referred to by @c Concept.
     *
     * \throws Nothing.
     */
    template<class Map>
    binding(const static_binding<Map>&)
      : impl((
            BOOST_TYPE_ERASURE_INSTANTIATE(Concept, Map),
            static_binding<Map>()
        ))
    {}

    /**
     * Converts from another set of bindings.
     *
     * \pre Map must be an MPL map with an entry for each placeholder
     *      referred to by @c Concept.  The mapped type should be the
     *      corresponding placeholder in Concept2.
     *
     * \throws std::bad_alloc
     */
    template<class Concept2, class Map>
    binding(const binding<Concept2>& other, const Map&
#ifndef BOOST_TYPE_ERASURE_DOXYGEN
        , typename ::boost::enable_if<
            ::boost::mpl::and_<
                ::boost::type_erasure::detail::check_map<Concept, Map>,
                ::boost::type_erasure::is_subconcept<Concept, Concept2, Map>
            >
        >::type* = 0
#endif
        )
      : impl(
            other,
            static_binding<Map>(),
            ::boost::type_erasure::detail::can_optimize_conversion<Concept2, Concept, Map>()
        )
    {}

    /**
     * Converts from another set of bindings.
     *
     * \pre Map must be an MPL map with an entry for each placeholder
     *      referred to by @c Concept.  The mapped type should be the
     *      corresponding placeholder in Concept2.
     *
     * \throws std::bad_alloc
     */
    template<class Concept2, class Map>
    binding(const binding<Concept2>& other, const static_binding<Map>&
#ifndef BOOST_TYPE_ERASURE_DOXYGEN
        , typename ::boost::enable_if<
            ::boost::mpl::and_<
                ::boost::type_erasure::detail::check_map<Concept, Map>,
                ::boost::type_erasure::is_subconcept<Concept, Concept2, Map>
            >
        >::type* = 0
#endif
        )
      : impl(
            other,
            static_binding<Map>(),
            ::boost::type_erasure::detail::can_optimize_conversion<Concept2, Concept, Map>()
        )
    {}

    /**
     * Converts from another set of bindings.
     *
     * \pre Map must be an MPL map with an entry for each placeholder
     *      referred to by @c Concept.  The mapped type should be the
     *      corresponding placeholder in Concept2.
     *
     * \throws std::bad_alloc
     * \throws std::bad_any_cast
     */
    template<class Placeholders, class Map>
    binding(const dynamic_binding<Placeholders>& other, const static_binding<Map>&)
      : impl(
            other,
            static_binding<Map>()
        )
    {}

    /**
     * \return true iff the sets of types that the placeholders
     *         bind to are the same for both arguments.
     *
     * \throws Nothing.
     */
    friend bool operator==(const binding& lhs, const binding& rhs)
    { return *lhs.impl.table == *rhs.impl.table; }
    
    /**
     * \return true iff the arguments do not map to identical
     *         sets of types.
     *
     * \throws Nothing.
     */
    friend bool operator!=(const binding& lhs, const binding& rhs)
    { return !(lhs == rhs); }

    /** INTERNAL ONLY */
    template<class T>
    typename T::type find() const { return impl.table->lookup((T*)0); }
private:
    template<class C2>
    friend class binding;
    template<class P>
    friend class dynamic_binding;
    /** INTERNAL ONLY */
    struct impl_type
    {
        impl_type() {
            table = &::boost::type_erasure::detail::make_vtable_init<
                typename ::boost::mpl::transform<
                    actual_concept,
                    ::boost::type_erasure::detail::get_null_vtable_entry<
                        ::boost::mpl::_1
                    >
                >::type,
                table_type
            >::type::value;
        }
        template<class Map>
        impl_type(const static_binding<Map>&)
        {
            table = &::boost::type_erasure::detail::make_vtable_init<
                typename ::boost::mpl::transform<
                    actual_concept,
                    ::boost::type_erasure::detail::rebind_placeholders<
                        ::boost::mpl::_1,
                        typename ::boost::type_erasure::detail::add_deductions<
                            Map,
                            placeholder_subs
                        >::type
                    >
                >::type,
                table_type
            >::type::value;
        }
        template<class Concept2, class Map>
        impl_type(const binding<Concept2>& other, const static_binding<Map>&, boost::mpl::false_)
          : manager(new table_type)
        {
            manager->template convert_from<
                typename ::boost::type_erasure::detail::convert_deductions<
                    Map,
                    placeholder_subs,
                    typename binding<Concept2>::placeholder_subs
                >::type
            >(*other.impl.table);
            table = manager.get();
        }
        template<class PlaceholderList, class Map>
        impl_type(const dynamic_binding<PlaceholderList>& other, const static_binding<Map>&)
          : manager(new table_type)
        {
            manager->template convert_from<
                // FIXME: What do we need to do with deduced placeholder in other
                typename ::boost::type_erasure::detail::add_deductions<
                    Map,
                    placeholder_subs
                >::type
            >(other.impl);
            table = manager.get();
        }
        template<class Concept2, class Map>
        impl_type(const binding<Concept2>& other, const static_binding<Map>&, boost::mpl::true_)
          : table(other.impl.table),
            manager(other.impl.manager)
        {}
        const table_type* table;
        ::boost::shared_ptr<table_type> manager;
    } impl;
};

}
}

#endif