diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 715ec932d..b3ab7f236 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -980,11 +980,12 @@ public: static handle cast(T src, return_value_policy /* policy */, handle /* parent */) { if (std::is_floating_point::value) { return PyFloat_FromDouble((double) src); - } else if (sizeof(T) <= sizeof(long)) { + } else if (sizeof(T) <= sizeof(ssize_t)) { + // This returns a long automatically if needed if (std::is_signed::value) - return PyLong_FromLong((long) src); + return PYBIND11_LONG_FROM_SIGNED(src); else - return PyLong_FromUnsignedLong((unsigned long) src); + return PYBIND11_LONG_FROM_UNSIGNED(src); } else { if (std::is_signed::value) return PyLong_FromLongLong((long long) src); diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 7692063ab..7d629c0ff 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -158,6 +158,8 @@ #define PYBIND11_BYTES_SIZE PyBytes_Size #define PYBIND11_LONG_CHECK(o) PyLong_Check(o) #define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o) +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o) #define PYBIND11_BYTES_NAME "bytes" #define PYBIND11_STRING_NAME "str" #define PYBIND11_SLICE_OBJECT PyObject @@ -180,6 +182,8 @@ #define PYBIND11_BYTES_SIZE PyString_Size #define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) #define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) +#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. #define PYBIND11_BYTES_NAME "str" #define PYBIND11_STRING_NAME "unicode" #define PYBIND11_SLICE_OBJECT PySliceObject diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp index e5413c2cc..450813403 100644 --- a/tests/test_builtin_casters.cpp +++ b/tests/test_builtin_casters.cpp @@ -155,4 +155,9 @@ TEST_SUBMODULE(builtin_casters, m) { // test_complex m.def("complex_cast", [](float x) { return "{}"_s.format(x); }); m.def("complex_cast", [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }); + + // test int vs. long (Python 2) + m.def("int_cast", []() {return (int) 42;}); + m.def("long_cast", []() {return (long) 42;}); + m.def("longlong_cast", []() {return ULLONG_MAX;}); } diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 2f311f152..01d0437b5 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -323,3 +323,16 @@ def test_numpy_bool(): assert convert(np.bool_(False)) is False assert noconvert(np.bool_(True)) is True assert noconvert(np.bool_(False)) is False + + +def test_int_long(): + """In Python 2, a C++ int should return a Python int rather than long + if possible: longs are not always accepted where ints are used (such + as the argument to sys.exit()). A C++ long long is always a Python + long.""" + + import sys + must_be_long = type(getattr(sys, 'maxint', 1) + 1) + assert isinstance(m.int_cast(), int) + assert isinstance(m.long_cast(), int) + assert isinstance(m.longlong_cast(), must_be_long)