From aae1dcfe7e97c157c39df2ad3b60197a0b00aefa Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 2 Jan 2008 18:51:27 +0000 Subject: [PATCH] Add peekref_method support, to solve an issue related to virtual methods, user classes, and reference counting. --- examples/foo.cc | 24 ++++++++++++++++++++++++ examples/foo.h | 24 ++++++++++++++++++++---- examples/foomodulegen.py | 13 +++++++++++-- examples/footest.py | 36 +++++++++++++++++++++++++++++++++--- pybindgen/cppclass.py | 15 ++++++++++++--- pybindgen/gccxmlparser.py | 2 ++ 6 files changed, 102 insertions(+), 12 deletions(-) diff --git a/examples/foo.cc b/examples/foo.cc index c2e1f51..8998580 100644 --- a/examples/foo.cc +++ b/examples/foo.cc @@ -174,3 +174,27 @@ template <> std::string TypeNameGet (void) { return "int"; } + + +static Zbr *g_zbr = NULL; +int Zbr::instance_count = 0; + +void store_zbr (Zbr *zbr) +{ + if (g_zbr) + g_zbr->Unref (); + g_zbr = zbr; +} + +int invoke_zbr (int x) +{ + return g_zbr->get_int (x); +} + +void delete_stored_zbr (void) +{ + if (g_zbr) + g_zbr->Unref (); + g_zbr = NULL; +} + diff --git a/examples/foo.h b/examples/foo.h index 476dadb..a0ef741 100644 --- a/examples/foo.h +++ b/examples/foo.h @@ -91,21 +91,21 @@ public: Foo* get_hidden_subclass_pointer (); class Zbr -// -#- incref_method=Ref; decref_method=Unref -#- +// -#- incref_method=Ref; decref_method=Unref; peekref_method=GetReferenceCount -#- { int m_refcount; std::string m_datum; public: Zbr () : m_refcount (1), m_datum ("") - {} + { Zbr::instance_count++; } Zbr (std::string datum) : m_refcount (1), m_datum (datum) - {} + { Zbr::instance_count++; } std::string get_datum () const { return m_datum; } Zbr (Zbr const & other) : m_refcount (1), m_datum (other.get_datum ()) - {} + {Zbr::instance_count++;} void Ref () { // std::cerr << "Ref Zbr " << this << " from " << m_refcount << std::endl; @@ -116,8 +116,24 @@ public: if (--m_refcount == 0) delete this; } + int GetReferenceCount () const { return m_refcount; } + + virtual int get_int (int x) { + return x; + } + + static int instance_count; + + virtual ~Zbr () { + --Zbr::instance_count; + } }; +// -#- @zbr(transfer_ownership=true) -#- +void store_zbr (Zbr *zbr); +int invoke_zbr (int x); +void delete_stored_zbr (void); + class Foobar { diff --git a/examples/foomodulegen.py b/examples/foomodulegen.py index bee9ed3..bdaf778 100755 --- a/examples/foomodulegen.py +++ b/examples/foomodulegen.py @@ -90,13 +90,22 @@ def my_module_gen(out_file): ## Zbr is a reference counted class - Zbr = CppClass('Zbr', incref_method='Ref', decref_method='Unref') + Zbr = CppClass('Zbr', incref_method='Ref', decref_method='Unref', + peekref_method="GetReferenceCount", allow_subclassing=True) mod.add_class(Zbr) Zbr.add_constructor( CppConstructor([Parameter.new('std::string', 'datum')])) Zbr.add_method(CppMethod(ReturnValue.new('std::string'), 'get_datum', [])) + Zbr.add_method(CppMethod(ReturnValue.new('int'), 'get_int', [Parameter.new('int', 'x')], + is_virtual=True)) + Zbr.add_static_attribute(ReturnValue.new('int'), 'instance_count') - + mod.add_function(Function(ReturnValue.new('void'), 'store_zbr', + [Parameter.new('Zbr*', 'zbr', transfer_ownership=True)])) + mod.add_function(Function(ReturnValue.new('int'), 'invoke_zbr', + [Parameter.new('int', 'x')])) + mod.add_function(Function(ReturnValue.new('void'), 'delete_stored_zbr', [])) + mod.add_function(Function(ReturnValue.new('int'), 'print_something', [Parameter.new('const char*', 'message')])) diff --git a/examples/footest.py b/examples/footest.py index 1563e49..dc0fe80 100644 --- a/examples/footest.py +++ b/examples/footest.py @@ -252,9 +252,7 @@ class TestFoo(unittest.TestCase): while gc.collect(): pass count_before = foo.SomeObject.instance_count - obj = Test("xxx", "yyy") - foo.store_some_object(obj) - del obj + foo.store_some_object(Test("xxx", "yyy")) while gc.collect(): pass @@ -612,5 +610,37 @@ class TestFoo(unittest.TestCase): x = c.invoke_protected_virtual(2) self.assertEqual(x, 9) + + def test_subclass_with_reference_counting(self): + class Test(foo.Zbr): + def __init__(self, y): + super(Test, self).__init__("foo") + self.y = y + def _get_int(self, x): + return getattr(self, 'y', 0) + x + + while gc.collect(): + pass + count_before = foo.Zbr.instance_count + foo.store_zbr(Test(123)) + while gc.collect(): + pass + + ## check that Zbr isn't prematurely deleted + self.assertEqual(foo.Zbr.instance_count, count_before + 1) + + ## invoke the virtual method and check that it returns the correct value + value = foo.invoke_zbr(456) + self.assertEqual(value, 123+456) + + ## now delete the object from the C side.. + foo.delete_stored_zbr() + while gc.collect(): + pass + + ## check that Zbr was finally deleted + self.assertEqual(foo.Zbr.instance_count, count_before) + + if __name__ == '__main__': unittest.main() diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 57eda51..4b10360 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -260,7 +260,8 @@ class CppClass(object): def __init__(self, name, parent=None, incref_method=None, decref_method=None, automatic_type_narrowing=None, allow_subclassing=None, - is_singleton=False, outer_class=None): + is_singleton=False, outer_class=None, + peekref_method=None): """Constructor name -- class name parent -- optional parent class wrapper @@ -282,6 +283,8 @@ class CppClass(object): is_singleton -- if True, the class is considered a singleton, and so the python wrapper will never call the C++ class destructor to free the value. + peekref_method -- if the class supports reference counting, the + name of the method that returns the current reference count. """ assert outer_class is None or isinstance(outer_class, CppClass) self.outer_class = outer_class @@ -318,9 +321,11 @@ class CppClass(object): if incref_method is None and parent is not None: self.incref_method = parent.incref_method self.decref_method = parent.decref_method + self.peekref_method = parent.peekref_method else: self.incref_method = incref_method self.decref_method = decref_method + self.peekref_method = peekref_method if automatic_type_narrowing is None: if parent is None: @@ -962,10 +967,14 @@ static void if self.helper_class is None: visit_self = '' else: + if self.peekref_method is None: + peekref_code = '' + else: + peekref_code = " && self->obj->%s() == 1" % self.peekref_method visit_self = ''' - if (self->obj && typeid(*self->obj) == typeid(%s)) + if (self->obj && typeid(*self->obj) == typeid(%s)%s) Py_VISIT(self); -''' % self.helper_class.name +''' % (self.helper_class.name, peekref_code) code_sink.writeln(r''' static int diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 7d56868..e2861fc 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -480,6 +480,8 @@ class ModuleParser(object): kwargs.setdefault('incref_method', value) elif name == 'decref_method': kwargs.setdefault('decref_method', value) + elif name == 'peekref_method': + kwargs.setdefault('peekref_method', value) elif name == 'automatic_type_narrowing': kwargs.setdefault('automatic_type_narrowing', annotations_scanner.parse_boolean(value)) else: