Re-implement implicit conversions using a non-combinatorial approach, for greater efficiency.
This commit is contained in:
1 parent
f9baba1bc9
commit
944cef3e14
3 files changed
+122
-48
No files matched your search
+75
-11
@@ -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
@@ -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):
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user