]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/python/src/object/inheritance.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / python / src / object / inheritance.cpp
1 // Copyright David Abrahams 2002.
2 // Distributed under the Boost Software License, Version 1.0. (See
3 // accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 #include <boost/python/object/inheritance.hpp>
6 #include <boost/python/type_id.hpp>
7 #include <boost/graph/breadth_first_search.hpp>
8 #if _MSC_FULL_VER >= 13102171 && _MSC_FULL_VER <= 13102179
9 # include <boost/graph/reverse_graph.hpp>
10 #endif
11 #include <boost/graph/adjacency_list.hpp>
12 #include <boost/graph/reverse_graph.hpp>
13 #include <boost/property_map/property_map.hpp>
14 #include <boost/bind/bind.hpp>
15 #include <boost/integer_traits.hpp>
16 #include <boost/tuple/tuple.hpp>
17 #include <boost/tuple/tuple_comparison.hpp>
18 #include <queue>
19 #include <vector>
20 #include <functional>
21
22 //
23 // Procedure:
24 //
25 // The search is a BFS over the space of (type,address) pairs
26 // guided by the edges of the casting graph whose nodes
27 // correspond to classes, and whose edges are traversed by
28 // applying associated cast functions to an address. We use
29 // vertex distance to the goal node in the cast_graph to rate the
30 // paths. The vertex distance to any goal node is calculated on
31 // demand and outdated by the addition of edges to the graph.
32
33 namespace boost {
34 namespace
35 {
36 enum edge_cast_t { edge_cast = 8010 };
37 template <class T> inline void unused_variable(const T&) { }
38 }
39
40 // Install properties
41 BOOST_INSTALL_PROPERTY(edge, cast);
42
43 namespace
44 {
45 typedef void*(*cast_function)(void*);
46
47 //
48 // Here we put together the low-level data structures of the
49 // casting graph representation.
50 //
51 typedef python::type_info class_id;
52
53 // represents a graph of available casts
54
55 #if 0
56 struct cast_graph
57 :
58 #else
59 typedef
60 #endif
61 adjacency_list<vecS,vecS, bidirectionalS, no_property
62
63 // edge index property allows us to look up edges in the connectivity matrix
64 , property<edge_index_t,std::size_t
65
66 // The function which casts a void* from the edge's source type
67 // to its destination type.
68 , property<edge_cast_t,cast_function> > >
69 #if 0
70 {};
71 #else
72 cast_graph;
73 #endif
74
75 typedef cast_graph::vertex_descriptor vertex_t;
76 typedef cast_graph::edge_descriptor edge_t;
77
78 struct smart_graph
79 {
80 typedef std::vector<std::size_t>::const_iterator node_distance_map;
81
82 typedef std::pair<cast_graph::out_edge_iterator
83 , cast_graph::out_edge_iterator> out_edges_t;
84
85 // Return a map of the distances from any node to the given
86 // target node
87 node_distance_map distances_to(vertex_t target) const
88 {
89 std::size_t n = num_vertices(m_topology);
90 if (m_distances.size() != n * n)
91 {
92 m_distances.clear();
93 m_distances.resize(n * n, (std::numeric_limits<std::size_t>::max)());
94 m_known_vertices = n;
95 }
96
97 std::vector<std::size_t>::iterator to_target = m_distances.begin() + n * target;
98
99 // this node hasn't been used as a target yet
100 if (to_target[target] != 0)
101 {
102 typedef reverse_graph<cast_graph> reverse_cast_graph;
103 reverse_cast_graph reverse_topology(m_topology);
104
105 to_target[target] = 0;
106
107 breadth_first_search(
108 reverse_topology, target
109 , visitor(
110 make_bfs_visitor(
111 record_distances(
112 make_iterator_property_map(
113 to_target
114 , get(vertex_index, reverse_topology)
115 # ifdef BOOST_NO_STD_ITERATOR_TRAITS
116 , *to_target
117 # endif
118 )
119 , on_tree_edge()
120 ))));
121 }
122
123 return to_target;
124 }
125
126 cast_graph& topology() { return m_topology; }
127 cast_graph const& topology() const { return m_topology; }
128
129 smart_graph()
130 : m_known_vertices(0)
131 {}
132
133 private:
134 cast_graph m_topology;
135 mutable std::vector<std::size_t> m_distances;
136 mutable std::size_t m_known_vertices;
137 };
138
139 smart_graph& full_graph()
140 {
141 static smart_graph x;
142 return x;
143 }
144
145 smart_graph& up_graph()
146 {
147 static smart_graph x;
148 return x;
149 }
150
151 //
152 // Our index of class types
153 //
154 using boost::python::objects::dynamic_id_function;
155 typedef tuples::tuple<
156 class_id // static type
157 , vertex_t // corresponding vertex
158 , dynamic_id_function // dynamic_id if polymorphic, or 0
159 >
160 index_entry_interface;
161 typedef index_entry_interface::inherited index_entry;
162 enum { ksrc_static_t, kvertex, kdynamic_id };
163
164 typedef std::vector<index_entry> type_index_t;
165
166
167 type_index_t& type_index()
168 {
169 static type_index_t x;
170 return x;
171 }
172
173 template <class Tuple>
174 struct select1st
175 {
176 typedef typename tuples::element<0, Tuple>::type result_type;
177
178 result_type const& operator()(Tuple const& x) const
179 {
180 return tuples::get<0>(x);
181 }
182 };
183
184 // map a type to a position in the index
185 inline type_index_t::iterator type_position(class_id type)
186 {
187 using namespace boost::placeholders;
188 typedef index_entry entry;
189
190 return std::lower_bound(
191 type_index().begin(), type_index().end()
192 , boost::make_tuple(type, vertex_t(), dynamic_id_function(0))
193 , boost::bind<bool>(std::less<class_id>()
194 , boost::bind<class_id>(select1st<entry>(), _1)
195 , boost::bind<class_id>(select1st<entry>(), _2)));
196 }
197
198 inline index_entry* seek_type(class_id type)
199 {
200 type_index_t::iterator p = type_position(type);
201 if (p == type_index().end() || tuples::get<ksrc_static_t>(*p) != type)
202 return 0;
203 else
204 return &*p;
205 }
206
207 // Get the entry for a type, inserting if necessary
208 inline type_index_t::iterator demand_type(class_id type)
209 {
210 type_index_t::iterator p = type_position(type);
211
212 if (p != type_index().end() && tuples::get<ksrc_static_t>(*p) == type)
213 return p;
214
215 vertex_t v = add_vertex(full_graph().topology());
216 vertex_t v2 = add_vertex(up_graph().topology());
217 unused_variable(v2);
218 assert(v == v2);
219 return type_index().insert(p, boost::make_tuple(type, v, dynamic_id_function(0)));
220 }
221
222 // Map a two types to a vertex in the graph, inserting if necessary
223 typedef std::pair<type_index_t::iterator, type_index_t::iterator>
224 type_index_iterator_pair;
225
226 inline type_index_iterator_pair
227 demand_types(class_id t1, class_id t2)
228 {
229 // be sure there will be no reallocation
230 type_index().reserve(type_index().size() + 2);
231 type_index_t::iterator first = demand_type(t1);
232 type_index_t::iterator second = demand_type(t2);
233 if (first == second)
234 ++first;
235 return std::make_pair(first, second);
236 }
237
238 struct q_elt
239 {
240 q_elt(std::size_t distance
241 , void* src_address
242 , vertex_t target
243 , cast_function cast
244 )
245 : distance(distance)
246 , src_address(src_address)
247 , target(target)
248 , cast(cast)
249 {}
250
251 std::size_t distance;
252 void* src_address;
253 vertex_t target;
254 cast_function cast;
255
256 bool operator<(q_elt const& rhs) const
257 {
258 return distance < rhs.distance;
259 }
260 };
261
262 // Optimization:
263 //
264 // Given p, src_t, dst_t
265 //
266 // Get a pointer pd to the most-derived object
267 // if it's polymorphic, dynamic_cast to void*
268 // otherwise pd = p
269 //
270 // Get the most-derived typeid src_td
271 //
272 // ptrdiff_t offset = p - pd
273 //
274 // Now we can keep a cache, for [src_t, offset, src_td, dst_t] of
275 // the cast transformation function to use on p and the next src_t
276 // in the chain. src_td, dst_t don't change throughout this
277 // process. In order to represent unreachability, when a pair is
278 // found to be unreachable, we stick a 0-returning "dead-cast"
279 // function in the cache.
280
281 // This is needed in a few places below
282 inline void* identity_cast(void* p)
283 {
284 return p;
285 }
286
287 void* search(smart_graph const& g, void* p, vertex_t src, vertex_t dst)
288 {
289 // I think this test was thoroughly bogus -- dwa
290 // If we know there's no path; bail now.
291 // if (src > g.known_vertices() || dst > g.known_vertices())
292 // return 0;
293
294 smart_graph::node_distance_map d(g.distances_to(dst));
295
296 if (d[src] == (std::numeric_limits<std::size_t>::max)())
297 return 0;
298
299 typedef property_map<cast_graph,edge_cast_t>::const_type cast_map;
300 cast_map casts = get(edge_cast, g.topology());
301
302 typedef std::pair<vertex_t,void*> search_state;
303 typedef std::vector<search_state> visited_t;
304 visited_t visited;
305 std::priority_queue<q_elt> q;
306
307 q.push(q_elt(d[src], p, src, identity_cast));
308 while (!q.empty())
309 {
310 q_elt top = q.top();
311 q.pop();
312
313 // Check to see if we have a real state
314 void* dst_address = top.cast(top.src_address);
315 if (dst_address == 0)
316 continue;
317
318 if (top.target == dst)
319 return dst_address;
320
321 search_state s(top.target,dst_address);
322
323 visited_t::iterator pos = std::lower_bound(
324 visited.begin(), visited.end(), s);
325
326 // If already visited, continue
327 if (pos != visited.end() && *pos == s)
328 continue;
329
330 visited.insert(pos, s); // mark it
331
332 // expand it:
333 smart_graph::out_edges_t edges = out_edges(s.first, g.topology());
334 for (cast_graph::out_edge_iterator p = edges.first
335 , finish = edges.second
336 ; p != finish
337 ; ++p
338 )
339 {
340 edge_t e = *p;
341 q.push(q_elt(
342 d[target(e, g.topology())]
343 , dst_address
344 , target(e, g.topology())
345 , boost::get(casts, e)));
346 }
347 }
348 return 0;
349 }
350
351 struct cache_element
352 {
353 typedef tuples::tuple<
354 class_id // source static type
355 , class_id // target type
356 , std::ptrdiff_t // offset within source object
357 , class_id // source dynamic type
358 >::inherited key_type;
359
360 cache_element(key_type const& k)
361 : key(k)
362 , offset(0)
363 {}
364
365 key_type key;
366 std::ptrdiff_t offset;
367
368 BOOST_STATIC_CONSTANT(
369 std::ptrdiff_t, not_found = integer_traits<std::ptrdiff_t>::const_min);
370
371 bool operator<(cache_element const& rhs) const
372 {
373 return this->key < rhs.key;
374 }
375
376 bool unreachable() const
377 {
378 return offset == not_found;
379 }
380 };
381
382 enum { kdst_t = ksrc_static_t + 1, koffset, ksrc_dynamic_t };
383 typedef std::vector<cache_element> cache_t;
384
385 cache_t& cache()
386 {
387 static cache_t x;
388 return x;
389 }
390
391 inline void* convert_type(void* const p, class_id src_t, class_id dst_t, bool polymorphic)
392 {
393 // Quickly rule out unregistered types
394 index_entry* src_p = seek_type(src_t);
395 if (src_p == 0)
396 return 0;
397
398 index_entry* dst_p = seek_type(dst_t);
399 if (dst_p == 0)
400 return 0;
401
402 // Look up the dynamic_id function and call it to get the dynamic
403 // info
404 boost::python::objects::dynamic_id_t dynamic_id = polymorphic
405 ? tuples::get<kdynamic_id>(*src_p)(p)
406 : std::make_pair(p, src_t);
407
408 // Look in the cache first for a quickie address translation
409 std::ptrdiff_t offset = (char*)p - (char*)dynamic_id.first;
410
411 cache_element seek(boost::make_tuple(src_t, dst_t, offset, dynamic_id.second));
412 cache_t& c = cache();
413 cache_t::iterator const cache_pos
414 = std::lower_bound(c.begin(), c.end(), seek);
415
416
417 // if found in the cache, we're done
418 if (cache_pos != c.end() && cache_pos->key == seek.key)
419 {
420 return cache_pos->offset == cache_element::not_found
421 ? 0 : (char*)p + cache_pos->offset;
422 }
423
424 // If we are starting at the most-derived type, only look in the up graph
425 smart_graph const& g = polymorphic && dynamic_id.second != src_t
426 ? full_graph() : up_graph();
427
428 void* result = search(
429 g, p, tuples::get<kvertex>(*src_p)
430 , tuples::get<kvertex>(*dst_p));
431
432 // update the cache
433 c.insert(cache_pos, seek)->offset
434 = (result == 0) ? cache_element::not_found : (char*)result - (char*)p;
435
436 return result;
437 }
438 }
439
440 namespace python { namespace objects {
441
442 BOOST_PYTHON_DECL void* find_dynamic_type(void* p, class_id src_t, class_id dst_t)
443 {
444 return convert_type(p, src_t, dst_t, true);
445 }
446
447 BOOST_PYTHON_DECL void* find_static_type(void* p, class_id src_t, class_id dst_t)
448 {
449 return convert_type(p, src_t, dst_t, false);
450 }
451
452 BOOST_PYTHON_DECL void add_cast(
453 class_id src_t, class_id dst_t, cast_function cast, bool is_downcast)
454 {
455 // adding an edge will invalidate any record of unreachability in
456 // the cache.
457 static std::size_t expected_cache_len = 0;
458 cache_t& c = cache();
459 if (c.size() > expected_cache_len)
460 {
461 c.erase(std::remove_if(
462 c.begin(), c.end(),
463 mem_fn(&cache_element::unreachable))
464 , c.end());
465
466 // If any new cache entries get added, we'll have to do this
467 // again when the next edge is added
468 expected_cache_len = c.size();
469 }
470
471 type_index_iterator_pair types = demand_types(src_t, dst_t);
472 vertex_t src = tuples::get<kvertex>(*types.first);
473 vertex_t dst = tuples::get<kvertex>(*types.second);
474
475 cast_graph* const g[2] = { &up_graph().topology(), &full_graph().topology() };
476
477 for (cast_graph*const* p = g + (is_downcast ? 1 : 0); p < g + 2; ++p)
478 {
479 edge_t e;
480 bool added;
481
482 tie(e, added) = add_edge(src, dst, **p);
483 assert(added);
484
485 put(get(edge_cast, **p), e, cast);
486 put(get(edge_index, **p), e, num_edges(full_graph().topology()) - 1);
487 }
488 }
489
490 BOOST_PYTHON_DECL void register_dynamic_id_aux(
491 class_id static_id, dynamic_id_function get_dynamic_id)
492 {
493 tuples::get<kdynamic_id>(*demand_type(static_id)) = get_dynamic_id;
494 }
495
496 }}} // namespace boost::python::objects