Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2009-04-19 16:46:39 +0400
committerCampbell Barton <ideasman42@gmail.com>2009-04-19 16:46:39 +0400
commit8d2cb5bea44f4245dd17f2d82cbd0251d8090fd5 (patch)
treeb28e45f1edaf083c15d2176079836a4497685e57 /source/gameengine/Expressions/PyObjectPlus.h
parent92cea7c1b1540d11ed9729bacfabd23ccb7a79c7 (diff)
BGE Python API
This changes how the BGE classes and Python work together, which hasnt changed since blender went opensource. The main difference is PyObjectPlus - the base class for most game engine classes, no longer inherit from PyObject, and cannot be cast to a PyObject. This has the advantage that the BGE does not have to keep 2 reference counts valid for C++ and Python. Previously C++ classes would never be freed while python held a reference, however this reference could be problematic eg: a GameObject that isnt in a scene anymore should not be used by python, doing so could even crash blender in some cases. Instead PyObjectPlus has a member "PyObject *m_proxy" which is lazily initialized when python needs it. m_proxy reference counts are managed by python, though it should never be freed while the C++ class exists since it holds a reference to avoid making and freeing it all the time. When the C++ class is free'd it sets the m_proxy reference to NULL, If python accesses this variable it will raise a RuntimeError, (check the isValid attribute to see if its valid without raising an error). - This replaces the m_zombie bool and IsZombie() tests added recently. In python return values that used to be.. return value->AddRef(); Are now return value->GetProxy(); or... return value->NewProxy(true); // true means python owns this C++ value which will be deleted when the PyObject is freed
Diffstat (limited to 'source/gameengine/Expressions/PyObjectPlus.h')
-rw-r--r--source/gameengine/Expressions/PyObjectPlus.h137
1 files changed, 72 insertions, 65 deletions
diff --git a/source/gameengine/Expressions/PyObjectPlus.h b/source/gameengine/Expressions/PyObjectPlus.h
index 58a74e4ca74..3be9a2f2bcb 100644
--- a/source/gameengine/Expressions/PyObjectPlus.h
+++ b/source/gameengine/Expressions/PyObjectPlus.h
@@ -81,6 +81,20 @@ static inline void Py_Fatal(const char *M) {
exit(-1);
};
+typedef struct {
+ PyObject_HEAD /* required python macro */
+ class PyObjectPlus *ref;
+ bool py_owns;
+} PyObjectPlus_Proxy;
+
+#define BGE_PROXY_ERROR_MSG "Blender Game Engine data has been freed, cannot use this python variable"
+#define BGE_PROXY_REF(_self) (((PyObjectPlus_Proxy *)_self)->ref)
+#define BGE_PROXY_PYOWNS(_self) (((PyObjectPlus_Proxy *)_self)->py_owns)
+
+/* Note, sometimes we dont care what BGE type this is as long as its a proxy */
+#define BGE_PROXY_CHECK_TYPE(_self) ((_self)->ob_type->tp_dealloc == PyDestructor)
+
+
// This must be the first line of each
// PyC++ class
#define Py_Header \
@@ -90,7 +104,10 @@ static inline void Py_Fatal(const char *M) {
static PyAttributeDef Attributes[]; \
static PyParentObject Parents[]; \
virtual PyTypeObject *GetType(void) {return &Type;}; \
- virtual PyParentObject *GetParents(void) {return Parents;}
+ virtual PyParentObject *GetParents(void) {return Parents;} \
+ virtual PyObject *GetProxy() {return GetProxy_Ext(this, &Type);}; \
+ virtual PyObject *NewProxy(bool py_owns) {return NewProxy_Ext(this, &Type, py_owns);}; \
+
@@ -106,7 +123,7 @@ static inline void Py_Fatal(const char *M) {
if (PyCObject_Check(descr)) { \
return py_get_attrdef((void *)this, (const PyAttributeDef*)PyCObject_AsVoidPtr(descr)); \
} else if (descr->ob_type->tp_descr_get) { \
- return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, (PyObject *)this); \
+ return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, this->m_proxy); \
} else { \
fprintf(stderr, "unknown attribute type"); \
return descr; \
@@ -165,52 +182,60 @@ static inline void Py_Fatal(const char *M) {
#define KX_PYMETHOD(class_name, method_name) \
PyObject* Py##method_name(PyObject* self, PyObject* args, PyObject* kwds); \
static PyObject* sPy##method_name( PyObject* self, PyObject* args, PyObject* kwds) { \
- return ((class_name*) self)->Py##method_name(self, args, kwds); \
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self); \
+ return ((class_name*)self_plus)->Py##method_name(self, args, kwds); \
}; \
#define KX_PYMETHOD_VARARGS(class_name, method_name) \
PyObject* Py##method_name(PyObject* self, PyObject* args); \
static PyObject* sPy##method_name( PyObject* self, PyObject* args) { \
- return ((class_name*) self)->Py##method_name(self, args); \
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self); \
+ return ((class_name*)self_plus)->Py##method_name(self, args); \
}; \
#define KX_PYMETHOD_NOARGS(class_name, method_name) \
PyObject* Py##method_name(PyObject* self); \
static PyObject* sPy##method_name( PyObject* self) { \
- return ((class_name*) self)->Py##method_name(self); \
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self); \
+ return ((class_name*)self_plus)->Py##method_name(self); \
}; \
#define KX_PYMETHOD_O(class_name, method_name) \
PyObject* Py##method_name(PyObject* self, PyObject* value); \
static PyObject* sPy##method_name( PyObject* self, PyObject* value) { \
- return ((class_name*) self)->Py##method_name(self, value); \
+ PyObjectPlus *self_plus= ((PyObjectPlus_Proxy *)self)->ref; \
+ return ((class_name*) self_plus)->Py##method_name(self, value); \
}; \
#define KX_PYMETHOD_DOC(class_name, method_name) \
PyObject* Py##method_name(PyObject* self, PyObject* args, PyObject* kwds); \
static PyObject* sPy##method_name( PyObject* self, PyObject* args, PyObject* kwds) { \
- return ((class_name*) self)->Py##method_name(self, args, kwds); \
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self); \
+ return ((class_name*)self_plus)->Py##method_name(self, args, kwds); \
}; \
static const char method_name##_doc[]; \
#define KX_PYMETHOD_DOC_VARARGS(class_name, method_name) \
PyObject* Py##method_name(PyObject* self, PyObject* args); \
static PyObject* sPy##method_name( PyObject* self, PyObject* args) { \
- return ((class_name*) self)->Py##method_name(self, args); \
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self); \
+ return ((class_name*)self_plus)->Py##method_name(self, args); \
}; \
static const char method_name##_doc[]; \
#define KX_PYMETHOD_DOC_O(class_name, method_name) \
PyObject* Py##method_name(PyObject* self, PyObject* value); \
static PyObject* sPy##method_name( PyObject* self, PyObject* value) { \
- return ((class_name*) self)->Py##method_name(self, value); \
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self); \
+ return ((class_name*)self_plus)->Py##method_name(self, value); \
}; \
static const char method_name##_doc[]; \
#define KX_PYMETHOD_DOC_NOARGS(class_name, method_name) \
PyObject* Py##method_name(PyObject* self); \
static PyObject* sPy##method_name( PyObject* self) { \
- return ((class_name*) self)->Py##method_name(self); \
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self); \
+ return ((class_name*)self_plus)->Py##method_name(self); \
}; \
static const char method_name##_doc[]; \
@@ -233,19 +258,19 @@ static inline void Py_Fatal(const char *M) {
*/
#define KX_PYMETHODDEF_DOC(class_name, method_name, doc_string) \
const char class_name::method_name##_doc[] = doc_string; \
-PyObject* class_name::Py##method_name(PyObject*, PyObject* args, PyObject*)
+PyObject* class_name::Py##method_name(PyObject* self, PyObject* args, PyObject*)
#define KX_PYMETHODDEF_DOC_VARARGS(class_name, method_name, doc_string) \
const char class_name::method_name##_doc[] = doc_string; \
-PyObject* class_name::Py##method_name(PyObject*, PyObject* args)
+PyObject* class_name::Py##method_name(PyObject* self, PyObject* args)
#define KX_PYMETHODDEF_DOC_O(class_name, method_name, doc_string) \
const char class_name::method_name##_doc[] = doc_string; \
-PyObject* class_name::Py##method_name(PyObject*, PyObject* value)
+PyObject* class_name::Py##method_name(PyObject* self, PyObject* value)
#define KX_PYMETHODDEF_DOC_NOARGS(class_name, method_name, doc_string) \
const char class_name::method_name##_doc[] = doc_string; \
-PyObject* class_name::Py##method_name(PyObject*)
+PyObject* class_name::Py##method_name(PyObject* self)
/**
* Attribute management
@@ -394,19 +419,17 @@ typedef struct KX_PYATTRIBUTE_DEF {
------------------------------*/
typedef PyTypeObject * PyParentObject; // Define the PyParent Object
-class PyObjectPlus : public PyObject
+class PyObjectPlus
{ // The PyObjectPlus abstract class
Py_Header; // Always start with Py_Header
public:
PyObjectPlus(PyTypeObject *T);
- bool m_zombie;
+
+ PyObject *m_proxy; /* actually a PyObjectPlus_Proxy */
virtual ~PyObjectPlus(); // destructor
- static void PyDestructor(PyObject *P) // python wrapper
- {
- delete ((PyObjectPlus *) P);
- };
+ static void PyDestructor(PyObject *self); // python wrapper
// void INCREF(void) {
// Py_INCREF(this);
@@ -418,15 +441,15 @@ public:
virtual PyObject *py_getattro(PyObject *attr); // py_getattro method
static PyObject *py_base_getattro(PyObject * self, PyObject *attr) // This should be the entry in Type.
{
- if (((PyObjectPlus*)self)->IsZombie()) {
- if (!strcmp(PyString_AsString(attr), "isValid")) {
- Py_RETURN_FALSE;
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self);
+ if(self_plus==NULL) {
+ if(!strcmp("isValid", PyString_AsString(attr))) {
+ Py_RETURN_TRUE;
}
- ((PyObjectPlus*)self)->IsZombiePyErr(); /* raise an error */
+ PyErr_SetString(PyExc_RuntimeError, "data has been removed");
return NULL;
}
-
- return ((PyObjectPlus*) self)->py_getattro(attr);
+ return self_plus->py_getattro(attr);
}
static PyObject* py_get_attrdef(void *self, const PyAttributeDef *attrdef);
@@ -441,22 +464,29 @@ public:
virtual int py_setattro(PyObject *attr, PyObject *value); // py_setattro method
static int py_base_setattro(PyObject *self, PyObject *attr, PyObject *value) // the PyType should reference this
{
- if (((PyObjectPlus*)self)->IsZombie()) {
- /* you cant set isValid anyway */
- ((PyObjectPlus*)self)->IsZombiePyErr();
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self);
+ if(self_plus==NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "data has been removed");
return -1;
}
if (value==NULL)
- return ((PyObjectPlus*)self)->py_delattro(attr);
+ return self_plus->py_delattro(attr);
- return ((PyObjectPlus*)self)->py_setattro(attr, value);
+ return self_plus->py_setattro(attr, value);
}
virtual PyObject *py_repr(void); // py_repr method
- static PyObject *py_base_repr(PyObject *PyObj) // This should be the entry in Type.
+ static PyObject *py_base_repr(PyObject *self) // This should be the entry in Type.
{
- return ((PyObjectPlus*) PyObj)->py_repr();
+
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self);
+ if(self_plus==NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "data has been removed");
+ return NULL;
+ }
+
+ return self_plus->py_repr();
}
// isA methods
@@ -465,43 +495,20 @@ public:
PyObject *Py_isA(PyObject *value);
static PyObject *sPy_isA(PyObject *self, PyObject *value)
{
- return ((PyObjectPlus*)self)->Py_isA(value);
- }
-
- /* Kindof dumb, always returns True, the false case is checked for, before this function gets accessed */
- static PyObject* pyattr_get_is_valid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
-
- bool IsZombie()
- {
- return m_zombie;
- }
-
- bool IsZombiePyErr()
- {
- if(m_zombie) {
- /*
- PyObject *this_pystr = PyObject_Repr(this);
-
- PyErr_Format(
- PyExc_RuntimeError,
- "\"%s\" of type \"%s\" has been freed by the blender game engine, "
- "scripts cannot access this anymore, check for this case with the \"isValid\" attribute",
- PyString_AsString(this_pystr), ob_type->tp_name );
-
- Py_DECREF(this_pystr);
- */
-
- PyErr_SetString(PyExc_RuntimeError, "This value has been freed by the blender game engine but python is still holding a reference, this value cant be used.");
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self);
+ if(self_plus==NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "data has been removed");
+ return NULL;
}
- return m_zombie;
+ return self_plus->Py_isA(value);
}
- void SetZombie(bool is_zombie)
- {
- m_zombie= is_zombie;
- }
+ /* Kindof dumb, always returns True, the false case is checked for, before this function gets accessed */
+ static PyObject* pyattr_get_is_valid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+ static PyObject *GetProxy_Ext(PyObjectPlus *self, PyTypeObject *tp);
+ static PyObject *NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool py_owns);
};
PyObject *py_getattr_dict(PyObject *pydict, PyObject *tp_dict);