Wrap C++ arithmetic operators

This commit is contained in:
Gustavo J. A. M. Carneiro committed 2008-10-06 15:36:44 +01:00
1 parent b0336e7f06
commit 259bb357f1
5 files changed
+236 -48

No files matched your search

+59 -1
View File
@@ -4,7 +4,7 @@ python to C or C to python. These can be useful in certain
specialized contexts, such as converting list elements.
"""
from typehandlers.base import ReverseWrapperBase
from typehandlers.base import ReverseWrapperBase, ForwardWrapperBase
class PythonToCConverter(ReverseWrapperBase):
@@ -77,3 +77,61 @@ class PythonToCConverter(ReverseWrapperBase):
def get_prototype(self):
return "int %s(PyObject *value, %s *address)" % (self.c_function_name, self.return_value.ctype)
class CToPythonConverter(ForwardWrapperBase):
'''
Utility function that converts a C value to a PyObject*.
'''
def __init__(self, value_type, c_function_name):
"""
value_type -- a ReturnValue object handling the value type;
class_ -- the class (CppClass object)
attribute_name -- name of attribute
getter -- None, or name of a method of the class used to get the value
"""
super(CToPythonConverter, self).__init__(value_type, [], parse_error_return="return 0;", error_return="return 0;")
self.c_function_name = c_function_name
self.unblock_threads = False
def generate(self, code_sink):
save_return_value_value = self.return_value.value
self.return_value.value = "*cvalue"
try:
self.return_value.convert_c_to_python(self)
finally:
self.return_value.value = save_return_value_value
code_sink.writeln(self.get_prototype())
code_sink.writeln("{")
code_sink.indent()
params = self.build_params.get_parameters()
if params:
if params == ['""']:
self.after_call.write_code('Py_INCREF(Py_None);')
self.after_call.write_code('py_retval = Py_None;')
else:
assert params[0][0] == '"'
params[0] = "(char *) " + params[0]
self.after_call.write_code('py_retval = Py_BuildValue(%s);' %
(', '.join(params),))
## cleanup and return
self.after_call.write_cleanup()
self.after_call.write_code('return py_retval;')
self.declarations.get_code_sink().flush_to(code_sink)
code_sink.writeln()
self.before_parse.sink.flush_to(code_sink)
self.before_call.sink.flush_to(code_sink)
self.after_call.sink.flush_to(code_sink)
code_sink.unindent()
code_sink.writeln("}")
def get_prototype(self):
return "PyObject* %s(%s *cvalue)" % (self.c_function_name, self.return_value.ctype)
+70 -4
View File
@@ -451,7 +451,6 @@ class CppClass(object):
self._wrapper_registry = None
self.binary_comparison_operators = set()
self.binary_numeric_operators = dict()
self.pynumbermethods = None
## list of CppClasses from which a value of this class can be
## implicitly generated; corresponds to a
@@ -590,7 +589,7 @@ class CppClass(object):
if not isinstance(operator, str):
raise TypeError("expected operator name as string")
if operator not in ['==', '!=', '<', '<=', '>', '>=']:
raise NotImplementedError("The operator %r is invalid or not yet supported by PyBindGen" % (operator,))
raise ValueError("The operator %r is invalid or not yet supported by PyBindGen" % (operator,))
self.binary_comparison_operators.add(operator)
def add_binary_numeric_operator(self, operator, result_cppclass=None,
@@ -608,7 +607,7 @@ class CppClass(object):
if not isinstance(operator, str):
raise TypeError("expected operator name as string")
if operator not in ['+', '-', '*', '/']:
raise NotImplementedError("The operator %r is invalid or not yet supported by PyBindGen" % (operator,))
raise ValueError("The operator %r is invalid or not yet supported by PyBindGen" % (operator,))
try:
l = self.binary_numeric_operators[operator]
except KeyError:
@@ -620,7 +619,9 @@ class CppClass(object):
left_cppclass = self
if right_cppclass is None:
right_cppclass = self
l.append((result_cppclass, left_cppclass, right_cppclass))
op = (result_cppclass, left_cppclass, right_cppclass)
if op not in l:
l.append(op)
def add_class(self, *args, **kwargs):
"""
@@ -1489,8 +1490,73 @@ typedef struct {
if self.slots.get("tp_richcompare", "NULL") == "NULL":
self.slots["tp_richcompare"] = self._generate_tp_richcompare(code_sink)
if self.binary_numeric_operators:
self.slots["tp_as_number"] = self._generate_number_methods(code_sink)
self._generate_type_structure(code_sink, docstring)
def _generate_number_methods(self, code_sink):
number_methods_var_name = "%s__py_number_methods" % (self.mangled_full_name,)
pynumbermethods = PyNumberMethods()
pynumbermethods.slots['variable'] = number_methods_var_name
# iterate over all types and request generation of the
# convertion functions for that type (so that those functions
# are not generated in the middle of one of the wrappers we
# are about to generate)
root_module = self.module.get_root()
for dummy_op_symbol, op_types in self.binary_numeric_operators.iteritems():
for (retval, left, right) in op_types:
root_module.generate_c_to_python_type_converter(retval.ThisClassReturn(retval.full_name), code_sink)
root_module.generate_python_to_c_type_converter(left.ThisClassReturn(left.full_name), code_sink)
root_module.generate_python_to_c_type_converter(right.ThisClassReturn(right.full_name), code_sink)
def try_wrap_operator(op_symbol, slot_name):
try:
op_types = self.binary_numeric_operators[op_symbol]
except KeyError:
return
wrapper_name = "%s__%s" % (self.mangled_full_name, slot_name)
pynumbermethods.slots[slot_name] = wrapper_name
code_sink.writeln(("static PyObject*\n"
"%s (PyObject *py_left, PyObject *py_right)\n"
"{") % wrapper_name)
code_sink.indent()
for (retval, left, right) in op_types:
retval_converter = root_module.generate_c_to_python_type_converter(retval.ThisClassReturn(retval.full_name), code_sink)
left_converter = root_module.generate_python_to_c_type_converter(left.ThisClassReturn(left.full_name), code_sink)
right_converter = root_module.generate_python_to_c_type_converter(right.ThisClassReturn(right.full_name), code_sink)
code_sink.writeln("{")
code_sink.indent()
code_sink.writeln("%s left;" % left.full_name)
code_sink.writeln("%s right;" % right.full_name)
code_sink.writeln("if (%s(py_left, &left) && %s(py_right, &right)) {" % (left_converter, right_converter))
code_sink.indent()
code_sink.writeln("%s result = (left %s right);" % (retval.full_name, op_symbol))
code_sink.writeln("return %s(&result);" % retval_converter)
code_sink.unindent()
code_sink.writeln("}")
code_sink.unindent()
code_sink.writeln("}")
code_sink.writeln("Py_INCREF(Py_NotImplemented);")
code_sink.writeln("return Py_NotImplemented;")
code_sink.unindent()
code_sink.writeln("}")
try_wrap_operator('+', 'nb_add')
try_wrap_operator('-', 'nb_subtract')
try_wrap_operator('*', 'nb_multiply')
try_wrap_operator('/', 'nb_divide')
pynumbermethods.generate(code_sink)
return '&' + number_methods_var_name
def _generate_type_structure(self, code_sink, docstring):
"""generate the type structure"""
+40 -1
View File
@@ -51,7 +51,7 @@ from typehandlers.codesink import MemoryCodeSink, CodeSink, FileCodeSink, NullCo
from cppclass import CppClass
from enum import Enum
from container import Container
from converter_functions import PythonToCConverter
from converter_functions import PythonToCConverter, CToPythonConverter
import utils
import warnings
import traceback
@@ -878,6 +878,45 @@ class Module(ModuleBase):
return converter_function_name
def get_c_to_python_type_converter_function_name(self, value_type):
"""
Internal API, do not use.
"""
assert isinstance(value_type, ReturnValue)
ctype = value_type.ctype
mangled_ctype = utils.mangle_name(ctype)
converter_function_name = "_wrap_convert_c2py__%s" % mangled_ctype
return converter_function_name
def generate_c_to_python_type_converter(self, value_type, code_sink):
"""
Generates a c-to-python converter function for a given type
and returns the name of the generated function. If called
multiple times with the same name only the first time is the
converter function generated.
Use: this method is to be considered pybindgen internal, used
by code generation modules.
@type value_type: L{ReturnValue}
@type code_sink: L{CodeSink}
@returns: name of the converter function
"""
assert isinstance(value_type, ReturnValue)
converter_function_name = self.get_c_to_python_type_converter_function_name(value_type)
try:
self.declare_one_time_definition(converter_function_name)
except KeyError:
return converter_function_name
else:
converter = CToPythonConverter(value_type, converter_function_name)
self.header.writeln("\n%s;\n" % converter.get_prototype())
code_sink.writeln()
converter.generate(code_sink)
code_sink.writeln()
return converter_function_name
class SubModule(ModuleBase):
def __init__(self, name, parent, docstring=None, cpp_namespace=None):
"""
+40 -40
View File
@@ -105,52 +105,52 @@ class PyTypeObject(object):
class PyNumberMethods(object):
TEMPLATE = (
'PyNumberMethods %(variable)s = {\n'
'(binaryfunc) %(nb_add)s;\n'
'(binaryfunc) %(nb_subtract)s;\n'
'(binaryfunc) %(nb_multiply)s;\n'
'(binaryfunc) %(nb_divide)s;\n'
'(binaryfunc) %(nb_remainder)s;\n'
'(binaryfunc) %(nb_divmod)s;\n'
'(ternaryfunc) %(nb_power)s;\n'
'(unaryfunc) %(nb_negative)s;\n'
'(unaryfunc) %(nb_positive)s;\n'
'(unaryfunc) %(nb_absolute)s;\n'
'(inquiry) %(nb_nonzero)s;\n'
'(unaryfunc) %(nb_invert)s;\n'
'(binaryfunc) %(nb_lshift)s;\n'
'(binaryfunc) %(nb_rshift)s;\n'
'(binaryfunc) %(nb_and)s;\n'
'(binaryfunc) %(nb_xor)s;\n'
'(binaryfunc) %(nb_or)s;\n'
'(coercion) %(nb_coerce)s;\n'
'(unaryfunc) %(nb_int)s;\n'
'(unaryfunc) %(nb_long)s;\n'
'(unaryfunc) %(nb_float)s;\n'
'(unaryfunc) %(nb_oct)s;\n'
'(unaryfunc) %(nb_hex)s;\n'
'static PyNumberMethods %(variable)s = {\n'
'(binaryfunc) %(nb_add)s,\n'
'(binaryfunc) %(nb_subtract)s,\n'
'(binaryfunc) %(nb_multiply)s,\n'
'(binaryfunc) %(nb_divide)s,\n'
'(binaryfunc) %(nb_remainder)s,\n'
'(binaryfunc) %(nb_divmod)s,\n'
'(ternaryfunc) %(nb_power)s,\n'
'(unaryfunc) %(nb_negative)s,\n'
'(unaryfunc) %(nb_positive)s,\n'
'(unaryfunc) %(nb_absolute)s,\n'
'(inquiry) %(nb_nonzero)s,\n'
'(unaryfunc) %(nb_invert)s,\n'
'(binaryfunc) %(nb_lshift)s,\n'
'(binaryfunc) %(nb_rshift)s,\n'
'(binaryfunc) %(nb_and)s,\n'
'(binaryfunc) %(nb_xor)s,\n'
'(binaryfunc) %(nb_or)s,\n'
'(coercion) %(nb_coerce)s,\n'
'(unaryfunc) %(nb_int)s,\n'
'(unaryfunc) %(nb_long)s,\n'
'(unaryfunc) %(nb_float)s,\n'
'(unaryfunc) %(nb_oct)s,\n'
'(unaryfunc) %(nb_hex)s,\n'
'/* Added in release 2.0 */\n'
'(binaryfunc) %(nb_inplace_add)s;\n'
'(binaryfunc) %(nb_inplace_subtract)s;\n'
'(binaryfunc) %(nb_inplace_multiply)s;\n'
'(binaryfunc) %(nb_inplace_divide)s;\n'
'(binaryfunc) %(nb_inplace_remainder)s;\n'
'(ternaryfunc) %(nb_inplace_power)s;\n'
'(binaryfunc) %(nb_inplace_lshift)s;\n'
'(binaryfunc) %(nb_inplace_rshift)s;\n'
'(binaryfunc) %(nb_inplace_and)s;\n'
'(binaryfunc) %(nb_inplace_xor)s;\n'
'(binaryfunc) %(nb_inplace_or)s;\n'
'(binaryfunc) %(nb_inplace_add)s,\n'
'(binaryfunc) %(nb_inplace_subtract)s,\n'
'(binaryfunc) %(nb_inplace_multiply)s,\n'
'(binaryfunc) %(nb_inplace_divide)s,\n'
'(binaryfunc) %(nb_inplace_remainder)s,\n'
'(ternaryfunc) %(nb_inplace_power)s,\n'
'(binaryfunc) %(nb_inplace_lshift)s,\n'
'(binaryfunc) %(nb_inplace_rshift)s,\n'
'(binaryfunc) %(nb_inplace_and)s,\n'
'(binaryfunc) %(nb_inplace_xor)s,\n'
'(binaryfunc) %(nb_inplace_or)s,\n'
'\n'
'/* Added in release 2.2 */\n'
'/* The following require the Py_TPFLAGS_HAVE_CLASS flag */\n'
'(binaryfunc) %(nb_floor_divide)s;\n'
'(binaryfunc) %(nb_true_divide)s;\n'
'(binaryfunc) %(nb_inplace_floor_divide);\n'
'(binaryfunc) %(nb_inplace_true_divide);\n'
'(binaryfunc) %(nb_floor_divide)s,\n'
'(binaryfunc) %(nb_true_divide)s,\n'
'(binaryfunc) %(nb_inplace_floor_divide)s,\n'
'(binaryfunc) %(nb_inplace_true_divide)s,\n'
'\n'
'/* Added in release 2.5 */\n'
'(unaryfunc) %(nb_index)s;\n'
'(unaryfunc) %(nb_index)s,\n'
'\n'
'};\n'
)
+27 -2
View File
@@ -1020,7 +1020,32 @@ class TestFoo(unittest.TestCase):
self.assert_(t2 != t3)
self.assert_(not(t2 == t3))
def test_numeric_operators(self):
t1 = foo.Tupl()
t1.x = 4
t1.y = 6
t2 = foo.Tupl()
t2.x = 2
t2.y = 3
r = t1 + t2
self.assertEqual(r.x, t1.x + t2.x)
self.assertEqual(r.y, t1.y + t2.y)
r = t1 - t2
self.assertEqual(r.x, t1.x - t2.x)
self.assertEqual(r.y, t1.y - t2.y)
r = t1 * t2
self.assertEqual(r.x, t1.x * t2.x)
self.assertEqual(r.y, t1.y * t2.y)
r = t1 / t2
self.assertEqual(r.x, t1.x / t2.x)
self.assertEqual(r.y, t1.y / t2.y)
if __name__ == '__main__':
unittest.main()