From 4e07fc523dc43535e148e3ad26392267b78ca2ca Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Tue, 21 Aug 2012 00:46:00 -0700 Subject: [PATCH] More C++ class fixes, tests. --- Cython/Compiler/ExprNodes.py | 6 +-- Cython/Compiler/ModuleNode.py | 4 +- Cython/Compiler/Nodes.py | 8 +++- Cython/Compiler/Options.py | 1 + Cython/Compiler/PyrexTypes.py | 13 ++++++ Cython/Compiler/Symtab.py | 30 +++++++++----- tests/run/cpp_classes_def.pyx | 93 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 tests/run/cpp_classes_def.pyx diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index b798221..9c7ecad 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1329,9 +1329,8 @@ class NewExprNode(AtomicExprNode): self.cpp_check(env) constructor = type.scope.lookup(u'') if constructor is None: - return_type = PyrexTypes.CFuncType(type, [], exception_check='+') - return_type = PyrexTypes.CPtrType(return_type) - type.scope.declare_cfunction(u'', return_type, self.pos) + func_type = PyrexTypes.CFuncType(type, [], exception_check='+') + type.scope.declare_cfunction(u'', func_type, self.pos) constructor = type.scope.lookup(u'') self.class_type = type self.entry = constructor @@ -3705,6 +3704,7 @@ class CallNode(ExprNode): self.function.entry = constructor self.function.set_cname(type.declaration_code("")) self.analyse_c_function_call(env) + self.type = type return True def is_lvalue(self): diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 9d0463d..e5c3bb6 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -669,7 +669,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): def generate_struct_union_predeclaration(self, entry, code): type = entry.type if type.is_cpp_class and type.templates: - code.putln("template " % ", class ".join([T.declaration_code("") for T in type.templates])) + code.putln("template " % ", typename ".join([T.declaration_code("") for T in type.templates])) code.putln(self.sue_predeclaration(type, type.kind, type.cname)) def sue_header_footer(self, type, kind, name): @@ -728,7 +728,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.put(" : public %s" % base_class_decl) code.putln(" {") for attr in scope.var_entries: - if attr.type.is_cfunction: + if attr.type.is_cfunction and attr.name != "": code.put("virtual ") code.putln( "%s;" % diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 4dac0b7..ff3ef5f 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1197,7 +1197,7 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode): if self.entry is None: return self.entry.is_cpp_class = 1 - scope.class_namespace = self.entry.type.declaration_code("") + scope.type = self.entry.type defined_funcs = [] if self.attributes is not None: if self.in_pxd and not env.in_cinclude: @@ -1206,6 +1206,8 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode): attr.analyse_declarations(scope) if isinstance(attr, CFuncDefNode): defined_funcs.append(attr) + if self.templates is not None: + attr.template_declaration = "template " % ", typename ".join(self.templates) self.body = StatListNode(self.pos, stats=defined_funcs) self.scope = scope @@ -1935,6 +1937,7 @@ class CFuncDefNode(FuncDefNode): # py_func wrapper for calling from Python # overridable whether or not this is a cpdef function # inline_in_pxd whether this is an inline function in a pxd file + # template_declaration String or None Used for c++ class methods child_attrs = ["base_type", "declarator", "body", "py_func"] @@ -1943,6 +1946,7 @@ class CFuncDefNode(FuncDefNode): directive_locals = None directive_returns = None override = None + template_declaration = None def unqualified_name(self): return self.entry.name @@ -2150,6 +2154,8 @@ class CFuncDefNode(FuncDefNode): header = self.return_type.declaration_code(entity, dll_linkage=dll_linkage) #print (storage_class, modifiers, header) + if self.template_declaration: + code.putln(self.template_declaration) code.putln("%s%s%s {" % (storage_class, modifiers, header)) def generate_argument_declarations(self, env, code): diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py index 5a125e0..1abeed6 100644 --- a/Cython/Compiler/Options.py +++ b/Cython/Compiler/Options.py @@ -125,6 +125,7 @@ directive_defaults = { # experimental, subject to change 'binding': False, + 'experimental_cpp_class_def': False } # Extra warning directives diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index d844adb..18a7290 100755 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1213,6 +1213,19 @@ class CVoidType(CType): def is_complete(self): return 0 +class InvisibleVoidType(CVoidType): + # + # For use with C++ constructors and destructors return types. + # Acts like void, but does not print out a declaration. + # + def declaration_code(self, entity_code, + for_display = 0, dll_linkage = None, pyrex = 0): + if pyrex or for_display: + base_code = "[void]" + else: + base_code = public_decl("", dll_linkage) + return self.base_declaration_code(base_code, entity_code) + class CNumericType(CType): # diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 0b2cb1b..424ed43 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -1995,7 +1995,7 @@ class CppClassScope(Scope): is_cpp_class_scope = 1 default_constructor = None - class_namespace = None + type = None def __init__(self, name, outer_scope, templates=None): Scope.__init__(self, name, outer_scope, None) @@ -2010,15 +2010,21 @@ class CppClassScope(Scope): def declare_var(self, name, type, pos, cname = None, visibility = 'extern', api = 0, in_pxd = 0, is_cdef = 0, - allow_pyobject = 0): + allow_pyobject = 0, defining = 0): # Add an entry for an attribute. if not cname: cname = name - entry = self.declare(name, cname, type, pos, visibility) + entry = self.lookup_here(name) + if defining and entry is not None: + if not entry.type.same_as(type): + error(pos, "Function signature does not match previous declaration") + else: + entry = self.declare(name, cname, type, pos, visibility) entry.is_variable = 1 - if type.is_cfunction and self.class_namespace: - entry.func_cname = "%s::%s" % (self.class_namespace, cname) - self.var_entries.append(entry) + if type.is_cfunction and self.type: + entry.func_cname = "%s::%s" % (self.type.declaration_code(""), cname) + if name != "this" and (defining or name != ""): + self.var_entries.append(entry) if type.is_pyobject and not allow_pyobject: error(pos, "C++ class member cannot be a Python object") @@ -2056,14 +2062,20 @@ class CppClassScope(Scope): def declare_cfunction(self, name, type, pos, cname = None, visibility = 'extern', api = 0, in_pxd = 0, defining = 0, modifiers = (), utility_code = None): - if name == self.name.split('::')[-1] and cname is None: + if name in (self.name.split('::')[-1], '__init__') and cname is None: self.check_base_default_constructor(pos) + cname = self.type.cname name = '' - type.return_type = self.lookup(self.name).type + type.return_type = PyrexTypes.InvisibleVoidType() + elif name == '__dealloc__' and cname is None: + cname = "~%s" % self.type.cname + name = '' + type.return_type = PyrexTypes.InvisibleVoidType() prev_entry = self.lookup_here(name) entry = self.declare_var(name, type, pos, + defining=defining, cname=cname, visibility=visibility) - if prev_entry: + if prev_entry and not defining: entry.overloaded_alternatives = prev_entry.all_alternatives() entry.utility_code = utility_code type.entry = entry diff --git a/tests/run/cpp_classes_def.pyx b/tests/run/cpp_classes_def.pyx new file mode 100644 index 0000000..1173d36 --- /dev/null +++ b/tests/run/cpp_classes_def.pyx @@ -0,0 +1,93 @@ +# cython: experimental_cpp_class_def=True +# tag: cpp + +cdef double pi +from math import pi +from libc.math cimport sin, cos + +cdef extern from "shapes.h" namespace "shapes": + cdef cppclass Shape: + float area() + +cdef cppclass RegularPolygon(Shape): + float radius # major + int n + __init__(int n, float radius): + this.n = n + this.radius = radius + float area(): + cdef double theta = pi / this.n + return this.radius * this.radius * sin(theta) * cos(theta) * this.n + +def test_Poly(int n, float radius=1): + """ + >>> test_Poly(4) + 2.0 + >>> test_Poly(3) + 1.299038052558899 + >>> test_Poly(3, 10.0) + 129.90380859375 + >>> test_Poly(100) + 3.139525890350342 + >>> test_Poly(1000) + 3.1415719985961914 + """ + cdef RegularPolygon* poly + try: + poly = new RegularPolygon(n, radius) + poly.n = n + poly.radius = radius + return poly.area() + finally: + del poly + + +cdef cppclass InitDealloc: + __init__(): + print "Init" + __dealloc__(): + print "Dealloc" + +def test_init_dealloc(): + """ + >>> test_init_dealloc() + start + Init + live + Dealloc + end + """ + print "start" + cdef InitDealloc *ptr = new InitDealloc() + print "live" + del ptr + print "end" + + +cdef cppclass WithTemplate[T]: + T value + void set_value(T value): + this.value = value + T get_value(): + return this.value + +cdef cppclass ResolveTemplate(WithTemplate[long]): + pass + +def test_templates(long value): + """ + >>> test_templates(10) + >>> test_templates(-2) + """ + cdef WithTemplate[long] *base = new WithTemplate[long]() + del base + + cdef ResolveTemplate *resolved = new ResolveTemplate() + resolved.set_value(value) + assert resolved.value == resolved.get_value() == value, resolved.value + + base = resolved + base.set_value(2 * value) + assert base.get_value() == base.value == 2 * value, base.value + + del base -- 2.7.4