From 06882321d807bc015d50f456e5feb97981291998 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Tue, 5 Feb 2008 22:45:12 +0000 Subject: [PATCH] Add support for customising an 'instance creation function', for cases when creating instances with new ClassName does not work (*cough*ns-3*cough*). --- examples/foo.h | 11 +++- examples/foomodulegen-auto.py | 9 +++ examples/foomodulegen.py | 10 ++- examples/footest.py | 7 +++ pybindgen/cppclass.py | 112 +++++++++++++++++++++++++--------- pybindgen/cppmethod.py | 15 ++--- 6 files changed, 122 insertions(+), 42 deletions(-) diff --git a/examples/foo.h b/examples/foo.h index 61441ab..191b9fc 100644 --- a/examples/foo.h +++ b/examples/foo.h @@ -30,21 +30,26 @@ class Foo // -#- automatic_type_narrowing=True -#- { std::string m_datum; + bool m_initialized; public: static int instance_count; - Foo () : m_datum ("") + Foo () : m_datum (""), m_initialized (false) { Foo::instance_count++; } - Foo (std::string datum) : m_datum (datum) + Foo (std::string datum) : m_datum (datum), m_initialized (false) { Foo::instance_count++; } std::string get_datum () const { return m_datum; } - Foo (Foo const & other) : m_datum (other.get_datum ()) + Foo (Foo const & other) : m_datum (other.get_datum ()), m_initialized (false) { Foo::instance_count++; } + void initialize () { m_initialized = true; } + bool is_initialized () const { return m_initialized; } + virtual ~Foo() { Foo::instance_count--; } }; + class Zoo // -#- automatic_type_narrowing=True -#- { diff --git a/examples/foomodulegen-auto.py b/examples/foomodulegen-auto.py index d98c8b7..f59ade6 100644 --- a/examples/foomodulegen-auto.py +++ b/examples/foomodulegen-auto.py @@ -115,6 +115,15 @@ _wrap_PyBar_Hooray_lenx(PyBar *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject wrapper_body, flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])) + + Foo, = [cls for cls in module.classes if cls.name == 'Foo'] + def Foo_instance_creation_function(dummy_cpp_class, code_block, lvalue, + parameters, construct_type_name): + code_block.write_code( + "%s = new %s(%s);" % (lvalue, construct_type_name, parameters)) + code_block.write_code("%s->initialize();" % (lvalue,)) + Foo.set_instance_creation_function(Foo_instance_creation_function) + module.generate(out) diff --git a/examples/foomodulegen.py b/examples/foomodulegen.py index 78f4379..21b5ed8 100755 --- a/examples/foomodulegen.py +++ b/examples/foomodulegen.py @@ -60,11 +60,20 @@ def my_module_gen(out_file): Foo = CppClass('Foo', automatic_type_narrowing=True) mod.add_class(Foo) + + def Foo_instance_creation_function(dummy_cpp_class, code_block, lvalue, + parameters, construct_type_name): + code_block.write_code( + "%s = new %s(%s);" % (lvalue, construct_type_name, parameters)) + code_block.write_code("%s->initialize();" % (lvalue,)) + Foo.set_instance_creation_function(Foo_instance_creation_function) + Foo.add_static_attribute(ReturnValue.new('int'), 'instance_count') Foo.add_constructor( CppConstructor([Parameter.new('std::string', 'datum')])) Foo.add_constructor(CppConstructor([])) Foo.add_method(CppMethod(ReturnValue.new('std::string'), 'get_datum', [])) + Foo.add_method(CppMethod(ReturnValue.new('bool'), 'is_initialized', [], is_const=True)) Zoo = CppClass('Zoo', automatic_type_narrowing=True) mod.add_class(Zoo) @@ -74,7 +83,6 @@ def my_module_gen(out_file): Zoo.add_method(CppMethod(ReturnValue.new('std::string'), 'get_datum', [])) Zoo.implicitly_converts_to(Foo) - Foobar = CppClass('Foobar') mod.add_class(Foobar) Foobar.add_static_attribute(ReturnValue.new('int'), 'instance_count') diff --git a/examples/footest.py b/examples/footest.py index 8ac817a..bc665fb 100644 --- a/examples/footest.py +++ b/examples/footest.py @@ -660,5 +660,12 @@ class TestFoo(unittest.TestCase): else: self.assertEqual(v2, len("Hooray!") + 123) + def test_instance_creation_function(self): + f = foo.Foo() + self.assert_(f.is_initialized()) + + b = foo.Bar() + self.assert_(b.is_initialized()) + if __name__ == '__main__': unittest.main() diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index fe4dab1..34dc953 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -22,6 +22,27 @@ import settings import utils +def default_instance_creation_function(cpp_class, code_block, lvalue, + parameters, construct_type_name): + """ + Default "instance creation function"; it is called whenever a new + C++ class instance needs to be created; this default + implementation uses a standard C++ new allocator. + + cpp_class -- the CppClass object whose instance is to be created + code_block -- CodeBlock object on which the instance creation code should be generated + lvalue -- lvalue expression that should hold the result in the end + contruct_type_name -- actual name of type to be constructed (it is + not always the class name, sometimes it's + the python helper class) + parameters -- stringified list of parameters + """ + assert lvalue + assert not lvalue.startswith('None') + code_block.write_code( + "%s = new %s(%s);" % (lvalue, construct_type_name, parameters)) + + class CppHelperClass(object): """ Generates code for a C++ proxy subclass that takes care of @@ -302,6 +323,7 @@ class CppClass(object): self.constructors = [] # (name, wrapper) pairs self.slots = dict() self.helper_class = None + self.instance_creation_function = None ## set to True when we become aware generating the helper ## class is not going to be possible self.helper_class_disabled = False @@ -410,6 +432,30 @@ class CppClass(object): except ValueError: pass + def set_instance_creation_function(self, instance_creation_function): + """Set a custom function to be called to create instances of this + class and its subclasses. + + instance_creation_function -- instance creation function; see + default_instance_creation_function() + for signature and example. + """ + self.instance_creation_function = instance_creation_function + + def get_instance_creation_function(self): + if self.instance_creation_function is None: + if self.parent is None: + return default_instance_creation_function + else: + return self.parent.get_instance_creation_function() + else: + return self.instance_creation_function + + def write_create_instance(self, code_block, lvalue, parameters, construct_type_name=None): + instance_creation_func = self.get_instance_creation_function() + if construct_type_name is None: + construct_type_name = self.get_construct_name() + instance_creation_func(self, code_block, lvalue, parameters, construct_type_name) def get_pystruct(self): if self._pystruct is None: @@ -1151,8 +1197,9 @@ class CppClassParameter(CppClassParameterBase): wrapper.before_call.write_code( "%s->inst_dict = NULL;" % (self.py_name,)) - wrapper.before_call.write_code( - "%s->obj = new %s(%s);" % (self.py_name, self.cpp_class.get_construct_name(), self.value)) + self.cpp_class.write_create_instance(wrapper.before_call, + "%s->obj" % self.py_name, + self.value) wrapper.build_params.add_parameter("N", [self.py_name]) @@ -1208,8 +1255,10 @@ class CppClassRefParameter(CppClassParameterBase): if self.cpp_class.allow_subclassing: wrapper.after_call.write_code( "%s->inst_dict = NULL;" % (self.py_name,)) - wrapper.before_call.write_code( - "%s->obj = new %s;" % (self.py_name, self.cpp_class.get_construct_name())) + + self.cpp_class.write_create_instance(wrapper.before_call, + "%s->obj" % self.py_name, + '') wrapper.call_params.append('*%s->obj' % (self.py_name,)) wrapper.build_params.add_parameter("N", [self.py_name]) @@ -1248,8 +1297,9 @@ class CppClassRefParameter(CppClassParameterBase): "%s->inst_dict = NULL;" % (self.py_name,)) if self.direction == Parameter.DIRECTION_IN: - wrapper.before_call.write_code( - "%s->obj = new %s(%s);" % (self.py_name, self.cpp_class.get_construct_name(), self.value)) + self.cpp_class.write_create_instance(wrapper.before_call, + "%s->obj" % self.py_name, + self.value) wrapper.build_params.add_parameter("N", [self.py_name]) else: ## out/inout case: @@ -1274,11 +1324,13 @@ class CppClassRefParameter(CppClassParameterBase): wrapper.after_call.write_code( "if (%s->ob_refcnt == 1)\n" " %s->obj = NULL;\n" - "else\n" - " %s->obj = new %s(%s);" % - (self.py_name, self.py_name, self.py_name, - self.cpp_class.get_construct_name(), self.value)) - + "else{\n" % (self.py_name, self.py_name)) + wrapper.after_call.indent() + self.cpp_class.write_create_instance(wrapper.after_call, + "%s->obj" % self.py_name, + self.value) + wrapper.after_call.unindent() + wrapper.after_call.write_code('}') class CppClassReturnValue(CppClassReturnValueBase): @@ -1313,8 +1365,11 @@ class CppClassReturnValue(CppClassReturnValueBase): if self.cpp_class.allow_subclassing: wrapper.after_call.write_code( "%s->inst_dict = NULL;" % (py_name,)) - wrapper.after_call.write_code( - "%s->obj = new %s(%s);" % (py_name, self.cpp_class.get_construct_name(), self.value)) + + self.cpp_class.write_create_instance(wrapper.after_call, + "%s->obj" % py_name, + self.value) + wrapper.build_params.add_parameter("N", [py_name], prepend=True) def convert_python_to_c(self, wrapper): @@ -1456,10 +1511,9 @@ class CppClassPtrParameter(CppClassParameterBase): ## the object after the call. if self.direction == Parameter.DIRECTION_IN: - wrapper.before_call.write_code( - "%s->obj = new %s(*%s);" % - (self.py_name, self.cpp_class.get_construct_name(), self.value)) - + self.cpp_class.write_create_instance(wrapper.before_call, + "%s->obj" % self.py_name, + '*'+self.value) wrapper.build_params.add_parameter("N", [self.py_name]) else: ## out/inout case: @@ -1484,11 +1538,13 @@ class CppClassPtrParameter(CppClassParameterBase): wrapper.after_call.write_code( "if (%s->ob_refcnt == 1)\n" " %s->obj = NULL;\n" - "else\n" - " %s->obj = new %s(*%s);" % - (self.py_name, self.py_name, self.py_name, - self.cpp_class.get_construct_name(), value)) - + "else {\n" % (self.py_name, self.py_name)) + wrapper.after_call.indent() + self.cpp_class.write_create_instance(wrapper.after_call, + "%s->obj" % self.py_name, + '*'+value) + wrapper.after_call.unindent() + wrapper.after_call.write_code('}') else: ## The PyObject gets a new reference to the same obj wrapper.before_call.write_code( @@ -1646,9 +1702,9 @@ class CppClassPtrReturnValue(CppClassReturnValueBase): else: if self.cpp_class.incref_method is None: ## The PyObject creates its own copy - wrapper.after_call.write_code( - "%s->obj = new %s(*%s);" - % (py_name, self.cpp_class.get_construct_name(), value)) + self.cpp_class.write_create_instance(wrapper.after_call, + "%s->obj" % py_name, + '*'+value) else: ## The PyObject gets a new reference to the same obj wrapper.after_call.write_code( @@ -1710,9 +1766,9 @@ class CppClassPtrReturnValue(CppClassReturnValueBase): if self.caller_owns_return: if self.cpp_class.incref_method is None: ## the caller receives a copy - wrapper.after_call.write_code( - "%s = new %s(*%s);" - % (self.value, self.cpp_class.get_construct_name(), value)) + self.cpp_class.write_create_instance(wrapper.after_call, + "%s" % self.value, + '*'+value) else: ## the caller gets a new reference to the same obj wrapper.after_call.write_code( diff --git a/pybindgen/cppmethod.py b/pybindgen/cppmethod.py index c5d3f05..ac4ba2f 100644 --- a/pybindgen/cppmethod.py +++ b/pybindgen/cppmethod.py @@ -249,9 +249,7 @@ class CppConstructor(ForwardWrapperBase): class_ = self._class #assert isinstance(class_, CppClass) if class_.helper_class is None: - self.before_call.write_code( - 'self->obj = new %s(%s);' % - (class_.get_construct_name(), ", ".join(self.call_params))) + class_.write_create_instance(self.before_call, "self->obj", ", ".join(self.call_params)) else: ## We should only create a helper class instance when ## being called from a user python subclass. @@ -259,9 +257,8 @@ class CppConstructor(ForwardWrapperBase): self.before_call.write_code("{") self.before_call.indent() - self.before_call.write_code( - 'self->obj = new %s(%s);' % - (class_.helper_class.name, ", ".join(self.call_params))) + class_.write_create_instance(self.before_call, "self->obj", ", ".join(self.call_params), + class_.helper_class.name) self.before_call.write_code('((%s*) self->obj)->set_pyobj((PyObject *)self);' % class_.helper_class.name) @@ -270,15 +267,13 @@ class CppConstructor(ForwardWrapperBase): self.before_call.indent() try: - contruct_name = class_.get_construct_name() + class_.get_construct_name() except CodeGenerationError: self.before_call.write_code('PyErr_SetString(PyExc_TypeError, "class \'%s\' ' 'cannot be constructed");' % class_.name) self.before_call.write_code('return -1;') else: - self.before_call.write_code( - 'self->obj = new %s(%s);' % - (contruct_name, ", ".join(self.call_params))) + class_.write_create_instance(self.before_call, "self->obj", ", ".join(self.call_params)) self.before_call.unindent() self.before_call.write_code("}")