From 944cef3e148013bc6bfbb4d7445b30fa7e92a9c6 Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Fri, 21 Mar 2008 18:10:42 +0000 Subject: [PATCH] Re-implement implicit conversions using a non-combinatorial approach, for greater efficiency. --- pybindgen/cppclass.py | 86 +++++++++++++++++++++++++++++----- pybindgen/overloading.py | 74 +++++++++++++++-------------- pybindgen/typehandlers/base.py | 10 +++- 3 files changed, 122 insertions(+), 48 deletions(-) diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 9891c0f..b62411b 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -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 diff --git a/pybindgen/overloading.py b/pybindgen/overloading.py index b1a08a4..b34c621 100644 --- a/pybindgen/overloading.py +++ b/pybindgen/overloading.py @@ -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): diff --git a/pybindgen/typehandlers/base.py b/pybindgen/typehandlers/base.py index cb6b83b..4625502 100644 --- a/pybindgen/typehandlers/base.py +++ b/pybindgen/typehandlers/base.py @@ -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"