Commit 72ee1f75 authored by Robert Bradshaw's avatar Robert Bradshaw

Allow C++ classes to have Python object members.

parent d31c5e78
......@@ -274,7 +274,7 @@ class UtilityCodeBase(object):
elif not values:
values = None
elif len(values) == 1:
values = values[0]
values = list(values)[0]
kwargs[name] = values
if proto is not None:
......@@ -1967,9 +1967,12 @@ class CCodeWriter(object):
if entry.type.is_pyobject:
self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
def put_var_xdecref(self, entry):
def put_var_xdecref(self, entry, nanny=True):
if entry.type.is_pyobject:
if nanny:
self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
else:
self.putln("Py_XDECREF(%s);" % self.entry_as_pyobject(entry))
def put_var_decref_clear(self, entry):
self._put_var_decref_clear(entry, null_check=False)
......
......@@ -897,19 +897,60 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
[base_class.empty_declaration_code() for base_class in type.base_classes])
code.put(" : public %s" % base_class_decl)
code.putln(" {")
py_attrs = [e for e in scope.entries.values()
if e.type.is_pyobject and not e.is_inherited]
has_virtual_methods = False
has_destructor = False
constructor = None
destructor = None
for attr in scope.var_entries:
if attr.type.is_cfunction and attr.type.is_static_method:
code.put("static ")
elif attr.type.is_cfunction and attr.name != "<init>":
elif attr.name == "<init>":
constructor = attr
elif attr.name == "<del>":
destructor = attr
elif attr.type.is_cfunction:
code.put("virtual ")
has_virtual_methods = True
if attr.cname[0] == '~':
has_destructor = True
code.putln("%s;" % attr.type.declaration_code(attr.cname))
if has_virtual_methods and not has_destructor:
code.putln("virtual ~%s() { }" % type.cname)
if constructor or py_attrs:
if constructor:
arg_decls = []
arg_names = []
for arg in constructor.type.args[:len(constructor.type.args)-constructor.type.optional_arg_count]:
arg_decls.append(arg.declaration_code())
arg_names.append(arg.cname)
if constructor.type.optional_arg_count:
arg_decls.append(constructor.type.op_arg_struct.declaration_code(Naming.optional_args_cname))
arg_names.append(Naming.optional_args_cname)
if not arg_decls:
arg_decls = ["void"]
else:
arg_decls = ["void"]
arg_names = []
code.putln("%s(%s) {" % (type.cname, ", ".join(arg_decls)))
if py_attrs:
code.put_ensure_gil()
for attr in py_attrs:
code.put_init_var_to_py_none(attr, nanny=False);
if constructor:
code.putln("%s(%s);" % (constructor.cname, ", ".join(arg_names)))
if py_attrs:
code.put_release_ensured_gil()
code.putln("}")
if destructor or py_attrs or has_virtual_methods:
if has_virtual_methods:
code.put("virtual ")
code.putln("~%s() {" % type.cname)
if py_attrs:
code.put_ensure_gil()
if destructor:
code.putln("%s();" % destructor.cname)
if py_attrs:
for attr in py_attrs:
code.put_var_xdecref(attr, nanny=False);
code.put_release_ensured_gil()
code.putln("}")
code.putln("};")
def generate_enum_definition(self, entry, code):
......
......@@ -2274,22 +2274,20 @@ class CppClassScope(Scope):
entry.func_cname = "%s::%s" % (self.type.empty_declaration_code(), cname)
if name != "this" and (defining or name != "<init>"):
self.var_entries.append(entry)
if type.is_pyobject:
error(pos,
"C++ class member cannot be a Python object")
return entry
def declare_cfunction(self, name, type, pos,
cname=None, visibility='extern', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
if name in (self.name.split('::')[-1], '__init__') and cname is None:
cname = self.type.cname
class_name = self.name.split('::')[-1]
if name in (class_name, '__init__') and cname is None:
cname = "%s__init__%s" % (Naming.func_prefix, class_name)
name = '<init>'
type.return_type = PyrexTypes.InvisibleVoidType()
type.return_type = PyrexTypes.CVoidType()
elif name == '__dealloc__' and cname is None:
cname = "~%s" % self.type.cname
cname = "%s__dealloc__%s" % (Naming.func_prefix, class_name)
name = '<del>'
type.return_type = PyrexTypes.InvisibleVoidType()
type.return_type = PyrexTypes.CVoidType()
prev_entry = self.lookup_here(name)
entry = self.declare_var(name, type, pos,
defining=defining,
......@@ -2314,8 +2312,8 @@ class CppClassScope(Scope):
# to work with this type.
for base_entry in \
base_scope.inherited_var_entries + base_scope.var_entries:
#contructor is not inherited
if base_entry.name == "<init>":
#contructor/destructor is not inherited
if base_entry.name in ("<init>", "<del>"):
continue
#print base_entry.name, self.entries
if base_entry.name in self.entries:
......@@ -2323,6 +2321,7 @@ class CppClassScope(Scope):
entry = self.declare(base_entry.name, base_entry.cname,
base_entry.type, None, 'extern')
entry.is_variable = 1
entry.is_inherited = 1
self.inherited_var_entries.append(entry)
for base_entry in base_scope.cfunc_entries:
entry = self.declare_cfunction(base_entry.name, base_entry.type,
......
......@@ -690,6 +690,7 @@ typedef struct {PyObject **p; const char *s; const Py_ssize_t n; const char* enc
const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/
/////////////// ForceInitThreads.proto ///////////////
//@proto_block: utility_code_proto_before_types
#ifndef __PYX_FORCE_INIT_THREADS
#define __PYX_FORCE_INIT_THREADS 0
......@@ -1081,6 +1082,7 @@ __Pyx_FastGilFuncInit();
#endif
/////////////// NoFastGil.proto ///////////////
//@proto_block: utility_code_proto_before_types
#define __Pyx_PyGILState_Ensure PyGILState_Ensure
#define __Pyx_PyGILState_Release PyGILState_Release
......@@ -1089,6 +1091,7 @@ __Pyx_FastGilFuncInit();
#define __Pyx_FastGilFuncInit()
/////////////// FastGil.proto ///////////////
//@proto_block: utility_code_proto_before_types
struct __Pyx_FastGilVtab {
PyGILState_STATE (*Fast_PyGILState_Ensure)(void);
......
......@@ -151,3 +151,40 @@ def test_default_init_no_gil():
with nogil:
s = new Simple()
del s
cdef class NoisyAlloc(object):
cdef public name
def __init__(self, name):
print "NoisyAlloc.__init__", name
self.name = name
def __dealloc__(self):
try:
print "NoisyAlloc.__dealloc__", self.name
except:
pass # Suppress unraisable exception warning.
cdef cppclass CppClassWithObjectMember:
NoisyAlloc o
__init__(name):
try:
print "CppClassWithObjectMember.__init__", name
this.o = NoisyAlloc(name)
except:
pass # Suppress unraisable exception warning.
__dealloc__():
try:
print "CppClassWithObjectMember.__dealloc__", this.o.name
except:
pass # Suppress unraisable exception warning.
def test_CppClassWithObjectMember(name):
"""
>>> test_CppClassWithObjectMember("gertrude")
CppClassWithObjectMember.__init__ gertrude
NoisyAlloc.__init__ gertrude
CppClassWithObjectMember.__dealloc__ gertrude
NoisyAlloc.__dealloc__ gertrude
"""
x = new CppClassWithObjectMember(name)
del x
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment