diff --git a/pyste/NEWS b/pyste/NEWS index 9bb1d4c7..889d1b4a 100644 --- a/pyste/NEWS +++ b/pyste/NEWS @@ -1,3 +1,21 @@ +16 June 2003 +Thanks to discussions with David Abrahams and Roman Sulzhyk, some behaviours +have changed: + +- If you export a derived class without exporting its base classes, the derived + class will explicitly export the bases's methods and attributes. Before, if + you were interested in the bases's methods, you had to export the base + classes too. + +- Added a new function, no_override. When a member function is specified as + "no_override", no virtual wrappers are generated for it, improving + performance and letting the code more clean. + +- There was a bug in which the policy of virtual member functions was being + ignored (patch by Roman Sulzhyk). + +Thanks again to Roman Sulzhyk for the patches and discussion in the c++-sig. + 4 June 2003 Major improvements in memory usage. diff --git a/pyste/doc/pyste.txt b/pyste/doc/pyste.txt index 926c0cf8..b7494dc8 100644 --- a/pyste/doc/pyste.txt +++ b/pyste/doc/pyste.txt @@ -165,8 +165,8 @@ invokes Pyste passing the interface files to it. Pyste then generates a single cpp file with Boost.Python code, with all the classes and functions exported. Besides declaring the classes and functions, the user has a number of other -options, like renaming classes and methods, excluding methods and attributes, -and so on. +options, like renaming e excluding classes and member functionis. Those are +explained later on. [h2 Basics] @@ -200,7 +200,7 @@ That will expose the class, the free function and the enum found in [^hello.h]. [page:1 Renaming and Excluding] -You can easily rename functions, classes, methods, attributes, etc. Just use the +You can easily rename functions, classes, member functions, attributes, etc. Just use the function [^rename], like this: World = Class("World", "hello.h") @@ -208,7 +208,7 @@ function [^rename], like this: show = Function("choice", "hello.h") rename(show, "Show") -You can rename methods and attributes using this syntax: +You can rename member functions and attributes using this syntax: rename(World.greet, "Greet") rename(World.set, "Set") @@ -216,7 +216,7 @@ You can rename methods and attributes using this syntax: rename(choice.red, "Red") rename(choice.blue, "Blue") -You can exclude functions, classes, methods, attributes, etc, in the same way, +You can exclude functions, classes, member functions, attributes, etc, in the same way, with the function [^exclude]: exclude(World.greet) @@ -231,12 +231,25 @@ To access the operators of a class, access the member [^operator] like this The string inside the brackets is the same as the name of the operator in C++.[br] +[h2 Virtual Member Functions] + +Pyste automatically generates wrappers for virtual member functions, but you +may want to disable this behaviour (for performance reasons, or to let the code +more clean) if you do not plan to override the functions in Python. To do +this, use the function [^no_override]: + + C = Class('C', 'C.h') + no_override(C.foo) # C::foo is a virtual member function + +No wrapper code will be generated for the virtual member function C::foo that +way. + [page:1 Policies] Even thought Pyste can identify various elements in the C++ code, like virtual -methods, attributes, and so on, one thing that it can't do is to guess the -semantics of functions that return pointers or references. In this case, the -user must manually specify the policy. Policies are explained in the +member functions, attributes, and so on, one thing that it can't do is to +guess the semantics of functions that return pointers or references. In this +case, the user must manually specify the policy. Policies are explained in the [@../../doc/tutorial/doc/call_policies.html tutorial]. The policies in Pyste are named exactly as in Boost.Python, only the syntax is @@ -248,22 +261,23 @@ becomes in Pyste: return_internal_reference(1, with_custodian_and_ward(1, 2)) -The user can specify policies for functions and methods with the [^set_policy] -function: +The user can specify policies for functions and virtual member functions with +the [^set_policy] function: set_policy(f, return_internal_reference()) set_policy(C.foo, return_value_policy(manage_new_object)) [blurb -[$theme/note.gif] [*What if a function or method needs a policy and the user -doesn't set one?][br][br] If a function/method needs a policy and one was not -set, Pyste will issue a error. The user should then go in the interface file -and set the policy for it, otherwise the generated cpp won't compile. +[$theme/note.gif] [*What if a function or member function needs a policy and +the user doesn't set one?][br][br] If a function needs a policy and one +was not set, Pyste will issue a error. The user should then go in the +interface file and set the policy for it, otherwise the generated cpp won't +compile. ] [blurb [$theme/note.gif] -Note that, for functions/methods that return [^const T&], the policy +Note that, for functions that return [^const T&], the policy [^return_value_policy()] wil be used by default, because that's normally what you want. You can change it to something else if you need to, though. @@ -371,9 +385,9 @@ You can optionally declare the function in the interface file itself: names = Function("names", "test.h") set_wrapper(names, names_wrapper) -The same mechanism can be used with methods too. Just remember that the first -parameter of wrappers for methods is a pointer to the class, like in -Boost.Python: +The same mechanism can be used with member functions too. Just remember that +the first parameter of wrappers for member functions is a pointer to the +class, as in: struct C { @@ -394,7 +408,7 @@ And then in the interface file: [$theme/note.gif]Even though Boost.Python accepts either a pointer or a reference to the class in wrappers for member functions as the first parameter, Pyste expects them to be a [*pointer]. Doing otherwise will prevent your -code to compile when you set a wrapper for a virtual method. +code to compile when you set a wrapper for a virtual member function. ] [page:1 Exporting An Entire Header] @@ -479,7 +493,8 @@ functions, and export those. [page:1 Adding New Methods] -Suppose that you want to add a function to a class, turning it into a method: +Suppose that you want to add a function to a class, turning it into a member +function: struct World { @@ -492,14 +507,14 @@ Suppose that you want to add a function to a class, turning it into a method: return w.msg; } -Here, we want to make [^greet] work as a method of the class [^World]. We do +Here, we want to make [^greet] work as a member function of the class [^World]. We do that using the [^add_method] construct: W = Class("World", "hello.h") add_method(W, "greet") Notice also that then you can rename it, set its policy, just like a regular -method: +member function: rename(W.greet, 'Greet') diff --git a/pyste/src/ClassExporter.py b/pyste/src/ClassExporter.py index a1a82d14..e0f97077 100644 --- a/pyste/src/ClassExporter.py +++ b/pyste/src/ClassExporter.py @@ -6,7 +6,7 @@ from policies import * from SingleCodeUnit import SingleCodeUnit from EnumExporter import EnumExporter from utils import makeid, enumerate -from copy import deepcopy +import copy import exporterutils import re @@ -66,20 +66,16 @@ class ClassExporter(Exporter): self.info.rename = decl.name else: self.class_ = decl - self.public_members = \ - [x for x in self.class_.members if x.visibility == Scope.public] + self.class_ = copy.deepcopy(self.class_) + def ClassBases(self): - bases = [] - def GetBases(class_): - this_bases = [self.GetDeclaration(x.name) for x in class_.bases] - bases.extend(this_bases) - for base in this_bases: - GetBases(base) - - GetBases(self.class_) - return bases + all_bases = [] + for level in self.class_.hierarchy: + for base in level: + all_bases.append(base) + return [self.GetDeclaration(x.name) for x in all_bases] def Order(self): @@ -91,6 +87,8 @@ class ClassExporter(Exporter): def Export(self, codeunit, exported_names): + self.InheritMethods(exported_names) + self.MakeNonVirtual() if not self.info.exclude: self.CheckIsForwardDeclared() self.CheckForwardDeclarations() @@ -98,8 +96,8 @@ class ClassExporter(Exporter): self.ExportBases(exported_names) self.ExportConstructors() self.ExportVariables() - self.ExportMethods() self.ExportVirtualMethods() + self.ExportMethods() self.ExportOperators() self.ExportNestedClasses(exported_names) self.ExportNestedEnums() @@ -108,10 +106,38 @@ class ClassExporter(Exporter): self.Write(codeunit) + def InheritMethods(self, exported_names): + '''Go up in the class hierarchy looking for classes that were not + exported yet, and then add their public members to this classes + members, as if they were members of this class. This allows the user to + just export one type and automatically get all the methods from the + base classes. + ''' + valid_members = (Method, ClassVariable, NestedClass, ClassOperator, + ConverterOperator, ClassEnumeration) + for level in self.class_.hierarchy: + level_exported = False + for base in level: + base = self.GetDeclaration(base.name) + if base.FullName() not in exported_names: + for member in base.members: + if type(member) in valid_members: + member = copy.deepcopy(member) + #if type(member) not in (ClassVariable,: + # member.class_ = self.class_.FullName() + self.class_.members.append(member) + else: + level_exported = True + if level_exported: + break + self.public_members = \ + [x for x in self.class_.members if x.visibility == Scope.public] + def CheckIsForwardDeclared(self): if self.class_.incomplete: print "--> Error: Class %s is forward declared! " \ - "Please use the header with its complete definition." % self.class_.FullName() + "Please use the header with its complete definition." \ + % self.class_.FullName() print @@ -191,15 +217,16 @@ class ClassExporter(Exporter): def ExportBases(self, exported_names): 'Expose the bases of the class into the template section' - bases = self.class_.bases - bases_list = [] - for base in bases: - if base.visibility == Scope.public and base.name in exported_names: - bases_list.append(base.name) - if bases_list: - code = namespaces.python + 'bases< %s > ' % \ - (', '.join(bases_list)) - self.Add('template', code) + hierarchy = self.class_.hierarchy + for level in hierarchy: + exported = [] + for base in level: + if base.visibility == Scope.public and base.name in exported_names: + exported.append(base.name) + if exported: + code = namespaces.python + 'bases< %s > ' % (', '.join(exported)) + self.Add('template', code) + return def ExportConstructors(self): @@ -374,6 +401,14 @@ class ClassExporter(Exporter): self.Add('declaration', wrapper.code) + def MakeNonVirtual(self): + '''Make all methods that the user indicated to no_override no more virtual, delegating their + export to the ExportMethods routine''' + for member in self.class_.members: + if type(member) == Method and member.virtual: + member.virtual = not self.info[member.name].no_override + + def ExportVirtualMethods(self): # check if this class has any virtual methods has_virtual_methods = False @@ -565,7 +600,6 @@ class ClassExporter(Exporter): nested_info.include = self.info.include nested_info.name = nested_class.FullName() exporter = ClassExporter(nested_info) - self.declarations.append(nested_class) exporter.SetDeclarations(self.declarations) codeunit = SingleCodeUnit(None, None) exporter.Export(codeunit, exported_names) @@ -579,7 +613,6 @@ class ClassExporter(Exporter): enum_info.include = self.info.include enum_info.name = enum.FullName() exporter = EnumExporter(enum_info) - self.declarations.append(enum) exporter.SetDeclarations(self.declarations) codeunit = SingleCodeUnit(None, None) exporter.Export(codeunit, None) @@ -746,12 +779,17 @@ class _VirtualWrapperGenerator(object): else: pointer = method.PointerDeclaration() + # Add policy to overloaded methods also + policy = self.info[method.name].policy or '' + if policy: + policy = ', %s%s()' % (namespaces.python, policy.Code()) + # generate the defs definitions = [] # basic def - definitions.append('.def("%s", %s, %s)' % (rename, pointer, default_pointers[-1])) + definitions.append('.def("%s", %s, %s%s)' % (rename, pointer, default_pointers[-1], policy)) for default_pointer in default_pointers[:-1]: - definitions.append('.def("%s", %s)' % (rename, default_pointer)) + definitions.append('.def("%s", %s%s)' % (rename, default_pointer, policy)) return definitions @@ -766,7 +804,9 @@ class _VirtualWrapperGenerator(object): This method creates the instance variable self.virtual_methods. ''' def IsVirtual(m): - return type(m) is Method and m.virtual and m.visibility != Scope.private + return type(m) is Method and \ + m.virtual and \ + m.visibility != Scope.private all_methods = [x for x in self.class_.members if IsVirtual(x)] for base in self.bases: diff --git a/pyste/src/Exporter.py b/pyste/src/Exporter.py index e72c3b39..f37ed21e 100644 --- a/pyste/src/Exporter.py +++ b/pyste/src/Exporter.py @@ -64,7 +64,7 @@ class Exporter: def GetDeclaration(self, fullname): decls = self.GetDeclarations(fullname) - assert len(decls) == 1 + #assert len(decls) == 1 return decls[0] diff --git a/pyste/src/GCCXMLParser.py b/pyste/src/GCCXMLParser.py index 1f8c7550..63f76c82 100644 --- a/pyste/src/GCCXMLParser.py +++ b/pyste/src/GCCXMLParser.py @@ -2,6 +2,7 @@ from declarations import * from elementtree.ElementTree import ElementTree from xml.parsers.expat import ExpatError from copy import deepcopy +from utils import enumerate class InvalidXMLError(Exception): pass @@ -199,14 +200,18 @@ class GCCXMLParser(object): self.ParseFunction(id, element, Operator) - def GetBases(self, bases): - 'Parses the string "bases" from the xml into a list of Base instances.' + def GetHierarchy(self, bases): + '''Parses the string "bases" from the xml into a list of tuples of Base + instances. The first tuple is the most direct inheritance, and then it + goes up in the hierarchy. + ''' if bases is None: return [] - bases = bases.split() - baseobjs = [] - for base in bases: + base_names = bases.split() + this_level = [] + next_levels = [] + for base in base_names: # get the visibility split = base.split(':') if len(split) == 2: @@ -214,10 +219,21 @@ class GCCXMLParser(object): base = split[1] else: visib = Scope.public - decl = self.GetDecl(base) - baseobj = Base(decl.FullName(), visib) - baseobjs.append(baseobj) - return baseobjs + decl = self.GetDecl(base) + base = Base(decl.FullName(), visib) + this_level.append(base) + # normalize with the other levels + for index, level in enumerate(decl.hierarchy): + if index < len(next_levels): + next_levels[index] = next_levels[index] + level + else: + next_levels.append(level) + hierarchy = [] + if this_level: + hierarchy.append(tuple(this_level)) + if next_levels: + hierarchy.extend(next_levels) + return hierarchy def GetMembers(self, members): @@ -237,20 +253,22 @@ class GCCXMLParser(object): context = self.GetDecl(element.get('context')) incomplete = bool(element.get('incomplete', False)) if isinstance(context, str): - class_ = Class(name, context, [], abstract, []) - self.AddDecl(class_) + class_ = Class(name, context, [], abstract) else: # a nested class visib = element.get('access', Scope.public) class_ = NestedClass( - name, context.FullName(), visib, [], abstract, []) + name, context.FullName(), visib, [], abstract) + self.AddDecl(class_) # we have to add the declaration of the class before trying # to parse its members and bases, to avoid recursion. class_.location = location class_.incomplete = incomplete self.Update(id, class_) # now we can get the members and the bases - class_.bases = self.GetBases(element.get('bases')) + class_.hierarchy = self.GetHierarchy(element.get('bases')) + if class_.hierarchy: + class_.bases = class_.hierarchy[0] class_.members = self.GetMembers(element.get('members')) @@ -379,11 +397,10 @@ class GCCXMLParser(object): context = self.GetDecl(element.get('context')) if isinstance(context, str): enum = Enumeration(name, context) - self.AddDecl(enum) # in this case, is a top level decl else: visib = element.get('access', Scope.public) enum = ClassEnumeration(name, context.FullName(), visib) - + self.AddDecl(enum) enum.location = location for child in element: if child.tag == 'EnumValue': diff --git a/pyste/src/declarations.py b/pyste/src/declarations.py index b8f1ef9d..20475058 100644 --- a/pyste/src/declarations.py +++ b/pyste/src/declarations.py @@ -8,8 +8,6 @@ Module declarations class Declaration(object): 'Represents a basic declaration.' - __slots__ = 'name namespace location incomplete'.split() - def __init__(self, name, namespace): # the declaration name self.name = name @@ -44,15 +42,15 @@ class Declaration(object): class Class(Declaration): 'The declaration of a class or struct.' - __slots__= 'members abstract bases _members_count'.split() - def __init__(self, name, namespace, members, abstract, bases): + def __init__(self, name, namespace, members, abstract): Declaration.__init__(self, name, namespace) # list of members self.members = members # whatever the class has any abstract methods self.abstract = abstract # instances of Base - self.bases = bases + self.bases = () + self.hierarchy = () self._members_count = {} @@ -117,10 +115,8 @@ class Class(Declaration): class NestedClass(Class): 'The declaration of a class/struct inside another class/struct.' - __slots__= 'class_ visibility'.split() - - def __init__(self, name, class_, visib, members, abstract, bases): - Class.__init__(self, name, None, members, abstract, bases) + def __init__(self, name, class_, visib, members, abstract): + Class.__init__(self, name, None, members, abstract) self.class_ = class_ self.visibility = visib @@ -133,8 +129,6 @@ class NestedClass(Class): class Base: 'Represents a base class of another class.' - __slots__= 'name visibility'.split() - def __init__(self, name, visibility=None): # class_ is the full name of the base class self.name = name @@ -155,8 +149,6 @@ class Scope: class Function(Declaration): 'The declaration of a function.' - __slots__= 'result parameters'.split() - def __init__(self, name, namespace, result, params): Declaration.__init__(self, name, namespace) # the result type: instance of Type, or None (constructors) @@ -206,8 +198,6 @@ class Operator(Function): class Method(Function): 'The declaration of a method.' - __slots__= 'visibility virtual abstract static class_ const'.split() - def __init__(self, name, class_, result, params, visib, virtual, abstract, static, const): Function.__init__(self, name, None, result, params) self.visibility = visib @@ -301,8 +291,6 @@ class ConverterOperator(ClassOperator): class Type(Declaration): 'Represents a type.' - __slots__= 'const default volatile restricted incomplete'.split() - def __init__(self, name, const=False, default=None, incomplete=False): Declaration.__init__(self, name, None) # whatever the type is constant or not @@ -339,8 +327,6 @@ class Type(Declaration): class ArrayType(Type): 'Represents an array.' - __slots__= 'min max'.split() - def __init__(self, name, const=False, default=None, incomplete=False): 'min and max can be None.' Type.__init__(self, name, const) @@ -358,8 +344,6 @@ class ArrayType(Type): class ReferenceType(Type): 'A reference type.' - __slots__= 'expand'.split() - def __init__(self, name, const=False, default=None, incomplete=False, expandRef=True): Type.__init__(self, name, const, default, incomplete) self.expand = expandRef @@ -383,8 +367,6 @@ class ReferenceType(Type): class PointerType(Type): 'A pointer type.' - __slots__= 'expand'.split() - def __init__(self, name, const=False, default=None, incomplete=False, expandPointer=False): Type.__init__(self, name, const, default, incomplete) self.expand = expandPointer @@ -415,8 +397,6 @@ class FundamentalType(Type): class FunctionType(Type): 'A pointer to a function.' - __slots__= 'result parameters name'.split() - def __init__(self, result, parameters): Type.__init__(self, '', False) self.result = result @@ -438,8 +418,6 @@ class FunctionType(Type): class MethodType(FunctionType): 'A pointer to a member function of a class.' - __slots__= 'result parameters class_ name'.split() - def __init__(self, result, parameters, class_): Type.__init__(self, '', False) self.result = result @@ -460,8 +438,6 @@ class MethodType(FunctionType): class Variable(Declaration): 'Represents a global variable.' - __slots__= 'type'.split() - def __init__(self, type, name, namespace): Declaration.__init__(self, name, namespace) # instance of Type @@ -472,8 +448,6 @@ class Variable(Declaration): class ClassVariable(Variable): 'Represents a class variable.' - __slots__= 'visibility static class_'.split() - def __init__(self, type, name, class_, visib, static): Variable.__init__(self, type, name, None) self.visibility = visib @@ -488,8 +462,6 @@ class ClassVariable(Variable): class Enumeration(Declaration): - __slots__= 'values'.split() - def __init__(self, name, namespace): Declaration.__init__(self, name, namespace) self.values = {} # dict of str => int @@ -505,8 +477,6 @@ class Enumeration(Declaration): class ClassEnumeration(Enumeration): - __slots__= 'class_ visibility'.split() - def __init__(self, name, class_, visib): Enumeration.__init__(self, name, None) self.class_ = class_ @@ -525,8 +495,6 @@ class ClassEnumeration(Enumeration): class Typedef(Declaration): - __slots__= 'type visibility'.split() - def __init__(self, type, name, namespace): Declaration.__init__(self, name, namespace) self.type = type @@ -541,8 +509,6 @@ class Union(Declaration): class ClassUnion(Union): - __slots__= 'class_ visibility'.split() - def __init__(self, name, class_, visib): Union.__init__(self, name, None) self.class_ = class_ diff --git a/pyste/src/infos.py b/pyste/src/infos.py index 863d8ad4..67b5f553 100644 --- a/pyste/src/infos.py +++ b/pyste/src/infos.py @@ -215,3 +215,6 @@ def add_method(info, name, rename=None): info._Attribute('__added__', [(name, rename)]) else: added.append((name, rename)) + +def no_override(info): + info._Attribute('no_override', True) diff --git a/pyste/src/pyste.py b/pyste/src/pyste.py index 39938caf..cbb0090d 100644 --- a/pyste/src/pyste.py +++ b/pyste/src/pyste.py @@ -139,6 +139,7 @@ def CreateContext(): context['use_shared_ptr'] = infos.use_shared_ptr context['use_auto_ptr'] = infos.use_auto_ptr context['add_method'] = infos.add_method + context['no_override'] = infos.no_override # policies context['return_internal_reference'] = return_internal_reference context['with_custodian_and_ward'] = with_custodian_and_ward diff --git a/pyste/tests/inherit.cpp b/pyste/tests/inherit.cpp new file mode 100644 index 00000000..d5c34e96 --- /dev/null +++ b/pyste/tests/inherit.cpp @@ -0,0 +1,3 @@ +#include "inherit.h" + +int inherit::C::s = 1; diff --git a/pyste/tests/inherit.h b/pyste/tests/inherit.h index 04caddae..0703ddc5 100644 --- a/pyste/tests/inherit.h +++ b/pyste/tests/inherit.h @@ -1,3 +1,6 @@ + +namespace inherit { + template class A { @@ -16,3 +19,21 @@ class B : public A public: int go() { return get(); } }; + +struct C : B +{ + enum ab { a = 1, b = 2 }; + int f1() { return 1; } + int x; + static int s; +}; + +struct D : C +{ + int f2() { return 2; } + int y; +}; + +struct X {}; +struct E: X, D {}; +} diff --git a/pyste/tests/inherit.pyste b/pyste/tests/inherit.pyste index 2531ba83..0dc02998 100644 --- a/pyste/tests/inherit.pyste +++ b/pyste/tests/inherit.pyste @@ -1,8 +1,8 @@ -# Doesn't work: -A = Template('A', 'inherit.h') -A_int = A('int') +A = Template('inherit::A', 'inherit.h') +A_int = A('int', 'A_int') -Class('B', 'inherit.h') - -# Does work: -#AllFromHeader('inherit.h') +Class('inherit::B', 'inherit.h') +Class('inherit::D', 'inherit.h') +E = Class('inherit::E', 'inherit.h') +exclude(E.s) +exclude(E.ab) diff --git a/pyste/tests/inheritUT.py b/pyste/tests/inheritUT.py index 67be4fa4..0df82799 100644 --- a/pyste/tests/inheritUT.py +++ b/pyste/tests/inheritUT.py @@ -14,6 +14,15 @@ class InheritExampleTest(unittest.TestCase): self.assertEqual(b.go(), 1) self.assertEqual(b.get(), 1) + d = D() + self.assert_(issubclass(D, B)) + self.assertEqual(d.x, 0) + self.assertEqual(d.y, 0) + self.assertEqual(d.s, 1) + self.assertEqual(D.s, 1) + self.assertEqual(d.f1(), 1) + self.assertEqual(d.f2(), 2) + if __name__ == '__main__': diff --git a/pyste/tests/vars.cpp b/pyste/tests/vars.cpp new file mode 100644 index 00000000..03994cff --- /dev/null +++ b/pyste/tests/vars.cpp @@ -0,0 +1,7 @@ +#include "vars.h" + +const Color black = Color(0, 0, 0); +const Color red = Color(255, 0, 0); +const Color green = Color(0, 255, 0); +const Color blue = Color(0, 0, 255); +Color in_use = black; diff --git a/pyste/tests/virtual.h b/pyste/tests/virtual.h index cd194315..c7880a1b 100644 --- a/pyste/tests/virtual.h +++ b/pyste/tests/virtual.h @@ -15,6 +15,7 @@ public: { return name(); } + virtual int dummy() { return 0; } protected: virtual int f_abs() = 0; @@ -23,6 +24,13 @@ private: virtual const char* name() { return "C"; } }; +struct D +{ + virtual int dummy() { return 0; } +}; + inline int call_f(C& c) { return c.f(); } +inline int call_dummy(C* c) { return c->dummy(); } +inline int call_dummy(D* d) { return d->dummy(); } } diff --git a/pyste/tests/virtual.pyste b/pyste/tests/virtual.pyste index 3fa072f8..f148ad97 100644 --- a/pyste/tests/virtual.pyste +++ b/pyste/tests/virtual.pyste @@ -1,2 +1,6 @@ -Class('virtual_::C', 'virtual.h') +C = Class('virtual_::C', 'virtual.h') +no_override(C.dummy) +D = Class('virtual_::D', 'virtual.h') +no_override(D.dummy) Function('virtual_::call_f', 'virtual.h') +Function('virtual_::call_dummy', 'virtual.h') diff --git a/pyste/tests/virtual2.h b/pyste/tests/virtual2.h index 0f5aab32..7c9659d3 100644 --- a/pyste/tests/virtual2.h +++ b/pyste/tests/virtual2.h @@ -5,12 +5,14 @@ struct A { virtual int f() { return 0; } virtual int f1() { return 10; } + virtual A* make_new() { return new A; } }; struct B: A { virtual int f() { return 1; } virtual int f2() { return 20; } + virtual A* make_new() { return new B; } }; inline int call_fs(A*a) diff --git a/pyste/tests/virtual2.pyste b/pyste/tests/virtual2.pyste index 2ca567e6..785b819c 100644 --- a/pyste/tests/virtual2.pyste +++ b/pyste/tests/virtual2.pyste @@ -1,4 +1,6 @@ -Class('virtual2::A', 'virtual2.h') -Class('virtual2::B', 'virtual2.h') +A = Class('virtual2::A', 'virtual2.h') +set_policy(A.make_new, return_value_policy(manage_new_object)) +B = Class('virtual2::B', 'virtual2.h') +set_policy(B.make_new, return_value_policy(manage_new_object)) Function('virtual2::call_fs', 'virtual2.h') Function('virtual2::call_f', 'virtual2.h') diff --git a/pyste/tests/virtual2UT.py b/pyste/tests/virtual2UT.py index 25dd81fe..2a722711 100644 --- a/pyste/tests/virtual2UT.py +++ b/pyste/tests/virtual2UT.py @@ -12,7 +12,14 @@ class Virtual2Test(unittest.TestCase): self.assertEqual(call_fs(b), 30) self.assertEqual(call_f(a), 0) self.assertEqual(call_f(b), 1) - + nb = b.make_new() + na = a.make_new() + self.assertEqual(na.f1(), 10) + self.assertEqual(nb.f1(), 10) + self.assertEqual(nb.f2(), 20) + self.assertEqual(call_fs(nb), 30) + self.assertEqual(call_f(na), 0) + self.assertEqual(call_f(nb), 1) class C(B): def f1(self): return 1 def f2(self): return 2 diff --git a/pyste/tests/virtualUT.py b/pyste/tests/virtualUT.py index a3f39661..767965f9 100644 --- a/pyste/tests/virtualUT.py +++ b/pyste/tests/virtualUT.py @@ -5,33 +5,45 @@ class VirtualTest(unittest.TestCase): def testIt(self): - class D(C): + class E(C): def f_abs(self): return 3 + def dummy(self): + # override should not work + return 100 - class E(C): + class F(C): def f(self): return 10 def name(self): - return 'E' + return 'F' + + class G(D): + def dummy(self): + # override should not work + return 100 - d = D() e = E() + f = F() - self.assertEqual(d.f(), 3) - self.assertEqual(call_f(d), 3) - self.assertEqual(e.f(), 10) - self.assertEqual(call_f(e), 10) - self.assertEqual(d.get_name(), 'C') + self.assertEqual(e.f(), 3) + self.assertEqual(call_f(e), 3) + self.assertEqual(f.f(), 10) + self.assertEqual(call_f(f), 10) + self.assertEqual(e.get_name(), 'C') #self.assertEqual(e.get_name(), 'E') check this later c = C() - def bar(arg): - c.bar(arg) - bar(1) # ok - bar('a') # ok - self.assertRaises(TypeError, bar, 1.0) - + c.bar(1) # ok + c.bar('a') # ok + self.assertRaises(TypeError, c.bar, 1.0) + + # test no_overrides + d = G() + self.assertEqual(e.dummy(), 100) + self.assertEqual(call_dummy(e), 0) + self.assertEqual(d.dummy(), 100) + self.assertEqual(call_dummy(d), 0)