From dc9f88b63388c270bb2c76d2af804dedb4deea2d Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 23 Dec 2007 21:19:06 +0000 Subject: [PATCH] Detect more cases where code was being generated which wouldn't compile due to missing virtual method implementation; handle them gracefully. --- pybindgen/cppclass.py | 76 +++++++++++++++++++-------------------- pybindgen/cppmethod.py | 28 +++++++-------- pybindgen/gccxmlparser.py | 1 + 3 files changed, 49 insertions(+), 56 deletions(-) diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 398e3b3..6cc6e02 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -35,6 +35,7 @@ class CppHelperClass(object): self.name = class_.name + "_PythonProxy" self.virtual_parent_callers = {} self.virtual_proxies = [] + self.cannot_be_constructed = False def add_virtual_parent_caller(self, parent_caller): """Add a new CppVirtualMethodParentCaller object to this helper class""" @@ -122,6 +123,8 @@ class CppHelperClass(object): utils.call_with_error_handling(virtual_proxy.generate, (NullCodeSink(),), {}, virtual_proxy) except utils.SkipWrapper: + if virtual_proxy.method.is_pure_virtual: + self.cannot_be_constructed = True continue finally: virtual_proxy.reset_code_generation_state() @@ -256,6 +259,7 @@ class CppClass(object): self.slots = dict() self.helper_class = None self.cannot_be_constructed = False + self.have_pure_virtual_methods = False ## list of CppClasses from which a value of this class can be ## implicitly generated; corresponds to a ## operator ThisClass(); in the other class. @@ -341,6 +345,23 @@ class CppClass(object): self._inherit_default_constructors() + def get_construct_name(self): + """Get a name usable for new %s construction, or raise + CodeGenerationError if none found""" + if self.cannot_be_constructed: + raise CodeGenerationError("%s cannot be constructed" % self.full_name) + if self.have_pure_virtual_methods: + if self.helper_class is None: + raise CodeGenerationError("%s cannot be constructed" % self.full_name) + else: + if self.helper_class.cannot_be_constructed: + raise CodeGenerationError("%s cannot be constructed" % self.full_name) + else: + return self.helper_class.name + else: + return self.full_name + + def implicitly_converts_to(self, other): """ Declares that values of this class can be implicitly converted @@ -513,6 +534,7 @@ public: method.parameters[0].take_value_from_python_self = True method.module = self.module method.is_virtual = False + method.is_pure_virtual = False method.self_parameter_pystruct = self.pystruct else: raise TypeError @@ -533,19 +555,17 @@ public: "support was not enabled for this class") helper_class = self.get_helper_class() - parent_caller = CppVirtualMethodParentCaller(method.return_value, - method.method_name, - method.parameters) + parent_caller = CppVirtualMethodParentCaller(method) parent_caller.main_wrapper = method helper_class.add_virtual_parent_caller(parent_caller) - proxy = CppVirtualMethodProxy(method.return_value, - method.method_name, - method.parameters, - is_const=method.is_const) + proxy = CppVirtualMethodProxy(method) proxy.main_wrapper = method helper_class.add_virtual_proxy(proxy) + if method.is_pure_virtual: + self.have_pure_virtual_methods = True + def set_cannot_be_constructed(self, flag=True): self.cannot_be_constructed = flag @@ -952,10 +972,8 @@ class CppClassParameter(CppClassParameterBase): wrapper.before_call.write_code( "%s->inst_dict = NULL;" % (self.py_name,)) - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.before_call.write_code( - "%s->obj = new %s(%s);" % (self.py_name, self.cpp_class.full_name, self.value)) + "%s->obj = new %s(%s);" % (self.py_name, self.cpp_class.get_construct_name(), self.value)) wrapper.build_params.add_parameter("N", [self.py_name]) @@ -1011,12 +1029,8 @@ class CppClassRefParameter(CppClassParameterBase): if self.cpp_class.allow_subclassing: wrapper.after_call.write_code( "%s->inst_dict = NULL;" % (self.py_name,)) - - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.before_call.write_code( - "%s->obj = new %s;" % (self.py_name, self.cpp_class.full_name)) - + "%s->obj = new %s;" % (self.py_name, self.cpp_class.get_construct_name())) wrapper.call_params.append('*%s->obj' % (self.py_name,)) wrapper.build_params.add_parameter("N", [self.py_name]) @@ -1055,11 +1069,8 @@ class CppClassRefParameter(CppClassParameterBase): "%s->inst_dict = NULL;" % (self.py_name,)) if self.direction == Parameter.DIRECTION_IN: - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.before_call.write_code( - "%s->obj = new %s(%s);" % (self.py_name, self.cpp_class.full_name, self.value)) - + "%s->obj = new %s(%s);" % (self.py_name, self.cpp_class.get_construct_name(), self.value)) wrapper.build_params.add_parameter("N", [self.py_name]) else: ## out/inout case: @@ -1081,15 +1092,13 @@ class CppClassRefParameter(CppClassParameterBase): ## of the original object. Else the ->obj pointer is ## simply erased (we never owned this object in the first ## place). - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.after_call.write_code( "if (%s->ob_refcnt == 1)\n" " %s->obj = NULL;\n" "else\n" " %s->obj = new %s(%s);" % (self.py_name, self.py_name, self.py_name, - self.cpp_class.full_name, self.value)) + self.cpp_class.get_construct_name(), self.value)) @@ -1123,12 +1132,8 @@ class CppClassReturnValue(CppClassReturnValueBase): if self.cpp_class.allow_subclassing: wrapper.after_call.write_code( "%s->inst_dict = NULL;" % (py_name,)) - - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.after_call.write_code( - "%s->obj = new %s(%s);" % (py_name, self.cpp_class.full_name, self.value)) - + "%s->obj = new %s(%s);" % (py_name, self.cpp_class.get_construct_name(), self.value)) wrapper.build_params.add_parameter("N", [py_name], prepend=True) def convert_python_to_c(self, wrapper): @@ -1267,12 +1272,9 @@ class CppClassPtrParameter(CppClassParameterBase): ## the object after the call. if self.direction == Parameter.DIRECTION_IN: - - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.before_call.write_code( "%s->obj = new %s(*%s);" % - (self.py_name, self.cpp_class.full_name, self.value)) + (self.py_name, self.cpp_class.get_construct_name(), self.value)) wrapper.build_params.add_parameter("N", [self.py_name]) else: @@ -1295,15 +1297,13 @@ class CppClassPtrParameter(CppClassParameterBase): ## of the original object. Else the ->obj pointer is ## simply erased (we never owned this object in the first ## place). - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.after_call.write_code( "if (%s->ob_refcnt == 1)\n" " %s->obj = NULL;\n" "else\n" " %s->obj = new %s(*%s);" % (self.py_name, self.py_name, self.py_name, - self.cpp_class.full_name, value)) + self.cpp_class.get_construct_name(), value)) else: ## The PyObject gets a new reference to the same obj @@ -1457,11 +1457,9 @@ class CppClassPtrReturnValue(CppClassReturnValueBase): else: if self.cpp_class.incref_method is None: ## The PyObject creates its own copy - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.after_call.write_code( "%s->obj = new %s(*%s);" - % (py_name, self.cpp_class.full_name, value)) + % (py_name, self.cpp_class.get_construct_name(), value)) else: ## The PyObject gets a new reference to the same obj wrapper.after_call.write_code( @@ -1519,11 +1517,9 @@ class CppClassPtrReturnValue(CppClassReturnValueBase): if self.caller_owns_return: if self.cpp_class.incref_method is None: ## the caller receives a copy - if self.cpp_class.cannot_be_constructed: - raise CodeGenerationError("%s cannot be constructed" % self.cpp_class.full_name) wrapper.after_call.write_code( "%s = new %s(*%s);" - % (self.value, self.cpp_class.full_name, value)) + % (self.value, self.cpp_class.get_construct_name(), value)) else: ## the caller gets a new reference to the same obj wrapper.after_call.write_code( diff --git a/pybindgen/cppmethod.py b/pybindgen/cppmethod.py index ac00e31..2175e47 100644 --- a/pybindgen/cppmethod.py +++ b/pybindgen/cppmethod.py @@ -18,7 +18,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): + unblock_threads=None, is_pure_virtual=False): """ return_value -- the method return value method_name -- name of the method @@ -34,6 +34,7 @@ class CppMethod(ForwardWrapperBase): self.method_name = method_name self.is_static = is_static self.is_virtual = is_virtual + self.is_pure_virtual = is_pure_virtual self.is_const = is_const self.template_parameters = template_parameters self.mangled_name = utils.get_mangled_name(self.method_name, self.template_parameters) @@ -351,16 +352,14 @@ class CppVirtualMethodParentCaller(CppMethod): implementation in a parent base class. """ - def __init__(self, return_value, method_name, parameters, unblock_threads=None): + def __init__(self, method, unblock_threads=None): """ - return_value -- the method return value - method_name -- name of the method - parameters -- the method parameters """ super(CppVirtualMethodParentCaller, self).__init__( - return_value, method_name, parameters, unblock_threads=unblock_threads) + method.return_value, method.method_name, method.parameters, unblock_threads=unblock_threads) self._helper_class = None self.static_decl = False + self.method = method def set_class(self, class_): "Set the class wrapper object (CppClass)" @@ -447,15 +446,12 @@ class CppVirtualMethodProxy(ReverseWrapperBase): Class that generates a proxy virtual method that calls a similarly named python method. """ - def __init__(self, return_value, method_name, parameters, is_const=False): + def __init__(self, method): """ - return_value -- the method return value - method_name -- name of the virtual method - parameters -- the method parameters """ - super(CppVirtualMethodProxy, self).__init__(return_value, parameters) - self.method_name = method_name - self.is_const = is_const + super(CppVirtualMethodProxy, self).__init__(method.return_value, method.parameters) + self.method_name = method.method_name + self.method = method self._class = None self._helper_class = None @@ -493,7 +489,7 @@ class CppVirtualMethodProxy(ReverseWrapperBase): self.before_call.add_cleanup_code('Py_DECREF(py_retval);') def generate_declaration(self, code_sink): - if self.is_const: + if self.method.is_const: decl_post_modifiers = ' const' else: decl_post_modifiers = '' @@ -507,7 +503,7 @@ class CppVirtualMethodProxy(ReverseWrapperBase): def generate(self, code_sink): """generates the proxy virtual method""" - if self.is_const: + if self.method.is_const: decl_post_modifiers = ['const'] else: decl_post_modifiers = [] @@ -530,7 +526,7 @@ class CppVirtualMethodProxy(ReverseWrapperBase): '%s*' % self._class.full_name, 'self_obj_before') self.before_call.write_code("%s = reinterpret_cast< %s* >(m_pyself)->obj;" % (self_obj_before, self._class.pystruct)) - if self.is_const: + if self.method.is_const: this_expression = ("const_cast< %s* >((const %s*) this)" % (self._class.full_name, self._class.full_name)) else: diff --git a/pybindgen/gccxmlparser.py b/pybindgen/gccxmlparser.py index c3dc6fa..58b73c9 100644 --- a/pybindgen/gccxmlparser.py +++ b/pybindgen/gccxmlparser.py @@ -554,6 +554,7 @@ class ModuleParser(object): is_const=member.has_const, is_static=member.has_static, is_virtual=(is_virtual and class_wrapper.allow_subclassing), + is_pure_virtual=pure_virtual, template_parameters=template_parameters) method_wrapper.gccxml_definition = member class_wrapper.add_method(method_wrapper)