From 42805c021d74ea9f32fa75b68f5c6f94dcaa740f Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Wed, 2 Jan 2008 13:28:22 +0000 Subject: [PATCH] Add support for protected/private virtual methods. --- examples/foo.h | 23 +++++++++++++++++++++++ examples/foomodulegen.py | 20 ++++++++++++++++++++ examples/footest.py | 19 +++++++++++++++++++ pybindgen/cppclass.py | 38 +++++++++++++++++++++++--------------- pybindgen/cppmethod.py | 24 +++++++++++++++++++++--- pybindgen/gccxmlparser.py | 37 +++++++++++++++++++++++++------------ 6 files changed, 131 insertions(+), 30 deletions(-) diff --git a/examples/foo.h b/examples/foo.h index ae4d0b8..b153cef 100644 --- a/examples/foo.h +++ b/examples/foo.h @@ -542,4 +542,27 @@ inline AbstractBaseClass* get_abstract_base_class_ptr2 () } +// Class to test private/protected virtual methods +class AbstractBaseClass2 +{ +public: + AbstractBaseClass2 () {} + virtual ~AbstractBaseClass2 () {} + + int invoke_private_virtual (int x) { + return private_virtual (x); + } + + int invoke_protected_virtual (int x) { + return protected_virtual (x); + } + +protected: + virtual int protected_virtual (int x) { return x+1; } + +private: + virtual int private_virtual (int x) = 0; +}; + + #endif /* !FOO_H_ */ diff --git a/examples/foomodulegen.py b/examples/foomodulegen.py index ed81992..32a4224 100755 --- a/examples/foomodulegen.py +++ b/examples/foomodulegen.py @@ -343,6 +343,26 @@ def my_module_gen(out_file): mod.add_enum(Enum('NestedEnum', ['FOO_TYPE_AAA', 'FOO_TYPE_BBB', 'FOO_TYPE_CCC'], outer_class=SomeObject)) + + + SomeObject = CppClass('AbstractBaseClass2', allow_subclassing=True) + mod.add_class(SomeObject) + + SomeObject.add_method(CppMethod(ReturnValue.new('int'), 'invoke_private_virtual', + [Parameter.new('int', 'x')])) + SomeObject.add_method(CppMethod(ReturnValue.new('int'), 'invoke_protected_virtual', + [Parameter.new('int', 'x')])) + SomeObject.add_constructor(CppConstructor([])) + + SomeObject.add_method(CppMethod( + ReturnValue.new('int'), 'protected_virtual', [Parameter.new('int', 'x')], + is_virtual=True, visibility='protected')) + + SomeObject.add_method(CppMethod( + ReturnValue.new('int'), 'private_virtual', [Parameter.new('int', 'x')], + is_virtual=True, is_pure_virtual=True, visibility='private')) + + class MyErrorHandler(pybindgen.settings.ErrorHandler): def __init__(self): self.num_errors = 0 diff --git a/examples/footest.py b/examples/footest.py index f44dfe5..1563e49 100644 --- a/examples/footest.py +++ b/examples/footest.py @@ -593,5 +593,24 @@ class TestFoo(unittest.TestCase): def test_nested_enum(self): self.assert_(hasattr(foo.SomeObject, "FOO_TYPE_BBB")) + def test_private_virtual(self): + class Class(foo.AbstractBaseClass2): + def _private_virtual(self, x): + return x*x + c = Class() + x = c.invoke_private_virtual(2) + self.assertEqual(x, 4) + + def test_protected_virtual(self): + class Class(foo.AbstractBaseClass2): + def _private_virtual(self, x): + return x*x + def _protected_virtual(self, x): + y = super(Class, self)._protected_virtual(x) + return y*y + c = Class() + x = c.invoke_protected_virtual(2) + self.assertEqual(x, 9) + if __name__ == '__main__': unittest.main() diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 862721d..0f87221 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -134,6 +134,10 @@ void set_pyobj(PyObject *pyobj) code_sink.writeln() parent_caller.generate_declaration(code_sink) + for parent_caller_wrapper in parent_caller.wrappers: + parent_caller_wrapper.generate_parent_caller_method(code_sink) + + ## write the virtual proxies for virtual_proxy in self.virtual_proxies: virtual_proxy.class_ = self.class_ @@ -641,18 +645,20 @@ public: method.is_virtual = False method.is_pure_virtual = False method.self_parameter_pystruct = self.pystruct + method.visibility = 'public' else: raise TypeError - - try: - overload = self.methods[name] - except KeyError: - overload = CppOverloadedMethod(name) - overload.pystruct = self.pystruct - self.methods[name] = overload + + if method.visibility == 'public': + try: + overload = self.methods[name] + except KeyError: + overload = CppOverloadedMethod(name) + overload.pystruct = self.pystruct + self.methods[name] = overload - method.class_ = self - overload.add(method) + method.class_ = self + overload.add(method) if method.is_pure_virtual: self.have_pure_virtual_methods = True if method.is_virtual: @@ -662,10 +668,11 @@ public: helper_class = self.get_helper_class() if helper_class is not None: if not method.is_pure_virtual: - parent_caller = CppVirtualMethodParentCaller(method) - parent_caller.main_wrapper = method - helper_class.add_virtual_parent_caller(parent_caller) - + if method.visibility in ['public', 'protected']: + parent_caller = CppVirtualMethodParentCaller(method) + parent_caller.main_wrapper = method + helper_class.add_virtual_parent_caller(parent_caller) + proxy = CppVirtualMethodProxy(method) proxy.main_wrapper = method helper_class.add_virtual_proxy(proxy) @@ -866,7 +873,8 @@ typedef struct { def _generate_constructor(self, code_sink): """generate the constructor, if any""" have_constructor = True - if self.constructors and not self.cannot_be_constructed: + if self.constructors and ((not self.cannot_be_constructed) or self.helper_class is not None + and not self.helper_class.cannot_be_constructed): code_sink.writeln() overload = CppOverloadedConstructor(None) self.constructors_overload = overload @@ -880,7 +888,7 @@ typedef struct { ## In C++, and unlike Python, constructors with ## parameters are not automatically inheritted by ## subclasses. We must generate a 'no constructor' - ## tp_init to prevent this type from inheriring a + ## tp_init to prevent this type from inheriting a ## tp_init that will allocate an instance of the ## parent class instead of this class. code_sink.writeln() diff --git a/pybindgen/cppmethod.py b/pybindgen/cppmethod.py index 73645b7..390c6de 100644 --- a/pybindgen/cppmethod.py +++ b/pybindgen/cppmethod.py @@ -19,7 +19,7 @@ class CppMethod(ForwardWrapperBase): def __init__(self, return_value, method_name, parameters, is_static=False, template_parameters=(), is_virtual=False, is_const=False, unblock_threads=None, is_pure_virtual=False, - custom_template_method_name=None): + custom_template_method_name=None, visibility='public'): """ return_value -- the method return value method_name -- name of the method @@ -32,6 +32,8 @@ class CppMethod(ForwardWrapperBase): return_value, parameters, "return NULL;", "return NULL;", unblock_threads=unblock_threads) + assert visibility in ['public', 'protected', 'private'] + self.visibility = visibility self.method_name = method_name self.is_static = is_static self.is_virtual = is_virtual @@ -393,13 +395,29 @@ class CppVirtualMethodParentCaller(CppMethod): retline, line = self.get_wrapper_signature( '_wrap_'+self.method_name+overload_str, extra_wrapper_parameters) code_sink.writeln(' '.join(['static', retline, line]) + ';') - self.reset_code_generation_state() + def generate_parent_caller_method(self, code_sink): + ## generate a '%s__parent_caller' method (static methods + ## cannot "conquer" 'protected' access type, only regular + ## instance methods). + code_sink.writeln('inline %s %s__parent_caller(%s)' % ( + self.return_value.ctype, + self.method_name, + ', '.join([join_ctype_and_name(param.ctype, param.name) for param in self.parameters]) + )) + if self.return_value.ctype == 'void': + code_sink.writeln('{ %s::%s(%s); }' % (self._class.full_name, self.method_name, + ', '.join([param.name for param in self.parameters]))) + else: + code_sink.writeln('{ return %s::%s(%s); }' % (self._class.full_name, self.method_name, + ', '.join([param.name for param in self.parameters]))) + + def generate_call(self, class_): "virtual method implementation; do not call" #assert isinstance(class_, CppClass) - method = 'self->obj->%s::%s' % (class_.full_name, self.method_name) + method = 'reinterpret_cast< %s* >(self->obj)->%s__parent_caller' % (self._helper_class.name, self.method_name) if self.return_value.ctype == 'void': self.before_call.write_code( '%s(%s);' % diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index 8a38d5c..f666a2f 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -529,7 +529,7 @@ class ModuleParser(object): def _class_has_virtual_methods(self, cls): """return True if cls has at least one virtual method, else False""" - for member in cls.get_members('public'): + for member in cls.get_members(): if isinstance(member, calldef.member_function_t): if member.virtuality != calldef.VIRTUALITY_TYPES.NOT_VIRTUAL: return True @@ -553,15 +553,15 @@ class ModuleParser(object): ## look for protected or private pure virtual functions; if any is found, ## then the class cannot be constructed (because private/protected ## virtual functions not yet implemented. - pure_virtual = (member.virtuality == calldef.VIRTUALITY_TYPES.PURE_VIRTUAL) - if pure_virtual: - warnings.warn_explicit("%s: protected/private virtual functions not yet implemented " - "by PyBindGen, so the constructor for the class will " - "have to be disabled to avoid compilation errors." - % member, - Warning, member.location.file_name, member.location.line) - class_wrapper.set_cannot_be_constructed(True) - break + #pure_virtual = (member.virtuality == calldef.VIRTUALITY_TYPES.PURE_VIRTUAL) + #if pure_virtual: + # warnings.warn_explicit("%s: protected/private virtual functions not yet implemented " + # "by PyBindGen, so the constructor for the class will " + # "have to be disabled to avoid compilation errors." + # % member, + # Warning, member.location.file_name, member.location.line) + # class_wrapper.set_cannot_be_constructed(True) + # break elif isinstance(member, calldef.constructor_t): if member.access_type not in ['protected', 'private']: @@ -577,7 +577,7 @@ class ModuleParser(object): if cpp_class is class_wrapper and is_reference: have_copy_constructor = True - for member in cls.get_members('public'): + for member in cls.get_members(): if member.name in [class_wrapper.incref_method, class_wrapper.decref_method]: continue @@ -650,7 +650,8 @@ class ModuleParser(object): is_virtual=(is_virtual and class_wrapper.allow_subclassing), is_pure_virtual=pure_virtual, template_parameters=template_parameters, - custom_template_method_name=custom_template_method_name) + custom_template_method_name=custom_template_method_name, + visibility=member.access_type) method_wrapper.gccxml_definition = member try: class_wrapper.add_method(method_wrapper) @@ -661,6 +662,9 @@ class ModuleParser(object): ## ------------ constructor -------------------- elif isinstance(member, calldef.constructor_t): + if member.access_type != 'public': + continue + if not member.arguments: have_trivial_constructor = True @@ -688,6 +692,15 @@ class ModuleParser(object): ## ------------ attribute -------------------- elif isinstance(member, variable_t): + if member.access_type == 'protected': + warnings.warn_explicit("%s: protected member variables not yet implemented " + "by PyBindGen." + % member, + Warning, member.location.file_name, member.location.line) + continue + if member.access_type == 'private': + continue + try: return_type = type_registry.lookup_return(member.type) except (TypeLookupError, TypeConfigurationError), ex: