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:
Gustavo J. A. M. Carneiro committed 2007-12-23 21:19:06 +00:00
1 parent a98d53c904
commit dc9f88b633
3 files changed
+49 -56

No files matched your search

+36 -40
View File
@@ -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
View File
@@ -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:
+1
View File
@@ -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)