Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/python/src/object/inheritance.cpp @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

File size: 14.4 KB
Line 
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.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
33namespace boost {
34namespace
35{
36  enum edge_cast_t { edge_cast = 8010 };
37  template <class T> inline void unused_variable(const T&) { }
38}
39
40// Install properties
41BOOST_INSTALL_PROPERTY(edge, cast);
42
43namespace
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      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
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
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
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  {
239      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
255      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
281  inline void* identity_cast(void* p)
282  {
283      return p;
284  }
285
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
359      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     
370      bool operator<(cache_element const& rhs) const
371      {
372          return this->key < rhs.key;
373      }
374
375      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
384  cache_t& cache()
385  {
386      static cache_t x;
387      return x;
388  }
389
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
439namespace python { namespace objects {
440
441BOOST_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
446BOOST_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
451BOOST_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
489BOOST_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
Note: See TracBrowser for help on using the repository browser.