Reputation: 1051
I have created getsetters for a public variable number_bananas I have in my Box class. number_bananas is public because the box is unlocked, anyone can eat bananas or put more in the box.
Here is my PyBox type:
typedef struct
{
PyObject_HEAD
Box *bx;
} PyBox;
In my Box class I have defined:
class Box {
public:
Box(double l, double b, double h);
int number_bananas;
...
Box::Box(double l, double b, double h)
{
number_bananas = 11;
length = l;
breadth = b;
height = h;
}
and here are the get/setters I have defined:
static PyObject *pyBox_getBananas(PyBox *self, void *closure)
{
Py_INCREF(self->bx->number_bananas);
return self->bx->number_bananas;
}
static int pyBox_setBananas(PyBox *self, PyObject *value, void *closure)
{
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "You're trying to put something that is not a banana!!");
return -1;
}
Py_DECREF(self->bx->number_bananas);
Py_INCREF(value);
self->bx->number_bananas = value;
return 0;
}
static PyGetSetDef pyBox_getseters[] = {
{"number_bananas", (getter)pyBox_getBananas, (setter)pyBox_setBananas, "number of bananas", NULL},
{NULL}
};
The constructor I have used to instantiate the Box class is defined by:
static int pyBox_init(PyObject *self, PyObject *args, PyObject *kwds)
{
static char* nams[] = {"length","breadth","height", NULL};
int l, b, h;
if(!PyArg_ParseTupleAndKeywords(args, kwds, "iii", nams, &l, &b, &h))
return -1;
((PyBox *)self)->bx = &(Box::Box(l,b,h));
return 0;
}
static PyObject *pyBox_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyBox *self;
self = (PyBox *) type->tp_alloc(type, 0);
return (PyObject *)self;
}
The module compiles successfully. However, I cannot find how to access the property number_bananas from Python.
The following results in Fatal error (segfault):
import adventureIsland
bo = adventureIsland.box(1,1,1)
print(bo.number_bananas)
So... my question is how do I access/set the number_bananas from Python?
Thanks!
Upvotes: 1
Views: 92
Reputation: 30907
self->bx->number_bananas
is not a PyObject*
so can't be increfed or decrefed. Instead you want to be converting it to/from a PyObject*
. In Python3 you do this with various PyLong_*
functions.
Some untested code would be:
static PyObject *pyBox_getBananas(PyBox *self, void *closure)
{
return PyLong_FromLong(self->bx->number_bananas);
}
static int pyBox_setBananas(PyBox *self, PyObject *value, void *closure)
{
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "You're trying delete the attribute!!");
return -1;
}
int valuei = PyLong_AsLong(value);
if (valuei==-1 and PyErr_Occurrred()) {
return -1;
}
self->bx->number_bananas = valuei;
return 0;
}
Potentially there might be problems with too large numbers if int
(number_of_bananas
) and long
(the result of PyLong_AsLong
) aren't the same size, which it might be worth you trying to identify and raise an exception.
You were having further issues due to a constructor problem:
((PyBox *)self)->bx = &(Box::Box(l,b,h));
This sets bx
to point to a temporary Box
that stops existing almost as soon as it is created. What you should be doing is allocating a Box
on the heap using the new
operator. (What's happening now is that the memory you're pointing to is being reused in other functions so the "value" is changing in confusing ways).
((PyBox *)self)->bx = new Box::Box(l,b,h);
You should then make sure you delete
this in the destructor (to avoid a memory leak).
Upvotes: 1