From abd22f12734589a225244018eec8087e1bd66e05 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sat, 14 Dec 2002 00:10:52 +0000 Subject: [PATCH] Handle unsigned long values that don't fit in a long. [SVN r16606] --- .../python/converter/builtin_converters.hpp | 10 ++- src/converter/builtin_converters.cpp | 63 ++++++++++++++----- test/test_builtin_converters.py | 7 +++ 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/include/boost/python/converter/builtin_converters.hpp b/include/boost/python/converter/builtin_converters.hpp index c46e7e8f..5ec25056 100644 --- a/include/boost/python/converter/builtin_converters.hpp +++ b/include/boost/python/converter/builtin_converters.hpp @@ -10,6 +10,7 @@ # include # include # include +# include // Since all we can use to decide how to convert an object to_python // is its C++ type, there can be only one such converter for each @@ -80,9 +81,12 @@ namespace detail BOOST_PYTHON_ARG_TO_PYTHON_BY_VALUE(T,expr) // Specialize converters for signed and unsigned T to Python Int -# define BOOST_PYTHON_TO_INT(T) \ - BOOST_PYTHON_TO_PYTHON_BY_VALUE(signed T, PyInt_FromLong(x)) \ - BOOST_PYTHON_TO_PYTHON_BY_VALUE(unsigned T, PyInt_FromLong(x)) +# define BOOST_PYTHON_TO_INT(T) \ + BOOST_PYTHON_TO_PYTHON_BY_VALUE(signed T, PyInt_FromLong(x)) \ + BOOST_PYTHON_TO_PYTHON_BY_VALUE( \ + unsigned T, static_cast(x) > std::numeric_limits::max() \ + ? PyLong_FromUnsignedLong(x) \ + : PyInt_FromLong(x)) // Bool is not signed. BOOST_PYTHON_TO_PYTHON_BY_VALUE(bool, PyInt_FromLong(x)) diff --git a/src/converter/builtin_converters.cpp b/src/converter/builtin_converters.cpp index ce842b12..5c08c945 100644 --- a/src/converter/builtin_converters.cpp +++ b/src/converter/builtin_converters.cpp @@ -65,8 +65,8 @@ namespace } }; - // A SlotPolicy for extracting integer types from Python objects - struct int_rvalue_from_python_base + // A SlotPolicy for extracting signed integer types from Python objects + struct signed_int_rvalue_from_python_base { static unaryfunc* get_slot(PyObject* obj) { @@ -80,7 +80,7 @@ namespace }; template - struct int_rvalue_from_python : int_rvalue_from_python_base + struct signed_int_rvalue_from_python : signed_int_rvalue_from_python_base { static T extract(PyObject* intermediate) { @@ -88,6 +88,41 @@ namespace } }; + // identity_unaryfunc/py_object_identity -- manufacture a unaryfunc + // "slot" which just returns its argument. + extern "C" PyObject* identity_unaryfunc(PyObject* x) + { + Py_INCREF(x); + return x; + } + unaryfunc py_object_identity = identity_unaryfunc; + + // A SlotPolicy for extracting unsigned integer types from Python objects + struct unsigned_int_rvalue_from_python_base + { + static unaryfunc* get_slot(PyObject* obj) + { + PyNumberMethods* number_methods = obj->ob_type->tp_as_number; + if (number_methods == 0) + return 0; + + return (PyInt_Check(obj) || PyLong_Check(obj)) + ? &py_object_identity : 0; + } + }; + + template + struct unsigned_int_rvalue_from_python : unsigned_int_rvalue_from_python_base + { + static T extract(PyObject* intermediate) + { + return numeric_cast( + PyLong_Check(intermediate) + ? PyLong_AsUnsignedLong(intermediate) + : PyInt_AS_LONG(intermediate)); + } + }; + // Checking Python's macro instead of Boost's - we don't seem to get // the config right all the time. Furthermore, Python's is defined // when long long is absent but __int64 is present. @@ -155,17 +190,6 @@ namespace }; #endif - // identity_unaryfunc/py_object_identity -- manufacture a unaryfunc - // "slot" which just returns its argument. Used for bool - // conversions, since all Python objects are directly convertible to - // bool - extern "C" PyObject* identity_unaryfunc(PyObject* x) - { - Py_INCREF(x); - return x; - } - unaryfunc py_object_identity = identity_unaryfunc; - // A SlotPolicy for extracting bool from a Python object struct bool_rvalue_from_python { @@ -282,8 +306,15 @@ BOOST_PYTHON_DECL PyObject* do_arg_to_python(PyObject* x) return x; } -#define REGISTER_INT_CONVERTERS(U) slot_rvalue_from_python >() -#define REGISTER_INT_CONVERTERS2(U) REGISTER_INT_CONVERTERS(signed U); REGISTER_INT_CONVERTERS(unsigned U) +#define REGISTER_INT_CONVERTERS(signedness, U) \ + slot_rvalue_from_python< \ + signedness U \ + ,signedness##_int_rvalue_from_python \ + >() + +#define REGISTER_INT_CONVERTERS2(U) \ + REGISTER_INT_CONVERTERS(signed, U); \ + REGISTER_INT_CONVERTERS(unsigned, U) void initialize_builtin_converters() { diff --git a/test/test_builtin_converters.py b/test/test_builtin_converters.py index e4d8b335..80e2149d 100644 --- a/test/test_builtin_converters.py +++ b/test/test_builtin_converters.py @@ -38,6 +38,13 @@ r""" 42 >>> rewrap_value_unsigned_long(42) 42 + + test unsigned long values which don't fit in a signed long. + strip any 'L' characters in case the platform has > 32 bit longs + +>>> hex(rewrap_value_unsigned_long(0x80000001L)).replace('L','') +'0x80000001' + >>> rewrap_value_long_long(42) 42L >>> rewrap_value_unsigned_long_long(42)