Michele De Stefano's C++ Utilities
sequence.hpp
Go to the documentation of this file.
1 // mds_utils/python/sequence.hpp
2 //
3 // Copyright (c) 2014 - Michele De Stefano (micdestefano@users.sourceforge.net)
4 //
5 // Distributed under the MIT License (See accompanying file LICENSE)
6 
7 #ifndef MDS_UTILS_PYTHON_SEQUENCE_HPP_INCLUDED
8 #define MDS_UTILS_PYTHON_SEQUENCE_HPP_INCLUDED
9 
10 #include <mds_utils/python/obj.hpp>
11 #include <boost/fusion/include/for_each.hpp>
12 #include <boost/fusion/include/size.hpp>
13 #include <boost/lexical_cast.hpp>
14 #include <algorithm>
15 #include <iterator>
16 #include <utility>
17 #include <stdexcept>
18 
36 namespace mds_utils {
37  namespace python {
38 
54 inline size_t len(PyObject *o) {
55  Py_ssize_t retval(PySequence_Length(o));
56 
57  if (retval == -1) {
58  throw std::runtime_error("Cannot retrieve the length");
59  }
60 
61  return static_cast<size_t>(retval);
62 }
63 
93 template<class Derived>
94 class Sequence_Base : public Obj {
95 
96  class from_fusion_seq {
97 
98  Derived& self;
99 
100  volatile size_t pos;
101 
102  public:
103 
104  from_fusion_seq(Derived& seq) : self(seq),pos(0) {}
105 
106  template <typename T>
107  void operator()(T const& x) const {
108  from_fusion_seq* pself(const_cast<from_fusion_seq*>(this));
109  if (!self.set_item(pself->pos++,x)) {
110  throw std::runtime_error("Failed to insert element n. " +
111  boost::lexical_cast<std::string>(pos-1) +
112  " into Python sequence");
113  }
114  }
115  };
116 
117  friend class from_fusion_seq;
118 
119 
120  class ProxyElem {
121 
122  Sequence_Base<Derived>& m_seq;
123 
124  size_t m_i;
125 
126  PyObject* get_item() const {
127  PyObject* po = PySequence_GetItem(m_seq,m_i);
128  if (po == NULL) throw std::runtime_error(
129  "Cannot access element " +
130  boost::lexical_cast<std::string>(m_i));
131  return po;
132  }
133 
134  public:
135 
136  ProxyElem(Sequence_Base<Derived>& seq,size_t i) : m_seq(seq),m_i(i) {}
137 
138  // Returns a new reference
139  operator PyObject* () {
140  return get_item();
141  }
142 
143  operator Obj () {
144  Obj o(get_item());
145  o.get_ownership();
146  return o;
147  }
148 
149  template<class T>
150  ProxyElem& operator =(const T& val) {
151 
152  PyObject *v = to_python(val);
153 
154  int success(PySequence_SetItem(m_seq,m_i,v));
155 
156  Py_XDECREF(v);
157 
158  if (success == -1) {
159  throw std::runtime_error("Cannot assign to element " +
160  boost::lexical_cast<std::string>(m_i));
161  }
162 
163  return *this;
164  }
165  };
166 
167 
168 protected:
169 
170  size_t m_len;
171 
180  size_t idx(long i) const {
181  long Nel(static_cast<long>(m_len));
182  if (i < -Nel || i >= Nel) throw std::invalid_argument(
183  "Cannot access element " +
184  boost::lexical_cast<std::string>(i));
185  if (i >= 0) return static_cast<size_t>(i);
186  return static_cast<size_t>(i+Nel);
187  }
188 
189 
190 
191 public:
192 
199  Sequence_Base() : m_len(0) {}
200 
201 
212  Sequence_Base(size_t len) : Obj(Derived::new_seq(len)),m_len(len) {
213  get_ownership();
214  }
215 
216 
230  Sequence_Base(PyObject *po) : Obj(po) {
231  assert(Derived::self_type_check(*this));
232  m_len = PySequence_Size(po);
233  }
234 
235 
237  Sequence_Base(Sequence_Base&& rhs) : Obj(std::move(rhs)) {
238  assert(Derived::self_type_check(*this));
239  m_len = PySequence_Size(m_po);
240  }
241 
242 
244  Sequence_Base(const Sequence_Base& rhs) : Obj(rhs) {
245  assert(Derived::self_type_check(*this));
246  m_len = PySequence_Size(m_po);
247  }
248 
249 
258  Sequence_Base(ProxyAttr&& rhs) : Obj(std::move(rhs)) {
259  assert(Derived::self_type_check(*this));
260  m_len = PySequence_Size(m_po);
261  }
262 
265  Obj::operator =(std::move(rhs));
266  m_len = rhs.m_len;
267  return *this;
268  }
269 
270 
273  Obj::operator =(rhs);
274  m_len = rhs.m_len;
275  return *this;
276  }
277 
295  template<class seq_T>
296  void set(const seq_T& seq) {
297  reset();
298  get_ownership();
299  m_len = boost::fusion::size(seq);
300  m_po = Derived::new_seq(m_len);
301  if (m_po == NULL) {
302  throw std::runtime_error("Could not instantiate a new sequence");
303  }
304 
305  Derived& self(*static_cast<Derived*>(this));
306 
307  boost::fusion::for_each(seq,from_fusion_seq(self));
308  }
309 
310 
329  template<class FwIt>
330  void set(FwIt b,FwIt e) {
331 
332  reset();
333 
334  Derived& self(*static_cast<Derived*>(this));
335 
336  size_t m_len(std::distance(b,e));
337 
338  m_po = Derived::new_seq(m_len);
339 
340  if (m_po == NULL) {
341  throw std::runtime_error("Could not instantiate a new sequence");
342  }
343 
344  get_ownership();
345 
346  size_t k;
347 
348  for (k = 0;k < m_len;++k,++b) {
349  if (!self.set_item(k,to_python(*b))) {
350  throw std::runtime_error("Failed to insert "
351  "element n. " +
352  boost::lexical_cast<std::string>(k) +
353  " into Python sequence");
354  }
355  }
356  }
357 
358 
375  void set(const Obj& o) {
376  if (!PySequence_Check(o)) {
377  throw std::invalid_argument("Cannot construct a sequence from "
378  "a non-sequence object.");
379  }
380 
381  reset();
382 
383  m_len = PySequence_Size(o);
384 
385  if (Derived::self_type_check(o)) {
386  m_po = o;
387  incref();
388  return;
389  }
390 
391 
392  if ((m_po = Derived::new_seq(m_len)) == NULL) {
393  throw std::runtime_error("Could not instantiate a new sequence");
394  }
395 
396  get_ownership();
397 
398  size_t k;
399 
400  PyObject *po(NULL);
401 
402  Derived& self(*static_cast<Derived*>(this));
403 
404  for (k = 0;k < m_len;++k,po = NULL) {
405 
406  po = PySequence_GetItem(o,k);
407 
408  if (po == NULL) {
409  throw std::runtime_error(
410  "Failed extracting sequence element n. " +
411  boost::lexical_cast<std::string>(k) +
412  " from the input sequence");
413  }
414 
415  if (!self.set_item(k,po)) {
416  throw std::runtime_error("Failed to insert "
417  "element n. " +
418  boost::lexical_cast<std::string>(k) +
419  " into Python sequence");
420  }
421  }
422  }
423 
424 
426  void del(long i) {
427  if (PySequence_DelItem(*this,idx(i)) == -1) {
428  throw std::runtime_error("Cannot delete item.");
429  }
430  }
431 
432 
434  size_t len() const { return m_len; }
435 
436 
446  ProxyElem operator [](long i) {
447  return ProxyElem(*this,idx(i));
448  }
449 };
450 
451 
469 class Sequence : public Sequence_Base<Sequence> {
470 
471  static PyObject* new_seq(size_t len) {
472  return PyList_New(len);
473  }
474 
475  static bool self_type_check(const Obj& o) {
476  return PySequence_Check(o);
477  }
478 
479  template<class T>
480  bool set_item(size_t pos,T const& x) {
481  PyObject *po(to_python(x));
482  bool retval(PySequence_SetItem(m_po,pos,po) == 0);
483 
484  Py_XDECREF(po);
485 
486  return retval;
487  }
488 
489  friend class Sequence_Base<Sequence>;
490 
491 public:
492 
494  Sequence() {}
495 
496 
510  Sequence(PyObject *po) : Sequence_Base<Sequence>(po) {}
511 
512 
514  Sequence(const Sequence& rhs) : Sequence_Base<Sequence>(rhs) {}
515 
516 
518  Sequence(Sequence&& rhs) : Sequence_Base<Sequence>(std::move(rhs)) {}
519 
520 
522  Sequence(ProxyAttr&& rhs) : Sequence_Base<Sequence>(std::move(rhs)) {}
523 
524 
526  Sequence& operator =(Sequence&& rhs) {
527  Sequence_Base<Sequence>::operator =(std::move(rhs));
528  return *this;
529  }
530 
532  Sequence& operator =(const Sequence& rhs) {
534  return *this;
535  }
536 };
537 
538  }
539 }
540 
541 #endif
Proxy class for managing attribute access.
Definition: obj.hpp:93
Wraps a generic Python sequence.
Definition: sequence.hpp:469
void reset()
Resets the object to the state given by the default constructor.
Definition: obj.hpp:377
Sequence(ProxyAttr &&rhs)
The move constructor from ProxyAttr objects.
Definition: sequence.hpp:522
Sequence_Base(const Sequence_Base &rhs)
The copy-constructor.
Definition: sequence.hpp:244
virtual void incref()
Increments the reference count using Py_XINCREF.
Definition: obj.hpp:363
PyObject * to_python(void)
Converts a value into a Python object.
Sequence(Sequence &&rhs)
The move constructor.
Definition: sequence.hpp:518
Sequence_Base(size_t len)
Creates a sequence of the desired length.
Definition: sequence.hpp:212
Main namespace of all Michele De Stefano&#39;s C++ utilities.
Definition: endian.hpp:30
Obj()
Default constructor.
Definition: obj.hpp:198
Sequence(PyObject *po)
Constructs a new Sequence from a Python sequence.
Definition: sequence.hpp:510
Sequence_Base(ProxyAttr &&rhs)
Move constructor from ProxyAttr object.
Definition: sequence.hpp:258
Sequence()
The default constructor.
Definition: sequence.hpp:494
PyObject * m_po
Underlying pointer to the wrapped Python object.
Definition: obj.hpp:165
size_t len() const
Returns the length of the sequence.
Definition: sequence.hpp:434
Contains a wrapper class for the PyObject* datatype.
Sequence_Base()
Default constructor.
Definition: sequence.hpp:199
Sequence(const Sequence &rhs)
The copy constructor.
Definition: sequence.hpp:514
void get_ownership()
Used in place of incref, when the wrapped PyObject* was increfed already.
Definition: obj.hpp:380
void del(long i)
Deletes the item in position i.
Definition: sequence.hpp:426
ProxyElem operator[](long i)
Element access.
Definition: sequence.hpp:446
size_t len(PyObject *o)
Retrieves the length of a sequence object.
Definition: sequence.hpp:54
Obj operator()()
Calling operator.
Definition: obj.hpp:435
Obj & operator=(const Obj &rhs)
Standard assignment.
Definition: obj.hpp:257
Sequence_Base< Derived > & operator=(Sequence_Base< Derived > &&rhs)
Move assignment.
Definition: sequence.hpp:264
size_t m_len
The length of the sequence.
Definition: sequence.hpp:170
Sequence_Base(Sequence_Base &&rhs)
Move constructor.
Definition: sequence.hpp:237
Sequence_Base(PyObject *po)
Constructs a new Sequence_Base from a Python sequence.
Definition: sequence.hpp:230
size_t idx(long i) const
Converts a Python index (that can also be negative) into a C index.
Definition: sequence.hpp:180
Base class for all generic sequence types.
Definition: sequence.hpp:94
This is a simple wrapper around the PyObject* datatype.
Definition: obj.hpp:68