Add peekref_method support, to solve an issue related to virtual methods, user classes, and reference counting.
This commit is contained in:
1 parent
76c1e4e888
commit
aae1dcfe7e
6 files changed
+102
-12
No files matched your search
@@ -174,3 +174,27 @@ template <> std::string TypeNameGet<int> (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;
|
||||
}
|
||||
|
||||
+20
-4
@@ -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
|
||||
{
|
||||
|
||||
@@ -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')]))
|
||||
|
||||
+33
-3
@@ -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()
|
||||
+12
-3
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user