Michele De Stefano's C++ Utilities
obj.hpp
Go to the documentation of this file.
1 // obj.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_OBJ_HPP_INCLUDED
8 #define MDS_UTILS_PYTHON_OBJ_HPP_INCLUDED
9 
10 #include <Python.h>
11 #include <string>
12 #include <stdexcept>
14 
34 namespace mds_utils {
35  namespace python {
36 
38 
39 class Obj;
40 
41 template<>
42 Obj get<Obj>(PyObject *po);
43 
44 PyObject* to_python(const Obj& o);
45 
47 
48 
68 class Obj {
69 
73  void release_obj() {
74  while (decref_on_destroy > 0) decref();
75  m_po = NULL;
76  }
77 
78 protected:
79 
85 
86 
93  class ProxyAttr {
94 
95  Obj& m_o;
96 
97  const std::string&
98  attr_name;
99 
100  PyObject
101  *m_attr;
102 
103  void reset_m_attr() {
104  Py_XDECREF(m_attr);
105  m_attr = NULL;
106  }
107 
108  public:
109 
110  ProxyAttr(Obj& o,const std::string& attr) :
111  m_o(o),attr_name(attr),
112  m_attr(NULL) {
113 
114  if (m_o.has_attr(attr_name)) {
115  /*
116  * The assignment only under this condition
117  * allows to create new attributes on the fly,
118  * without throwing any exception.
119  */
120  m_attr = PyObject_GetAttrString(m_o,attr_name.c_str());
121  }
122  }
123 
124  ~ProxyAttr() {
125  reset_m_attr();
126  }
127 
128  operator PyObject* () {
129  return m_attr;
130  }
131 
132  operator Obj () {
133  Obj o(m_attr);
134  o.incref();
135  return o;
136  }
137 
138  template<class T>
139  ProxyAttr& operator =(const T& val) {
140 
141  reset_m_attr();
142  m_attr = to_python(val);
143  Py_XINCREF(m_attr);
144 
145  /*
146  * The following instruction can create new attributes
147  * if they don't already exist.
148  */
149  if (PyObject_SetAttrString(m_o,attr_name.c_str(),m_attr) == -1) {
150  throw std::runtime_error("Cannot assign to attribute " +
151  attr_name);
152  }
153 
154  return *this;
155  }
156 
157  Obj operator()() {
158  Obj oattr(m_attr);
159 
160  return oattr();
161  }
162  };
163 
164 
165  PyObject *m_po;
166 
175  void check_callable() {
176  if (!is_callable()) {
177  throw std::logic_error("The object is not callable");
178  }
179  }
180 
189  void check_call_result(PyObject *result) {
190  if (result == NULL) {
191  throw std::runtime_error("Object call failed.");
192  }
193  }
194 
195 public:
196 
198  Obj() : decref_on_destroy(0),m_po(NULL) {}
199 
210  Obj(const Obj& rhs) : decref_on_destroy(0),m_po(rhs.m_po) {
211  incref();
212  }
213 
222  Obj(Obj&& rhs) : decref_on_destroy(0),m_po(rhs.transfer()) {
223  get_ownership();
224  }
225 
226 
237  Obj(ProxyAttr&& rhs) : decref_on_destroy(0),m_po(rhs) {
238  incref();
239  }
240 
253  Obj(PyObject *po) : decref_on_destroy(0),m_po(po) {}
254 
255 
257  Obj& operator =(const Obj& rhs) {
258  if (&rhs == this) return *this;
259  reset();
260  m_po = rhs.m_po;
261  incref();
262  return *this;
263  }
264 
265 
267  Obj& operator =(Obj&& rhs) {
268  m_po = rhs.transfer();
269  get_ownership();
270  return *this;
271  }
272 
285  virtual Obj& operator =(PyObject *po) {
286  if (m_po == po) return *this;
287  reset();
288  m_po = po;
289  return *this;
290  }
291 
292 
307  template<class T>
308  Obj& operator =(const T& val) {
309  reset();
310  m_po = to_python(val);
311  get_ownership();
312  return *this;
313  }
314 
316  virtual ~Obj() { reset(); }
317 
326  PyObject* getPyObject() const {
327  return m_po;
328  }
329 
338  operator PyObject* () const {
339  return m_po;
340  }
341 
350  virtual PyObject* transfer() {
351  Py_XINCREF(m_po);
352  PyObject *pret(m_po);
353  reset();
354  return pret;
355  }
356 
363  virtual void incref() {
364  Py_XINCREF(m_po);
366  }
367 
369  virtual void decref() {
370  if (decref_on_destroy > 0) {
371  Py_XDECREF(m_po);
373  }
374  }
375 
377  void reset() { release_obj(); }
378 
380  void get_ownership() {
382  }
383 
384 
386  bool has_attr(const std::string& name) const {
387  return PyObject_HasAttrString(m_po,name.c_str()) == 1;
388  }
389 
390 
407  ProxyAttr attr(const std::string& name) {
408  return ProxyAttr(*this,name);
409  }
410 
411 
420  bool is_callable() const {
421  return PyCallable_Check(m_po) == 1;
422  }
423 
424 
436 
437  check_callable();
438 
439  PyObject*
440  result(PyObject_CallObject(*this,NULL));
441 
442  check_call_result(result);
443 
444  Obj o(result);
445 
446  // No need to get the ownership here. It will be the destination
447  // to take it, if needed
448 
449  return o;
450  }
451 
452 
467  Obj operator()(const Obj& args) {
468 
469  check_callable();
470 
471  PyObject*
472  result(PyObject_CallObject(*this,args));
473 
474  check_call_result(result);
475 
476  Obj o(result);
477 
478  // No need to get the ownership here. It will be the destination
479  // to take it, if needed
480 
481  return o;
482  }
483 
504  Obj operator()(const Obj& args,const Obj& kw) {
505 
506  check_callable();
507 
508  PyObject*
509  result(PyObject_Call(*this,args,kw));
510 
511  check_call_result(result);
512 
513  Obj o(result);
514 
515  // No need to get the ownership here. It will be the destination
516  // to take it, if needed
517 
518  return o;
519  }
520 
521 };
522 
523 
525 
526 template<>
527 inline Obj get<Obj>(PyObject *po) {
528  return Obj(po);
529 }
530 
531 inline PyObject* to_python(const Obj& o) {
532  PyObject *retval(o);
533  Py_XINCREF(retval);
534  return retval;
535 }
536 
538 
539 
540  }
541 }
542 
543 #endif
544 
545 
Proxy class for managing attribute access.
Definition: obj.hpp:93
void reset()
Resets the object to the state given by the default constructor.
Definition: obj.hpp:377
void check_call_result(PyObject *result)
Checks if the result of the object call was successful.
Definition: obj.hpp:189
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.
size_t decref_on_destroy
Definition: obj.hpp:84
Main namespace of all Michele De Stefano&#39;s C++ utilities.
Definition: endian.hpp:30
Obj(const Obj &rhs)
The copy-constructor.
Definition: obj.hpp:210
Obj()
Default constructor.
Definition: obj.hpp:198
bool is_callable() const
Checks if the object is callable.
Definition: obj.hpp:420
Obj operator()(const Obj &args, const Obj &kw)
Calling operator, with positional and keyword arguments.
Definition: obj.hpp:504
PyObject * m_po
Underlying pointer to the wrapped Python object.
Definition: obj.hpp:165
bool has_attr(const std::string &name) const
Tests if the object has a particular attribute.
Definition: obj.hpp:386
Obj(Obj &&rhs)
Move constructor.
Definition: obj.hpp:222
virtual ~Obj()
Destructor.
Definition: obj.hpp:316
void check_callable()
Checks if the object is callable.
Definition: obj.hpp:175
void get_ownership()
Used in place of incref, when the wrapped PyObject* was increfed already.
Definition: obj.hpp:380
Obj operator()()
Calling operator.
Definition: obj.hpp:435
virtual PyObject * transfer()
Returns the Python object with transferred ownership.
Definition: obj.hpp:350
Obj(ProxyAttr &&rhs)
Move constructor from ProxyAttr objects.
Definition: obj.hpp:237
ProxyAttr attr(const std::string &name)
Retrieves an attribute.
Definition: obj.hpp:407
virtual void decref()
Decrements the reference count using Py_XDECREF.
Definition: obj.hpp:369
PyObject * getPyObject() const
Returns the underlying PyObject.
Definition: obj.hpp:326
Utilities for converting data from/to Python.
Obj operator()(const Obj &args)
Calling operator, with positional arguments.
Definition: obj.hpp:467
Obj(PyObject *po)
Construct from a Python object.
Definition: obj.hpp:253
This is a simple wrapper around the PyObject* datatype.
Definition: obj.hpp:68