Detect more cases where code was being generated which wouldn't compile due to missing virtual method implementation; handle them gracefully.
This commit is contained in:
1 parent
a98d53c904
commit
dc9f88b633
3 files changed
+49
-56
No files matched your search
+36
-40
@@ -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(
|
||||
|
||||
+12
-16
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user