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.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 };
unused_variable(const T &)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
distances_toboost::__anonb4f5f5ff0211::smart_graph87 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
topologyboost::__anonb4f5f5ff0211::smart_graph126 cast_graph& topology() { return m_topology; }
topologyboost::__anonb4f5f5ff0211::smart_graph127 cast_graph const& topology() const { return m_topology; }
128
smart_graphboost::__anonb4f5f5ff0211::smart_graph129 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
full_graph()139 smart_graph& full_graph()
140 {
141 static smart_graph x;
142 return x;
143 }
144
up_graph()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
type_index()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
operator ()boost::__anonb4f5f5ff0211::select1st178 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
type_position(class_id type)185 inline type_index_t::iterator type_position(class_id type)
186 {
187 typedef index_entry entry;
188
189 return std::lower_bound(
190 type_index().begin(), type_index().end()
191 , boost::make_tuple(type, vertex_t(), dynamic_id_function(0))
192 , boost::bind<bool>(std::less<class_id>()
193 , boost::bind<class_id>(select1st<entry>(), _1)
194 , boost::bind<class_id>(select1st<entry>(), _2)));
195 }
196
seek_type(class_id type)197 inline index_entry* seek_type(class_id type)
198 {
199 type_index_t::iterator p = type_position(type);
200 if (p == type_index().end() || tuples::get<ksrc_static_t>(*p) != type)
201 return 0;
202 else
203 return &*p;
204 }
205
206 // Get the entry for a type, inserting if necessary
demand_type(class_id type)207 inline type_index_t::iterator demand_type(class_id type)
208 {
209 type_index_t::iterator p = type_position(type);
210
211 if (p != type_index().end() && tuples::get<ksrc_static_t>(*p) == type)
212 return p;
213
214 vertex_t v = add_vertex(full_graph().topology());
215 vertex_t v2 = add_vertex(up_graph().topology());
216 unused_variable(v2);
217 assert(v == v2);
218 return type_index().insert(p, boost::make_tuple(type, v, dynamic_id_function(0)));
219 }
220
221 // Map a two types to a vertex in the graph, inserting if necessary
222 typedef std::pair<type_index_t::iterator, type_index_t::iterator>
223 type_index_iterator_pair;
224
225 inline type_index_iterator_pair
demand_types(class_id t1,class_id t2)226 demand_types(class_id t1, class_id t2)
227 {
228 // be sure there will be no reallocation
229 type_index().reserve(type_index().size() + 2);
230 type_index_t::iterator first = demand_type(t1);
231 type_index_t::iterator second = demand_type(t2);
232 if (first == second)
233 ++first;
234 return std::make_pair(first, second);
235 }
236
237 struct q_elt
238 {
q_eltboost::__anonb4f5f5ff0211::q_elt239 q_elt(std::size_t distance
240 , void* src_address
241 , vertex_t target
242 , cast_function cast
243 )
244 : distance(distance)
245 , src_address(src_address)
246 , target(target)
247 , cast(cast)
248 {}
249
250 std::size_t distance;
251 void* src_address;
252 vertex_t target;
253 cast_function cast;
254
operator <boost::__anonb4f5f5ff0211::q_elt255 bool operator<(q_elt const& rhs) const
256 {
257 return distance < rhs.distance;
258 }
259 };
260
261 // Optimization:
262 //
263 // Given p, src_t, dst_t
264 //
265 // Get a pointer pd to the most-derived object
266 // if it's polymorphic, dynamic_cast to void*
267 // otherwise pd = p
268 //
269 // Get the most-derived typeid src_td
270 //
271 // ptrdiff_t offset = p - pd
272 //
273 // Now we can keep a cache, for [src_t, offset, src_td, dst_t] of
274 // the cast transformation function to use on p and the next src_t
275 // in the chain. src_td, dst_t don't change throughout this
276 // process. In order to represent unreachability, when a pair is
277 // found to be unreachable, we stick a 0-returning "dead-cast"
278 // function in the cache.
279
280 // This is needed in a few places below
identity_cast(void * p)281 inline void* identity_cast(void* p)
282 {
283 return p;
284 }
285
search(smart_graph const & g,void * p,vertex_t src,vertex_t dst)286 void* search(smart_graph const& g, void* p, vertex_t src, vertex_t dst)
287 {
288 // I think this test was thoroughly bogus -- dwa
289 // If we know there's no path; bail now.
290 // if (src > g.known_vertices() || dst > g.known_vertices())
291 // return 0;
292
293 smart_graph::node_distance_map d(g.distances_to(dst));
294
295 if (d[src] == (std::numeric_limits<std::size_t>::max)())
296 return 0;
297
298 typedef property_map<cast_graph,edge_cast_t>::const_type cast_map;
299 cast_map casts = get(edge_cast, g.topology());
300
301 typedef std::pair<vertex_t,void*> search_state;
302 typedef std::vector<search_state> visited_t;
303 visited_t visited;
304 std::priority_queue<q_elt> q;
305
306 q.push(q_elt(d[src], p, src, identity_cast));
307 while (!q.empty())
308 {
309 q_elt top = q.top();
310 q.pop();
311
312 // Check to see if we have a real state
313 void* dst_address = top.cast(top.src_address);
314 if (dst_address == 0)
315 continue;
316
317 if (top.target == dst)
318 return dst_address;
319
320 search_state s(top.target,dst_address);
321
322 visited_t::iterator pos = std::lower_bound(
323 visited.begin(), visited.end(), s);
324
325 // If already visited, continue
326 if (pos != visited.end() && *pos == s)
327 continue;
328
329 visited.insert(pos, s); // mark it
330
331 // expand it:
332 smart_graph::out_edges_t edges = out_edges(s.first, g.topology());
333 for (cast_graph::out_edge_iterator p = edges.first
334 , finish = edges.second
335 ; p != finish
336 ; ++p
337 )
338 {
339 edge_t e = *p;
340 q.push(q_elt(
341 d[target(e, g.topology())]
342 , dst_address
343 , target(e, g.topology())
344 , boost::get(casts, e)));
345 }
346 }
347 return 0;
348 }
349
350 struct cache_element
351 {
352 typedef tuples::tuple<
353 class_id // source static type
354 , class_id // target type
355 , std::ptrdiff_t // offset within source object
356 , class_id // source dynamic type
357 >::inherited key_type;
358
cache_elementboost::__anonb4f5f5ff0211::cache_element359 cache_element(key_type const& k)
360 : key(k)
361 , offset(0)
362 {}
363
364 key_type key;
365 std::ptrdiff_t offset;
366
367 BOOST_STATIC_CONSTANT(
368 std::ptrdiff_t, not_found = integer_traits<std::ptrdiff_t>::const_min);
369
operator <boost::__anonb4f5f5ff0211::cache_element370 bool operator<(cache_element const& rhs) const
371 {
372 return this->key < rhs.key;
373 }
374
unreachableboost::__anonb4f5f5ff0211::cache_element375 bool unreachable() const
376 {
377 return offset == not_found;
378 }
379 };
380
381 enum { kdst_t = ksrc_static_t + 1, koffset, ksrc_dynamic_t };
382 typedef std::vector<cache_element> cache_t;
383
cache()384 cache_t& cache()
385 {
386 static cache_t x;
387 return x;
388 }
389
convert_type(void * const p,class_id src_t,class_id dst_t,bool polymorphic)390 inline void* convert_type(void* const p, class_id src_t, class_id dst_t, bool polymorphic)
391 {
392 // Quickly rule out unregistered types
393 index_entry* src_p = seek_type(src_t);
394 if (src_p == 0)
395 return 0;
396
397 index_entry* dst_p = seek_type(dst_t);
398 if (dst_p == 0)
399 return 0;
400
401 // Look up the dynamic_id function and call it to get the dynamic
402 // info
403 boost::python::objects::dynamic_id_t dynamic_id = polymorphic
404 ? tuples::get<kdynamic_id>(*src_p)(p)
405 : std::make_pair(p, src_t);
406
407 // Look in the cache first for a quickie address translation
408 std::ptrdiff_t offset = (char*)p - (char*)dynamic_id.first;
409
410 cache_element seek(boost::make_tuple(src_t, dst_t, offset, dynamic_id.second));
411 cache_t& c = cache();
412 cache_t::iterator const cache_pos
413 = std::lower_bound(c.begin(), c.end(), seek);
414
415
416 // if found in the cache, we're done
417 if (cache_pos != c.end() && cache_pos->key == seek.key)
418 {
419 return cache_pos->offset == cache_element::not_found
420 ? 0 : (char*)p + cache_pos->offset;
421 }
422
423 // If we are starting at the most-derived type, only look in the up graph
424 smart_graph const& g = polymorphic && dynamic_id.second != src_t
425 ? full_graph() : up_graph();
426
427 void* result = search(
428 g, p, tuples::get<kvertex>(*src_p)
429 , tuples::get<kvertex>(*dst_p));
430
431 // update the cache
432 c.insert(cache_pos, seek)->offset
433 = (result == 0) ? cache_element::not_found : (char*)result - (char*)p;
434
435 return result;
436 }
437 }
438
439 namespace python { namespace objects {
440
find_dynamic_type(void * p,class_id src_t,class_id dst_t)441 BOOST_PYTHON_DECL void* find_dynamic_type(void* p, class_id src_t, class_id dst_t)
442 {
443 return convert_type(p, src_t, dst_t, true);
444 }
445
find_static_type(void * p,class_id src_t,class_id dst_t)446 BOOST_PYTHON_DECL void* find_static_type(void* p, class_id src_t, class_id dst_t)
447 {
448 return convert_type(p, src_t, dst_t, false);
449 }
450
add_cast(class_id src_t,class_id dst_t,cast_function cast,bool is_downcast)451 BOOST_PYTHON_DECL void add_cast(
452 class_id src_t, class_id dst_t, cast_function cast, bool is_downcast)
453 {
454 // adding an edge will invalidate any record of unreachability in
455 // the cache.
456 static std::size_t expected_cache_len = 0;
457 cache_t& c = cache();
458 if (c.size() > expected_cache_len)
459 {
460 c.erase(std::remove_if(
461 c.begin(), c.end(),
462 mem_fn(&cache_element::unreachable))
463 , c.end());
464
465 // If any new cache entries get added, we'll have to do this
466 // again when the next edge is added
467 expected_cache_len = c.size();
468 }
469
470 type_index_iterator_pair types = demand_types(src_t, dst_t);
471 vertex_t src = tuples::get<kvertex>(*types.first);
472 vertex_t dst = tuples::get<kvertex>(*types.second);
473
474 cast_graph* const g[2] = { &up_graph().topology(), &full_graph().topology() };
475
476 for (cast_graph*const* p = g + (is_downcast ? 1 : 0); p < g + 2; ++p)
477 {
478 edge_t e;
479 bool added;
480
481 tie(e, added) = add_edge(src, dst, **p);
482 assert(added);
483
484 put(get(edge_cast, **p), e, cast);
485 put(get(edge_index, **p), e, num_edges(full_graph().topology()) - 1);
486 }
487 }
488
register_dynamic_id_aux(class_id static_id,dynamic_id_function get_dynamic_id)489 BOOST_PYTHON_DECL void register_dynamic_id_aux(
490 class_id static_id, dynamic_id_function get_dynamic_id)
491 {
492 tuples::get<kdynamic_id>(*demand_type(static_id)) = get_dynamic_id;
493 }
494
495 }}} // namespace boost::python::objects
496