diff options
-rw-r--r-- | libshiboken/conversions.h | 45 | ||||
-rw-r--r-- | tests/libsample/functions.cpp | 29 | ||||
-rw-r--r-- | tests/libsample/functions.h | 6 | ||||
-rwxr-xr-x | tests/samplebinding/implicitconv_numerical_test.py | 134 | ||||
-rw-r--r-- | tests/samplebinding/typesystem_sample.xml | 1 |
5 files changed, 206 insertions, 9 deletions
diff --git a/libshiboken/conversions.h b/libshiboken/conversions.h index 301a27212..93d777fcd 100644 --- a/libshiboken/conversions.h +++ b/libshiboken/conversions.h @@ -194,6 +194,15 @@ struct Converter<bool> } }; +/** + * Helper template for checking if a value of SourceT overflows when cast to TargetT + */ +template<typename SourceT, typename TargetT> +inline bool overflowCheck(SourceT value) +{ + return value < std::numeric_limits<TargetT>::min() || value > std::numeric_limits<TargetT>::max(); +} + template <typename PyIntEquiv> struct Converter_PyInt { @@ -203,12 +212,19 @@ struct Converter_PyInt } static PyIntEquiv toCpp(PyObject* pyobj) { + double d_result; long result; - if (PyFloat_Check(pyobj)) - result = (long) PyFloat_AS_DOUBLE(pyobj); - else - result = PyInt_AS_LONG(pyobj); - if (result < std::numeric_limits<PyIntEquiv>::min() || result > std::numeric_limits<PyIntEquiv>::max()) + if (PyFloat_Check(pyobj)) { + d_result = PyFloat_AS_DOUBLE(pyobj); + // If cast to long directly it could overflow silently + if (overflowCheck<double, PyIntEquiv>(d_result)) + PyErr_SetObject(PyExc_OverflowError, 0); + return (PyIntEquiv) d_result; + } else { + result = PyLong_AsLong(pyobj); + } + + if (overflowCheck<long, PyIntEquiv>(result)) PyErr_SetObject(PyExc_OverflowError, 0); return (PyIntEquiv) result; } @@ -232,7 +248,18 @@ struct Converter<unsigned long> } static unsigned long toCpp(PyObject* pyobj) { - return PyLong_AsUnsignedLong(pyobj); + unsigned long result; + if (PyFloat_Check(pyobj)) { + // Need to check for negatives manually + double double_result = PyFloat_AS_DOUBLE(pyobj); + if (overflowCheck<double, unsigned long>(double_result)) + PyErr_SetObject(PyExc_OverflowError, 0); + result = (unsigned long) double_result; + } else { + result = PyLong_AsUnsignedLong(pyobj); + } + + return result; } }; @@ -271,9 +298,9 @@ struct Converter_PyFloat } static PyFloatEquiv toCpp(PyObject* pyobj) { - if (PyInt_Check(pyobj)) - return (PyFloatEquiv) PyInt_AS_LONG(pyobj); - return (PyFloatEquiv) PyFloat_AS_DOUBLE(pyobj); + if (PyInt_Check(pyobj) || PyLong_Check(pyobj)) + return (PyFloatEquiv) PyLong_AsLong(pyobj); + return (PyFloatEquiv) PyFloat_AsDouble(pyobj); } }; diff --git a/tests/libsample/functions.cpp b/tests/libsample/functions.cpp index 8a7416bc6..0df75ba90 100644 --- a/tests/libsample/functions.cpp +++ b/tests/libsample/functions.cpp @@ -141,3 +141,32 @@ doubleUnsignedInt(unsigned int value) return value * 2; } +int +acceptInt(int x) +{ + return x; +} + +unsigned int +acceptUInt(unsigned int x) +{ + return x; +} + +long +acceptLong(long x) +{ + return x; +} + +unsigned long +acceptULong(unsigned long x) +{ + return x; +} + +double +acceptDouble(double x) +{ + return x; +} diff --git a/tests/libsample/functions.h b/tests/libsample/functions.h index 2629d3907..52a26a7a6 100644 --- a/tests/libsample/functions.h +++ b/tests/libsample/functions.h @@ -74,5 +74,11 @@ LIBSAMPLE_API GlobalOverloadFuncEnum overloadedFunc(double val); LIBSAMPLE_API unsigned int doubleUnsignedInt(unsigned int value); +LIBSAMPLE_API int acceptInt(int x); +LIBSAMPLE_API unsigned int acceptUInt(unsigned int x); +LIBSAMPLE_API long acceptLong(long x); +LIBSAMPLE_API unsigned long acceptULong(unsigned long x); +LIBSAMPLE_API double acceptDouble(double x); + #endif // FUNCTIONS_H diff --git a/tests/samplebinding/implicitconv_numerical_test.py b/tests/samplebinding/implicitconv_numerical_test.py new file mode 100755 index 000000000..d81a882c5 --- /dev/null +++ b/tests/samplebinding/implicitconv_numerical_test.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# This file is part of the Shiboken Python Bindings Generator project. +# +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# +# Contact: PySide team <contact@pyside.org> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# version 2.1 as published by the Free Software Foundation. Please +# review the following information to ensure the GNU Lesser General +# Public License version 2.1 requirements will be met: +# http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +# # +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +'''Test case for inplicit converting C++ numeric types.''' + +import unittest +import sys +import sample + + +class NumericTester(unittest.TestCase): + '''Helper class for numeric comparison testing''' + + def check_value(self, source, expected, callback, desired_type=None): + result = callback(source) + self.assertEqual(result, expected) + + if desired_type: + self.assertEqual(type(result), desired_type) + + +class FloatImplicitConvert(NumericTester): + '''Test case for implicit converting C++ numeric types.''' + + def testFloatAsInt(self): + '''Float as Int''' + self.check_value(3.14, 3, sample.acceptInt, int) + self.assertRaises(OverflowError, sample.acceptInt, sys.maxint + 400) + + def testFloatAsLong(self): + '''Float as Long''' + #C++ longs are python ints for us + self.check_value(3.14, 3, sample.acceptLong, int) + self.assertRaises(OverflowError, sample.acceptLong, sys.maxint + 400) + + def testFloatAsUInt(self): + '''Float as unsigned Int''' + self.check_value(3.14, 3, sample.acceptUInt, int) + self.assertRaises(OverflowError, sample.acceptUInt, -3.14) + + def testFloatAsULong(self): + '''Float as unsigned Long''' + #FIXME Breaking with SystemError "bad argument to internal function" + self.check_value(3.14, 3, sample.acceptULong, long) + self.assertRaises(OverflowError, sample.acceptULong, -3.14) + + def testFloatAsDouble(self): + '''Float as double''' + self.check_value(3.14, 3.14, sample.acceptDouble, float) + + +class IntImplicitConvert(NumericTester): + '''Test case for implicit converting C++ numeric types.''' + + def testIntAsInt(self): + '''Int as Int''' + self.check_value(3, 3, sample.acceptInt, int) + + def testIntAsLong(self): + '''Int as Long''' + self.check_value(3, 3, sample.acceptLong, int) + + # sys.maxint goes here as CPython implements int as a C long + self.check_value(sys.maxint, sys.maxint, sample.acceptLong, int) + self.check_value(-sys.maxint - 1, -sys.maxint - 1, sample.acceptLong, int) + + def testIntAsUInt(self): + '''Int as unsigned Int''' + self.check_value(3, 3, sample.acceptUInt, int) + self.assertRaises(OverflowError, sample.acceptUInt, -3) + + def testIntAsULong(self): + '''Int as unsigned Long''' + self.check_value(3, 3, sample.acceptULong, long) + self.assertRaises(OverflowError, sample.acceptULong, -3) + + def testFloatAsDouble(self): + '''Float as double''' + self.check_value(3.14, 3.14, sample.acceptDouble, float) + + +class LongImplicitConvert(NumericTester): + '''Test case for implicit converting C++ numeric types.''' + + def testLongAsInt(self): + '''Long as Int''' + self.check_value(24224l, 24224, sample.acceptInt, int) + self.assertRaises(OverflowError, sample.acceptInt, sys.maxint + 20) + + def testLongAsLong(self): + '''Long as Long''' + self.check_value(2405l, 2405, sample.acceptLong, int) + self.assertRaises(OverflowError, sample.acceptLong, sys.maxint + 20) + + def testLongAsUInt(self): + '''Long as unsigned Int''' + self.check_value(260l, 260, sample.acceptUInt, int) + self.assertRaises(OverflowError, sample.acceptUInt, -42) + + def testLongAsULong(self): + '''Long as unsigned Long''' + self.check_value(128l, 128, sample.acceptULong, long) + self.assertRaises(OverflowError, sample.acceptULong, -334l) + + def testLongAsDouble(self): + '''Float as double''' + self.check_value(42l, 42, sample.acceptDouble, float) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/samplebinding/typesystem_sample.xml b/tests/samplebinding/typesystem_sample.xml index f7b963e93..9dc3d1798 100644 --- a/tests/samplebinding/typesystem_sample.xml +++ b/tests/samplebinding/typesystem_sample.xml @@ -7,6 +7,7 @@ <primitive-type name="signed int" /> <primitive-type name="char"/> <primitive-type name="long"/> + <primitive-type name="unsigned long"/> <primitive-type name="Complex" target-lang-api-name="PyComplex"> <conversion-rule file="complex_conversions.h"/> |