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): ...@@ -274,7 +274,7 @@ class UtilityCodeBase(object):
elif not values: elif not values:
values = None values = None
elif len(values) == 1: elif len(values) == 1:
values = values[0] values = list(values)[0]
kwargs[name] = values kwargs[name] = values
if proto is not None: if proto is not None:
...@@ -1967,9 +1967,12 @@ class CCodeWriter(object): ...@@ -1967,9 +1967,12 @@ class CCodeWriter(object):
if entry.type.is_pyobject: if entry.type.is_pyobject:
self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry)) 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 entry.type.is_pyobject:
self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry)) 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): def put_var_decref_clear(self, entry):
self._put_var_decref_clear(entry, null_check=False) self._put_var_decref_clear(entry, null_check=False)
......
...@@ -897,19 +897,60 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -897,19 +897,60 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
[base_class.empty_declaration_code() for base_class in type.base_classes]) [base_class.empty_declaration_code() for base_class in type.base_classes])
code.put(" : public %s" % base_class_decl) code.put(" : public %s" % base_class_decl)
code.putln(" {") 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_virtual_methods = False
has_destructor = False constructor = None
destructor = None
for attr in scope.var_entries: for attr in scope.var_entries:
if attr.type.is_cfunction and attr.type.is_static_method: if attr.type.is_cfunction and attr.type.is_static_method:
code.put("static ") 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 ") code.put("virtual ")
has_virtual_methods = True has_virtual_methods = True
if attr.cname[0] == '~':
has_destructor = True
code.putln("%s;" % attr.type.declaration_code(attr.cname)) code.putln("%s;" % attr.type.declaration_code(attr.cname))
if has_virtual_methods and not has_destructor: if constructor or py_attrs:
code.putln("virtual ~%s() { }" % type.cname) 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("};") code.putln("};")
def generate_enum_definition(self, entry, code): def generate_enum_definition(self, entry, code):
......
...@@ -2274,22 +2274,20 @@ class CppClassScope(Scope): ...@@ -2274,22 +2274,20 @@ class CppClassScope(Scope):
entry.func_cname = "%s::%s" % (self.type.empty_declaration_code(), cname) entry.func_cname = "%s::%s" % (self.type.empty_declaration_code(), cname)
if name != "this" and (defining or name != "<init>"): if name != "this" and (defining or name != "<init>"):
self.var_entries.append(entry) self.var_entries.append(entry)
if type.is_pyobject:
error(pos,
"C++ class member cannot be a Python object")
return entry return entry
def declare_cfunction(self, name, type, pos, def declare_cfunction(self, name, type, pos,
cname=None, visibility='extern', api=0, in_pxd=0, cname=None, visibility='extern', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False): defining=0, modifiers=(), utility_code=None, overridable=False):
if name in (self.name.split('::')[-1], '__init__') and cname is None: class_name = self.name.split('::')[-1]
cname = self.type.cname if name in (class_name, '__init__') and cname is None:
cname = "%s__init__%s" % (Naming.func_prefix, class_name)
name = '<init>' name = '<init>'
type.return_type = PyrexTypes.InvisibleVoidType() type.return_type = PyrexTypes.CVoidType()
elif name == '__dealloc__' and cname is None: elif name == '__dealloc__' and cname is None:
cname = "~%s" % self.type.cname cname = "%s__dealloc__%s" % (Naming.func_prefix, class_name)
name = '<del>' name = '<del>'
type.return_type = PyrexTypes.InvisibleVoidType() type.return_type = PyrexTypes.CVoidType()
prev_entry = self.lookup_here(name) prev_entry = self.lookup_here(name)
entry = self.declare_var(name, type, pos, entry = self.declare_var(name, type, pos,
defining=defining, defining=defining,
...@@ -2314,8 +2312,8 @@ class CppClassScope(Scope): ...@@ -2314,8 +2312,8 @@ class CppClassScope(Scope):
# to work with this type. # to work with this type.
for base_entry in \ for base_entry in \
base_scope.inherited_var_entries + base_scope.var_entries: base_scope.inherited_var_entries + base_scope.var_entries:
#contructor is not inherited #contructor/destructor is not inherited
if base_entry.name == "<init>": if base_entry.name in ("<init>", "<del>"):
continue continue
#print base_entry.name, self.entries #print base_entry.name, self.entries
if base_entry.name in self.entries: if base_entry.name in self.entries:
...@@ -2323,6 +2321,7 @@ class CppClassScope(Scope): ...@@ -2323,6 +2321,7 @@ class CppClassScope(Scope):
entry = self.declare(base_entry.name, base_entry.cname, entry = self.declare(base_entry.name, base_entry.cname,
base_entry.type, None, 'extern') base_entry.type, None, 'extern')
entry.is_variable = 1 entry.is_variable = 1
entry.is_inherited = 1
self.inherited_var_entries.append(entry) self.inherited_var_entries.append(entry)
for base_entry in base_scope.cfunc_entries: for base_entry in base_scope.cfunc_entries:
entry = self.declare_cfunction(base_entry.name, base_entry.type, 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 ...@@ -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*/ const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/
/////////////// ForceInitThreads.proto /////////////// /////////////// ForceInitThreads.proto ///////////////
//@proto_block: utility_code_proto_before_types
#ifndef __PYX_FORCE_INIT_THREADS #ifndef __PYX_FORCE_INIT_THREADS
#define __PYX_FORCE_INIT_THREADS 0 #define __PYX_FORCE_INIT_THREADS 0
...@@ -1081,6 +1082,7 @@ __Pyx_FastGilFuncInit(); ...@@ -1081,6 +1082,7 @@ __Pyx_FastGilFuncInit();
#endif #endif
/////////////// NoFastGil.proto /////////////// /////////////// NoFastGil.proto ///////////////
//@proto_block: utility_code_proto_before_types
#define __Pyx_PyGILState_Ensure PyGILState_Ensure #define __Pyx_PyGILState_Ensure PyGILState_Ensure
#define __Pyx_PyGILState_Release PyGILState_Release #define __Pyx_PyGILState_Release PyGILState_Release
...@@ -1089,6 +1091,7 @@ __Pyx_FastGilFuncInit(); ...@@ -1089,6 +1091,7 @@ __Pyx_FastGilFuncInit();
#define __Pyx_FastGilFuncInit() #define __Pyx_FastGilFuncInit()
/////////////// FastGil.proto /////////////// /////////////// FastGil.proto ///////////////
//@proto_block: utility_code_proto_before_types
struct __Pyx_FastGilVtab { struct __Pyx_FastGilVtab {
PyGILState_STATE (*Fast_PyGILState_Ensure)(void); PyGILState_STATE (*Fast_PyGILState_Ensure)(void);
......
...@@ -151,3 +151,40 @@ def test_default_init_no_gil(): ...@@ -151,3 +151,40 @@ def test_default_init_no_gil():
with nogil: with nogil:
s = new Simple() s = new Simple()
del s 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