diff --git a/examples/foomodulegen.py b/examples/foomodulegen.py index 531d72d..25f3435 100755 --- a/examples/foomodulegen.py +++ b/examples/foomodulegen.py @@ -97,7 +97,7 @@ def my_module_gen(out_file): - SomeObject = CppClass('SomeObject') + SomeObject = CppClass('SomeObject', allow_subclassing=True) SomeObject.add_instance_attribute(ReturnValue.new('std::string'), 'm_prefix') SomeObject.add_static_attribute(ReturnValue.new('std::string'), 'staticData') ## not working correctly diff --git a/pybindgen/__init__.py b/pybindgen/__init__.py index 348561e..5278953 100644 --- a/pybindgen/__init__.py +++ b/pybindgen/__init__.py @@ -18,25 +18,54 @@ def write_preamble(code_sink, min_python_version=(2, 3)): code_sink.writeln(''' #define PY_SSIZE_T_CLEAN #include +''') + + if min_python_version < (2, 4): + code_sink.writeln(r''' +#if PY_VERSION_HEX < 0x02040000 + +#define Py_CLEAR(op) \ + do { \ + if (op) { \ + PyObject *tmp = (PyObject *)(op); \ + (op) = NULL; \ + Py_DECREF(tmp); \ + } \ + } while (0) + +#endif ''') if min_python_version < (2, 5): code_sink.writeln(r''' - #if PY_VERSION_HEX < 0x02050000 + typedef int Py_ssize_t; # define PY_SSIZE_T_MAX INT_MAX # define PY_SSIZE_T_MIN INT_MIN typedef inquiry lenfunc; typedef intargfunc ssizeargfunc; typedef intobjargproc ssizeobjargproc; + +#define Py_VISIT(op) \ + do { \ + if (op) { \ + int vret = visit((PyObject *)(op), arg); \ + if (vret) \ + return vret; \ + } \ + } while (0) + #endif +''') -#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) -# define PYBINDGEN_UNUSED __attribute__((__unused__)) + code_sink.writeln(r''' +#if __GNUC__ > 2 +# define PYBINDGEN_UNUSED(param) param __attribute__((__unused__)) +#elif __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define PYBINDGEN_UNUSED(param) __attribute__((__unused__)) param #else -# define PYBINDGEN_UNUSED +# define PYBINDGEN_UNUSED(param) #endif /* !__GNUC__ */ - ''') diff --git a/pybindgen/cppattribute.py b/pybindgen/cppattribute.py index 67a0a57..0e89684 100644 --- a/pybindgen/cppattribute.py +++ b/pybindgen/cppattribute.py @@ -188,7 +188,7 @@ class CppStaticAttributeSetter(PySetter): self.after_call.write_code('return 0;') ## now generate the function itself - code_sink.writeln(("static int %s(%s * PYBINDGEN_UNUSED dummy, " + code_sink.writeln(("static int %s(%s * PYBINDGEN_UNUSED(dummy), " "PyObject *value)") % (self.c_function_name, self.class_.pystruct)) code_sink.writeln('{') diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 32098e2..5d7f2db 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -131,6 +131,8 @@ class CppClass(object): else: self.allow_subclassing = parent.allow_subclassing else: + assert allow_subclassing or (self.parent is None or not self.parent.allow_subclassing), \ + "Cannot disable subclassing if the parent class allows it" self.allow_subclassing = allow_subclassing self.typeid_map = None @@ -230,7 +232,7 @@ public: return python_wrapper? python_wrapper : fallback_wrapper; -#else // non gcc 3+ compilers can only match against registerd classes, not hidden subclasses +#else // non gcc 3+ compilers can only match against explicitly registered classes, not hidden subclasses return fallback_wrapper; #endif } @@ -312,12 +314,23 @@ public: """Generates forward declarations for the instance and type structures""" - code_sink.writeln(''' + if self.allow_subclassing: + code_sink.writeln(''' +typedef struct { + PyObject_HEAD + %s *obj; + PyObject *inst_dict; +} %s; + ''' % (self.name, self.pystruct)) + + else: + + code_sink.writeln(''' typedef struct { PyObject_HEAD %s *obj; } %s; -''' % (self.name, self.pystruct)) + ''' % (self.name, self.pystruct)) code_sink.writeln() code_sink.writeln('extern PyTypeObject %s;' % (self.pytypestruct,)) @@ -365,6 +378,8 @@ typedef struct { have_constructor = self._generate_constructor(code_sink) self._generate_methods(code_sink) + if self.allow_subclassing: + self._generate_gc_methods(code_sink) self._generate_destructor(code_sink, have_constructor) self._generate_type_structure(code_sink, docstring) if self.typeid_map is not None: @@ -383,12 +398,17 @@ typedef struct { "tp_descr_set", "tp_is_gc"]: self.slots.setdefault(slot, "NULL") - self.slots.setdefault("tp_dictoffset", "0") self.slots.setdefault("tp_alloc", "PyType_GenericAlloc") self.slots.setdefault("tp_new", "PyType_GenericNew") - self.slots.setdefault("tp_free", "_PyObject_Del") + #self.slots.setdefault("tp_free", "_PyObject_Del") + self.slots.setdefault("tp_free", "0") self.slots.setdefault("tp_weaklistoffset", "0") - self.slots.setdefault("tp_flags", "Py_TPFLAGS_DEFAULT") + if self.allow_subclassing: + self.slots.setdefault("tp_flags", "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE") + self.slots.setdefault("tp_dictoffset", "offsetof(%s, inst_dict)" % self.pystruct) + else: + self.slots.setdefault("tp_flags", "Py_TPFLAGS_DEFAULT") + self.slots.setdefault("tp_dictoffset", "0") self.slots.setdefault("tp_doc", (docstring is None and 'NULL' or "\"%s\"" % (docstring,))) dict_ = dict(self.slots) @@ -467,6 +487,34 @@ typedef struct { code_sink.writeln("};") self.slots.setdefault("tp_methods", "%s_methods" % (self.name,)) + def _generate_gc_methods(self, code_sink): + """Generate tp_clear and tp_traverse""" + + ## --- tp_clear --- + tp_clear_function_name = "%s__tp_clear" % (self.pystruct,) + self.slots.setdefault("tp_clear", tp_clear_function_name ) + code_sink.writeln(r''' +static void +%s(%s *self) +{ + // fprintf(stderr, "tp_clear >>> %%s at %%p <<<\n", self->ob_type->tp_name, self); + Py_CLEAR(self->inst_dict); +} +''' % (tp_clear_function_name, self.pystruct)) + + ## --- tp_traverse --- + tp_traverse_function_name = "%s__tp_traverse" % (self.pystruct,) + self.slots.setdefault("tp_traverse", tp_traverse_function_name ) + code_sink.writeln(r''' +static int +%s(%s *self, visitproc visit, void *arg) +{ + // fprintf(stderr, "tp_traverse >>> %%s at %%p <<<\n", self->ob_type->tp_name, self); + Py_VISIT(self->inst_dict); + return 0; +} +''' % (tp_traverse_function_name, self.pystruct)) + def _generate_destructor(self, code_sink, have_constructor): """Generate a tp_dealloc function and register it in the type""" @@ -476,22 +524,30 @@ typedef struct { return tp_dealloc_function_name = "_wrap_%s__tp_dealloc" % (self.pystruct,) + + if self.allow_subclassing: + clear_code = "%s(self);" % self.slots["tp_clear"] + else: + clear_code = "" + if have_constructor: if self.decref_method is None: delete_code = "delete tmp;" else: delete_code = ("if (tmp)\n tmp->%s()" % (self.decref_method,)) - code_sink.writeln(''' + code_sink.writeln(r''' static void %s(%s *self) { + // fprintf(stderr, "dealloc >>> %%s at %%p <<<\n", self->ob_type->tp_name, self); %s *tmp = self->obj; self->obj = NULL; %s; - PyObject_DEL(self); + %s + self->ob_type->tp_free((PyObject*)self); } - ''' % (tp_dealloc_function_name, self.pystruct, self.name, delete_code)) +''' % (tp_dealloc_function_name, self.pystruct, self.name, delete_code, clear_code)) else: # don't have constructor @@ -499,9 +555,10 @@ static void static void %s(%s *self) { - PyObject_DEL(self); + %s + self->ob_type->tp_free((PyObject*)self); } - ''' % (tp_dealloc_function_name, self.pystruct)) +''' % (tp_dealloc_function_name, self.pystruct, clear_code)) code_sink.writeln() self.slots.setdefault("tp_dealloc", tp_dealloc_function_name ) diff --git a/pybindgen/cppmethod.py b/pybindgen/cppmethod.py index 439e03b..924e6b3 100644 --- a/pybindgen/cppmethod.py +++ b/pybindgen/cppmethod.py @@ -88,7 +88,7 @@ class CppMethod(ForwardWrapperBase): code_sink.writeln("static PyObject *") if 'METH_STATIC' in flags: - _self_name = 'PYBINDGEN_UNUSED dummy' + _self_name = 'PYBINDGEN_UNUSED(dummy)' else: _self_name = 'self' diff --git a/pybindgen/function.py b/pybindgen/function.py index 685eb87..1c035c5 100644 --- a/pybindgen/function.py +++ b/pybindgen/function.py @@ -48,7 +48,7 @@ class Function(ForwardWrapperBase): tmp_sink = codesink.MemoryCodeSink() self.generate_body(tmp_sink) code_sink.writeln("static PyObject *") - prototype_line = ("%s(PyObject * PYBINDGEN_UNUSED dummy, " + prototype_line = ("%s(PyObject * PYBINDGEN_UNUSED(dummy), " "PyObject *args, PyObject *kwargs") % (self.wrapper_actual_name,) if extra_wrapper_params: prototype_line += ", " + ", ".join(extra_wrapper_params)