Re-implement implicit conversions using a non-combinatorial approach, for greater efficiency.

This commit is contained in:
Gustavo J. A. M. Carneiro committed 2008-03-21 18:10:42 +00:00
1 parent f9baba1bc9
commit 944cef3e14
3 files changed
+122 -48

No files matched your search

+75 -11
View File
@@ -1313,15 +1313,47 @@ class CppClassParameter(CppClassParameterBase):
"parses python args to get C++ value"
assert isinstance(wrapper, ForwardWrapperBase)
assert isinstance(self.cpp_class, CppClass)
if self.take_value_from_python_self:
self.py_name = 'self'
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
else:
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name)
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
implicit_conversion_sources = self.cpp_class.get_all_implicit_conversions()
if not implicit_conversion_sources:
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name)
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
else:
self.py_name = wrapper.declarations.declare_variable(
'PyObject*', self.name)
tmp_value_variable = wrapper.declarations.declare_variable(
self.cpp_class.full_name, self.name)
wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name)
wrapper.before_call.write_code("if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, self.cpp_class.pytypestruct,
tmp_value_variable,
self.cpp_class.pystruct, self.py_name))
for conversion_source in implicit_conversion_sources:
wrapper.before_call.write_code("} else if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, conversion_source.pytypestruct,
tmp_value_variable,
conversion_source.pystruct, self.py_name))
wrapper.before_call.write_code("} else {\n")
wrapper.before_call.indent()
possible_type_names = ", ".join([cls.name for cls in [self.cpp_class] + implicit_conversion_sources])
wrapper.before_call.write_code("PyErr_Format(PyExc_TypeError, \"parameter must an instance of one of the types (%s), not %%s\", %s->ob_type->tp_name);" % (possible_type_names, self.py_name))
wrapper.before_call.write_error_return()
wrapper.before_call.unindent()
wrapper.before_call.write_code("}")
wrapper.call_params.append(tmp_value_variable)
def convert_c_to_python(self, wrapper):
'''Write some code before calling the Python method.'''
@@ -1374,12 +1406,44 @@ class CppClassRefParameter(CppClassParameterBase):
if self.direction == Parameter.DIRECTION_IN:
if self.take_value_from_python_self:
self.py_name = 'self'
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
else:
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name)
wrapper.call_params.append('*%s->obj' % (self.py_name,))
implicit_conversion_sources = self.cpp_class.get_all_implicit_conversions()
if not (implicit_conversion_sources and self.is_const):
self.py_name = wrapper.declarations.declare_variable(
self.cpp_class.pystruct+'*', self.name)
wrapper.parse_params.add_parameter(
'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name)
wrapper.call_params.append(
'*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name))
else:
self.py_name = wrapper.declarations.declare_variable(
'PyObject*', self.name)
tmp_value_variable = wrapper.declarations.declare_variable(
self.cpp_class.full_name, self.name)
wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name)
wrapper.before_call.write_code("if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, self.cpp_class.pytypestruct,
tmp_value_variable,
self.cpp_class.pystruct, self.py_name))
for conversion_source in implicit_conversion_sources:
wrapper.before_call.write_code("} else if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n"
" %s = *((%s *) %s)->obj;" %
(self.py_name, conversion_source.pytypestruct,
tmp_value_variable,
conversion_source.pystruct, self.py_name))
wrapper.before_call.write_code("} else {\n")
wrapper.before_call.indent()
possible_type_names = ", ".join([cls.name for cls in [self.cpp_class] + implicit_conversion_sources])
wrapper.before_call.write_code("PyErr_Format(PyExc_TypeError, \"parameter must an instance of one of the types (%s), not %%s\", %s->ob_type->tp_name);" % (possible_type_names, self.py_name))
wrapper.before_call.write_error_return()
wrapper.before_call.unindent()
wrapper.before_call.write_code("}")
wrapper.call_params.append(tmp_value_variable)
elif self.direction == Parameter.DIRECTION_OUT:
assert not self.take_value_from_python_self
+39 -35
View File
@@ -50,7 +50,7 @@ class OverloadedWrapper(object):
self.wrapper_function_name = None
self.pystruct = 'PyObject'
self.static_decl = True
self.enable_implicit_conversions = True
# self.enable_implicit_conversions = True
def add(self, wrapper):
"""
@@ -96,40 +96,44 @@ class OverloadedWrapper(object):
self.all_wrappers = []
for wrapper in self.wrappers:
self.all_wrappers.append(wrapper)
if not self.enable_implicit_conversions:
continue
## add additional wrappers to support implicit conversion
if not hasattr(wrapper, "clone"):
continue
implicit_conversion_classes = []
implicit_conversion_positions = []
for pos, param in enumerate(wrapper.parameters):
if not isinstance(param, (CppClassParameter, CppClassRefParameter)):
continue
if isinstance(param, (CppClassRefParameter)) and not param.is_const:
continue
conversion_sources = param.cpp_class.get_all_implicit_conversions()
if conversion_sources:
conversion_sources.insert(0, param.cpp_class)
implicit_conversion_classes.append(conversion_sources)
implicit_conversion_positions.append(pos)
if not implicit_conversion_classes:
continue
## Now generate the addition wrappers, with all possible
## combinations of implicit conversion source classes...
combinations = vector_counter(implicit_conversion_classes)
## skip the first one, as it is the same as in the original wrapper
combinations.next()
for combination in combinations:
wrapper_clone = wrapper.clone()
for pos, cpp_class in zip(implicit_conversion_positions, combination):
## now make this parameter handle a different C++ class
param = wrapper_clone.parameters[pos]
ref = (isinstance(param, CppClassRefParameter) and ' &' or '')
const = (param.is_const and 'const ' or '')
param.cpp_class = cpp_class
param.ctype = '%s%s%s' % (const, cpp_class.full_name, ref)
self.all_wrappers.append(wrapper_clone)
continue ### XXXX: implicit conversions are now handled
### differently; the code is embedded in
### CppClassParameter and CppClassRefParameter.
# if not self.enable_implicit_conversions:
# continue
# ## add additional wrappers to support implicit conversion
# if not hasattr(wrapper, "clone"):
# continue
# implicit_conversion_classes = []
# implicit_conversion_positions = []
# for pos, param in enumerate(wrapper.parameters):
# if not isinstance(param, (CppClassParameter, CppClassRefParameter)):
# continue
# if isinstance(param, (CppClassRefParameter)) and not param.is_const:
# continue
# conversion_sources = param.cpp_class.get_all_implicit_conversions()
# if conversion_sources:
# conversion_sources.insert(0, param.cpp_class)
# implicit_conversion_classes.append(conversion_sources)
# implicit_conversion_positions.append(pos)
# if not implicit_conversion_classes:
# continue
# ## Now generate the addition wrappers, with all possible
# ## combinations of implicit conversion source classes...
# combinations = vector_counter(implicit_conversion_classes)
# ## skip the first one, as it is the same as in the original wrapper
# combinations.next()
# for combination in combinations:
# wrapper_clone = wrapper.clone()
# for pos, cpp_class in zip(implicit_conversion_positions, combination):
# ## now make this parameter handle a different C++ class
# param = wrapper_clone.parameters[pos]
# ref = (isinstance(param, CppClassRefParameter) and ' &' or '')
# const = (param.is_const and 'const ' or '')
# param.cpp_class = cpp_class
# param.ctype = '%s%s%s' % (const, cpp_class.full_name, ref)
# self.all_wrappers.append(wrapper_clone)
def generate(self, code_sink):
+8 -2
View File
@@ -203,8 +203,7 @@ class CodeBlock(object):
self.sink.indent()
if failure_cleanup is not None:
self.sink.writeln(failure_cleanup)
self.write_cleanup()
self.sink.writeln(self.error_return)
self.write_error_return()
self.sink.unindent()
self.sink.writeln("}")
@@ -213,6 +212,13 @@ class CodeBlock(object):
for cleanup_action in self.get_cleanup_code():
self.sink.writeln(cleanup_action)
def write_error_return(self):
'''Add a chunk of code that cleans up and returns an error.
'''
self.write_cleanup()
self.sink.writeln(self.error_return)
class ParseTupleParameters(object):
"Object to keep track of PyArg_ParseTuple (or similar) parameters"