ecto
std_map_indexing_suite.hpp
Go to the documentation of this file.
1 // (C) Copyright Joel de Guzman 2003.
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 // Modified by Troy D. Straszheim and Jakob van Santen, 2009-03-26
6 // Pulled in to ecto in 2010-11 by Troy D. Straszheim
7 // Willow Garage BSD License not applicable
8 #ifndef ICETRAY_PYTHON_STD_MAP_INDEXING_SUITE_HPP_INCLUDED
9 # define ICETRAY_PYTHON_STD_MAP_INDEXING_SUITE_HPP_INCLUDED
10 
11 # include <ecto/python.hpp>
12 # include <boost/python/suite/indexing/indexing_suite.hpp>
13 # include <boost/python/iterator.hpp>
14 # include <boost/python/call_method.hpp>
15 # include <boost/python/tuple.hpp>
16 # include <boost/iterator/transform_iterator.hpp>
17 
18 namespace bp = boost::python;
19 
20 namespace boost { namespace python {
21 
22  // Forward declaration
23  template <class Container, bool NoProxy, class DerivedPolicies>
25 
26  namespace detail
27  {
28  template <class Container, bool NoProxy>
30  : public std_map_indexing_suite<Container,
31  NoProxy, final_std_map_derived_policies<Container, NoProxy> > {};
32  }
33 
34  // The map_indexing_suite class is a predefined indexing_suite derived
35  // class for wrapping std::vector (and std::vector like) classes. It provides
36  // all the policies required by the indexing_suite (see indexing_suite).
37  // Example usage:
38  //
39  // class X {...};
40  //
41  // ...
42  //
43  // class_<std::map<std::string, X> >("XMap")
44  // .def(map_indexing_suite<std::map<std::string, X> >())
45  // ;
46  //
47  // By default indexed elements are returned by proxy. This can be
48  // disabled by supplying *true* in the NoProxy template parameter.
49  //
50  template <
51  class Container,
52  bool NoProxy = false,
53  class DerivedPolicies
56  : public indexing_suite<
57  Container
58  , DerivedPolicies
59  , NoProxy
60  , true
61  , typename Container::value_type::second_type
62  , typename Container::key_type
63  , typename Container::key_type
64  >
65  {
66  public:
67 
68  typedef typename Container::value_type value_type;
69  typedef typename Container::value_type::second_type data_type;
70  typedef typename Container::key_type key_type;
71  typedef typename Container::key_type index_type;
72  typedef typename Container::size_type size_type;
73  typedef typename Container::difference_type difference_type;
74  typedef typename Container::const_iterator const_iterator;
75 
76 
77  // __getitem__ for std::pair
78 // FIXME: horrible (20x) performance regression vs. (pair.key(),pair.data())
79  static object pair_getitem(value_type const& x, int i) {
80  if (i==0 || i==-2) return object(x.first);
81  else if (i==1 || i==-1) return object(x.second);
82  else {
83  PyErr_SetString(PyExc_IndexError,"Index out of range.");
84  throw_error_already_set();
85  return object(); // None
86  }
87  }
88 
89 // __iter__ for std::pair
90 // here we cheat by making a tuple and returning its iterator
91 // FIXME: replace this with a pure C++ iterator
92 // how to handle the different return types of first and second?
93 static PyObject* pair_iter(value_type const& x) {
94 object tuple = bp::make_tuple(x.first,x.second);
95 return incref(tuple.attr("__iter__")().ptr());
96 }
97 
98  // __len__ std::pair = 2
99  static int pair_len(value_type const& x) { return 2; }
100 
101  // return a list of keys
102  static bp::list keys(Container const& x)
103  {
104  bp::list t;
105  for(typename Container::const_iterator it = x.begin(); it != x.end(); it++)
106  t.append(it->first);
107  return t;
108  }
109  // return a list of values
110  static bp::list values(Container const& x)
111  {
112  bp::list t;
113  for(typename Container::const_iterator it = x.begin(); it != x.end(); it++)
114  t.append(it->second);
115  return t;
116  }
117  // return a list of (key,value) tuples
118  static bp::list items(Container const& x)
119  {
120  bp::list t;
121  for(typename Container::const_iterator it = x.begin(); it != x.end(); it++)
122  t.append(bp::make_tuple(it->first, it->second));
123  return t;
124  }
125 
126 #if 0
127  // return a shallow copy of the map
128  // FIXME: is this actually a shallow copy, or did i duplicate the pairs?
129  static Container copy(Container const& x)
130  {
131  Container newmap;
132  for(const_iterator it = x.begin();it != x.end();it++) newmap.insert(*it);
133  return newmap;
134  }
135 #endif
136  // get with default value
137  static object dict_get(Container const& x, index_type const& k, object const& default_val = object())
138  {
139  const_iterator it = x.find(k);
140  if (it != x.end()) return object(it->second);
141  else return default_val;
142  }
143  // preserve default value info
144  BOOST_PYTHON_FUNCTION_OVERLOADS(dict_get_overloads, dict_get, 2, 3);
145 
146  // pop map[key], or throw an error if it doesn't exist
147  static object dict_pop(Container & x, index_type const& k)
148  {
149  const_iterator it = x.find(k);
150  object result;
151  if (it != x.end()) {
152  result = object(it->second);
153  x.erase(it->first);
154  return result;
155  }
156  else {
157  PyErr_SetString(PyExc_KeyError,"Key not found.");
158  throw_error_already_set();
159  return object(); // None
160  };
161  }
162 
163  // pop map[key], or return default_val if it doesn't exist
164  static object dict_pop_default(Container & x, index_type const& k, object const& default_val)
165  {
166  const_iterator it = x.find(k);
167  object result;
168  if (it != x.end()) {
169  result = object(it->second);
170  x.erase(it->first);
171  return result;
172  }
173  else return default_val;
174  }
175 
176  // pop a tuple, or throw an error if empty
177  static object dict_pop_item(Container & x)
178  {
179  const_iterator it = x.begin();
180  object result;
181  if (it != x.end()) {
182  result = boost::python::make_tuple(it->first,it->second);
183  x.erase(it->first);
184  return result;
185  }
186  else {
187  PyErr_SetString(PyExc_KeyError,"No more items to pop");
188  throw_error_already_set();
189  return object(); // None
190  };
191  }
192 
193  // create a new map with given keys, initialialized to value
194  static object dict_fromkeys(object const& keys, object const& value)
195  {
196  object newmap = object(typename Container::storage_type());
197  int numkeys = extract<int>(keys.attr("__len__")());
198  for(int i=0;i<numkeys;i++) { // 'cuz python is more fun in C++...
199  newmap.attr("__setitem__")
200  (keys.attr("__getitem__")(i),value);
201  }
202  return newmap;
203  }
204 
205  // spice up the constructors a bit
206  template <typename PyClassT>
207  struct init_factory {
208  typedef typename PyClassT::metadata::holder Holder;
209  typedef bp::objects::instance<Holder> instance_t;
210 
211  // connect the PyObject to a wrapped C++ instance
212  // borrowed from boost/python/object/make_holder.hpp
213  static void make_holder(PyObject *p)
214  {
215  void* memory = Holder::allocate(p, offsetof(instance_t, storage), sizeof(Holder));
216 
217  try {
218  // this only works for blank () constructors
219  (new (memory) Holder(p))->install(p);
220  }
221  catch(...) {
222  Holder::deallocate(p, memory);
223  throw;
224  }
225  }
226  static void from_dict(PyObject *p, bp::dict const& dict)
227  {
228  make_holder(p);
229  object newmap = object(bp::handle<>(borrowed(p)));
230  newmap.attr("update")(dict);
231  }
232  static void from_list(PyObject *p, bp::list const& list)
233  {
234  make_holder(p);
235  object newmap = object(bp::handle<>(borrowed(p)));
236  newmap.attr("update")(bp::dict(list));
237  }
238  };
239 
240  // copy keys and values from dictlike object (anything with keys())
241  static void dict_update(object & x, object const& dictlike)
242  {
243  object key;
244  object keys = dictlike.attr("keys")();
245  int numkeys = extract<int>(keys.attr("__len__")());
246  for(int i=0;i<numkeys;i++) {
247  key = keys.attr("__getitem__")(i);
248  x.attr("__setitem__")(key,dictlike.attr("__getitem__")(key));
249  }
250 
251  }
252 
253  // set up operators to sample the key, value, or a tuple from a std::pair
254  struct iterkeys
255  {
256  typedef key_type result_type;
257 
258  result_type operator()(value_type const& x) const
259  {
260  return x.first;
261  }
262  };
263 
264  struct itervalues
265  {
266  typedef data_type result_type;
267 
268  result_type operator()(value_type const& x) const
269  {
270  return x.second;
271  }
272  };
273 
274  struct iteritems {
275  typedef tuple result_type;
276 
277  result_type operator()(value_type const& x) const
278  {
279  return boost::python::make_tuple(x.first,x.second);
280  }
281  };
282 
283  template <typename Transform>
285  {
286  typedef boost::transform_iterator<Transform, const_iterator> iterator;
287 
288  static iterator begin(const Container& m)
289  {
290  return boost::make_transform_iterator(m.begin(), Transform());
291  }
292  static iterator end(const Container& m)
293  {
294  return boost::make_transform_iterator(m.end(), Transform());
295  }
296 
297  static bp::object range()
298  {
299  return bp::range(&begin, &end);
300  }
301  };
302 
303  template <typename Transform>
304  static bp::object
306  {
307  return make_transform_impl<Transform>::range();
308  }
309 
310  static object
311  print_elem(typename Container::value_type const& e)
312  {
313  return "(%s, %s)" % python::make_tuple(e.first, e.second);
314  }
315 
316  static
317  typename mpl::if_<
318  is_class<data_type>
319  , data_type&
320  , data_type
321  >::type
322  get_data(typename Container::value_type& e)
323  {
324  return e.second;
325  }
326 
327  static typename Container::key_type
328  get_key(typename Container::value_type& e)
329  {
330  return e.first;
331  }
332 
333  static data_type&
334  get_item(Container& container, index_type i_)
335  {
336  typename Container::iterator i = container.find(i_);
337  if (i == container.end())
338  {
339  PyErr_SetString(PyExc_KeyError, "Invalid key");
340  throw_error_already_set();
341  }
342  return i->second;
343  }
344 
345  static void
346  set_item(Container& container, index_type i, data_type const& v)
347  {
348  container[i] = v;
349  }
350 
351  static void
352  delete_item(Container& container, index_type i)
353  {
354  container.erase(i);
355  }
356 
357  static size_t
358  size(Container& container)
359  {
360  return container.size();
361  }
362 
363  static bool
364  contains(Container& container, key_type const& key)
365  {
366  return container.find(key) != container.end();
367  }
368 
369  static bool
370  compare_index(Container& container, index_type a, index_type b)
371  {
372  return container.key_comp()(a, b);
373  }
374 
375  static index_type
376  convert_index(Container& container, PyObject* i_)
377  {
378  extract<key_type const&> i(i_);
379  if (i.check())
380  {
381  return i();
382  }
383  else
384  {
385  extract<key_type> i(i_);
386  if (i.check())
387  return i();
388  }
389 
390  PyErr_SetString(PyExc_TypeError, "Invalid index type");
391  throw_error_already_set();
392  return index_type();
393  }
394 
395  template <class Class>
396  static void
397  extension_def(Class& cl)
398  {
399  // Wrap the map's element (value_type)
400  std::string elem_name = "std_map_indexing_suite_";
401  std::string cl_name;
402  object class_name(cl.attr("__name__"));
403  extract<std::string> class_name_extractor(class_name);
404  cl_name = class_name_extractor();
405  elem_name += cl_name;
406  elem_name += "_entry";
407 
408  typedef typename mpl::if_<
409  is_class<data_type>
410  , return_internal_reference<>
411  , default_call_policies
412  >::type get_data_return_policy;
413 
414  class_<value_type>(elem_name.c_str())
415  .def("__repr__", &DerivedPolicies::print_elem)
416  .def("data", &DerivedPolicies::get_data, get_data_return_policy(),
417  "K.data() -> the value associated with this pair.\n")
418  .def("key", &DerivedPolicies::get_key,
419  "K.key() -> the key associated with this pair.\n")
420  .def("__getitem__",&pair_getitem)
421  .def("__iter__",&pair_iter)
422  .def("__len__",&pair_len)
423  .def("first",&DerivedPolicies::get_key,
424  "K.first() -> the first item in this pair.\n")
425  .def("second",&DerivedPolicies::get_data, get_data_return_policy(),
426  "K.second() -> the second item in this pair.\n")
427  ;
428  // add convenience methods to the map
429 
430  cl
431  // declare constructors in descending order of arity
432  .def("__init__", init_factory<Class>::from_list,
433  "Initialize with keys and values from a Python dictionary: {'key':'value'}\n")
434  .def("__init__", init_factory<Class>::from_dict,
435  "Initialize with keys and values as tuples in a Python list: [('key','value')]\n")
436  .def(init<>()) // restore default constructor
437 
438  .def("keys", &keys, "D.keys() -> list of D's keys\n")
439  .def("has_key", &contains, "D.has_key(k) -> True if D has a key k, else False\n") // don't re-invent the wheel
440  .def("values", &values, "D.values() -> list of D's values\n")
441  .def("items", &items, "D.items() -> list of D's (key, value) pairs, as 2-tuples\n")
442  .def("clear", &Container::clear, "D.clear() -> None. Remove all items from D.\n")
443  //.def("copy", &copy, "D.copy() -> a shallow copy of D\n")
444  .def("get", dict_get, dict_get_overloads(args("default_val"),
445  "D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n"))
446  .def("pop", &dict_pop )
447  .def("pop", &dict_pop_default,
448  "D.pop(k[,d]) -> v, remove specified key and return the corresponding value\nIf key is not found, d is returned if given, otherwise KeyError is raised\n")
449  .def("popitem", &dict_pop_item,
450  "D.popitem() -> (k, v), remove and return some (key, value) pair as a\n2-tuple; but raise KeyError if D is empty\n")
451  .def("fromkeys", &dict_fromkeys,
452  (cl_name+".fromkeys(S,v) -> New "+cl_name+" with keys from S and values equal to v.\n").c_str())
453  .staticmethod("fromkeys")
454  .def("update", &dict_update,
455  "D.update(E) -> None. Update D from E: for k in E: D[k] = E[k]\n")
456  .def("iteritems",
457  make_transform<iteritems>(),
458  "D.iteritems() -> an iterator over the (key, value) items of D\n")
459  .def("iterkeys",
460  make_transform<iterkeys>(),
461  "D.iterkeys() -> an iterator over the keys of D\n")
462  .def("itervalues",
463  make_transform<itervalues>(),
464  "D.itervalues() -> an iterator over the values of D\n")
465  ;
466 
467  }
468 
469  };
470 
471 }} // namespace boost::python
472 
473 #endif // ICETRAY_PYTHON_STD_MAP_INDEXING_SUITE_HPP_INCLUDED
static void from_dict(PyObject *p, bp::dict const &dict)
Definition: std_map_indexing_suite.hpp:226
static void set_item(Container &container, index_type i, data_type const &v)
Definition: std_map_indexing_suite.hpp:346
Definition: std_map_indexing_suite.hpp:207
boost::transform_iterator< Transform, const_iterator > iterator
Definition: std_map_indexing_suite.hpp:286
static PyObject * pair_iter(value_type const &x)
Definition: std_map_indexing_suite.hpp:93
Definition: std_map_indexing_suite.hpp:20
static object dict_pop_default(Container &x, index_type const &k, object const &default_val)
Definition: std_map_indexing_suite.hpp:164
static object print_elem(typename Container::value_type const &e)
Definition: std_map_indexing_suite.hpp:311
static bool compare_index(Container &container, index_type a, index_type b)
Definition: std_map_indexing_suite.hpp:370
bp::objects::instance< Holder > instance_t
Definition: std_map_indexing_suite.hpp:209
Definition: std_map_indexing_suite.hpp:24
Definition: std_map_indexing_suite.hpp:284
static bp::object make_transform()
Definition: std_map_indexing_suite.hpp:305
static bool contains(Container &container, key_type const &key)
Definition: std_map_indexing_suite.hpp:364
static object dict_fromkeys(object const &keys, object const &value)
Definition: std_map_indexing_suite.hpp:194
Container::value_type::second_type data_type
Definition: std_map_indexing_suite.hpp:69
static object dict_pop_item(Container &x)
Definition: std_map_indexing_suite.hpp:177
static Container::key_type get_key(typename Container::value_type &e)
Definition: std_map_indexing_suite.hpp:328
static void extension_def(Class &cl)
Definition: std_map_indexing_suite.hpp:397
static object pair_getitem(value_type const &x, int i)
Definition: std_map_indexing_suite.hpp:79
static object dict_pop(Container &x, index_type const &k)
Definition: std_map_indexing_suite.hpp:147
static mpl::if_< is_class< data_type >, data_type &, data_type >::type get_data(typename Container::value_type &e)
Definition: std_map_indexing_suite.hpp:322
Container::const_iterator const_iterator
Definition: std_map_indexing_suite.hpp:74
static void delete_item(Container &container, index_type i)
Definition: std_map_indexing_suite.hpp:352
result_type operator()(value_type const &x) const
Definition: std_map_indexing_suite.hpp:258
Container::difference_type difference_type
Definition: std_map_indexing_suite.hpp:73
Container::size_type size_type
Definition: std_map_indexing_suite.hpp:72
static object dict_get(Container const &x, index_type const &k, object const &default_val=object())
Definition: std_map_indexing_suite.hpp:137
result_type operator()(value_type const &x) const
Definition: std_map_indexing_suite.hpp:268
static iterator end(const Container &m)
Definition: std_map_indexing_suite.hpp:292
Definition: std_map_indexing_suite.hpp:264
tuple result_type
Definition: std_map_indexing_suite.hpp:275
key_type result_type
Definition: std_map_indexing_suite.hpp:256
Definition: std_map_indexing_suite.hpp:29
static void dict_update(object &x, object const &dictlike)
Definition: std_map_indexing_suite.hpp:241
static bp::list items(Container const &x)
Definition: std_map_indexing_suite.hpp:118
static index_type convert_index(Container &container, PyObject *i_)
Definition: std_map_indexing_suite.hpp:376
data_type result_type
Definition: std_map_indexing_suite.hpp:266
static void make_holder(PyObject *p)
Definition: std_map_indexing_suite.hpp:213
static size_t size(Container &container)
Definition: std_map_indexing_suite.hpp:358
PyClassT::metadata::holder Holder
Definition: std_map_indexing_suite.hpp:208
static int pair_len(value_type const &x)
Definition: std_map_indexing_suite.hpp:99
static bp::list values(Container const &x)
Definition: std_map_indexing_suite.hpp:110
static void from_list(PyObject *p, bp::list const &list)
Definition: std_map_indexing_suite.hpp:232
result_type operator()(value_type const &x) const
Definition: std_map_indexing_suite.hpp:277
static iterator begin(const Container &m)
Definition: std_map_indexing_suite.hpp:288
static data_type & get_item(Container &container, index_type i_)
Definition: std_map_indexing_suite.hpp:334
Definition: std_map_indexing_suite.hpp:274
Definition: std_map_indexing_suite.hpp:20
Container::key_type index_type
Definition: std_map_indexing_suite.hpp:71
Container::value_type value_type
Definition: std_map_indexing_suite.hpp:68
static bp::list keys(Container const &x)
Definition: std_map_indexing_suite.hpp:102
Container::key_type key_type
Definition: std_map_indexing_suite.hpp:70
static bp::object range()
Definition: std_map_indexing_suite.hpp:297
Definition: std_map_indexing_suite.hpp:254