Wrap C++ arithmetic operators
This commit is contained in:
1 parent
b0336e7f06
commit
259bb357f1
5 files changed
+236
-48
No files matched your search
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
Reference in new issue
Block a user