Managing ref-counting is a complex and error prone business. If you choose C++ to extend or embed Python, you can simply use a modification on std::auto_ptr
. Instead of calling delete on the managed pointer, it will decref it.
So now you can do:
auto_pyptr pyHelloStr(PyStr_FromString("Hello World!"));
and forget about having to decref it altogether! Just like auto_ptr
you can get the PyObject *
with get()
, release it from managed control with release()
, and rebind it with reset(new_ptr)
. You can also incref it by calling inc()
, but be cautious as you can easily create a leak by increfing once to much.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /*
* auto_pyptr.h
*
* Originally from
* http://code.activestate.com/recipes/528875-automatic-ref-count-management-in-c-using-a-smart-/
*/
#ifndef AUTO_PYPTR_H_
#define AUTO_PYPTR_H_
#include <Python.h>
#include <memory>
typedef std::auto_ptr<PyObject> auto_pyptr_base;
/**
* An auto_ptr that, instead of deleting, decrements the reference count
* of a PyObject pointer.
*
* Make sure to only use this when you get a *new* reference (Py_INCREF or
* getting the result of any function that says it returns a new reference
* to a PyObject), NOT for "borrowed" references.
*/
class auto_pyptr : public auto_pyptr_base {
public:
auto_pyptr(PyObject * obj = NULL) : auto_pyptr_base(obj) {
}
~auto_pyptr() {
reset();
}
void reset(PyObject * obj = NULL) {
if(obj != get()) {
PyObject * old = release(); // Avoid the delete call
Py_XDECREF(old);
auto_pyptr_base::reset(obj);
}
}
void inc() {
PyObject * ptr = get();
if(ptr)
Py_INCREF(ptr);
}
/*
* Implement cast to PyObject pointer so you don't have to call var.get()
* every time you use the object.
*
* You still have to use get() in certain cases, notably varargs
* (i.e. "..."). GCC will warn you that this will abort at runtime.
*/
operator PyObject*() {
return this->get();
}
};
#endif /* AUTO_PYPTR_H_ */
|
I forked this recipe to add a cast to PyObject *
; this allows me to pass the pointer to other functions without calling get()
every time.
If you think this is a bad idea for some reason (e.g. some way I'm likely to shoot myself in the foot), feel free to let me know.