From 221fb1e11e79b50308ec5afc55ea1337b828fa77 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 17 Jul 2018 15:48:51 +0200 Subject: [PATCH] Untangle cast logic to not implicitly require castability (#1442) The current code requires implicitly that integral types are cast-able to floating point. In case of strongly-typed integrals (e.g. as explained at http://www.ilikebigbits.com/blog/2014/5/6/type-safe-identifiers-in-c) this is not always the case. This commit uses SFINAE to move the numeric conversions into separate `cast()` implementations to avoid the issue. --- include/pybind11/cast.h | 44 +++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index ce4dcd38..2d33d81b 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -17,6 +17,7 @@ #include #include #include +#include #if defined(PYBIND11_CPP17) # if defined(__has_include) @@ -1009,21 +1010,34 @@ public: return true; } - 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(ssize_t)) { - // This returns a long automatically if needed - if (std::is_signed::value) - return PYBIND11_LONG_FROM_SIGNED(src); - else - return PYBIND11_LONG_FROM_UNSIGNED(src); - } else { - if (std::is_signed::value) - return PyLong_FromLongLong((long long) src); - else - return PyLong_FromUnsignedLongLong((unsigned long long) src); - } + template + static typename std::enable_if::value, handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyFloat_FromDouble((double) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_SIGNED((long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLongLong((long long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromUnsignedLongLong((unsigned long long) src); } PYBIND11_TYPE_CASTER(T, _::value>("int", "float"));