From e15fa9f99a8bc6597ebadeb410c5fc7383478d16 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Wed, 8 Feb 2017 17:43:08 -0500 Subject: [PATCH] Avoid C-style const casts (#659) * Avoid C-style const casts Replace C-style casts that discard `const` with `const_cast` (and, where necessary, `reinterpret_cast` as well). * Warn about C-style const-discarding casts Change pybind11_enable_warnings to also enable `-Wcast-qual` (warn if a C-style cast discards `const`) by default. The previous commit should have gotten rid of all of these (at least, all the ones that tripped in my build, which included the tests), and this should discourage more from newly appearing. --- CMakeLists.txt | 2 +- include/pybind11/cast.h | 6 +++--- include/pybind11/numpy.h | 8 +++++--- include/pybind11/pybind11.h | 17 +++++++++-------- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 341f845e..ec104b0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ function(pybind11_enable_warnings target_name) if(MSVC) target_compile_options(${target_name} PRIVATE /W4) else() - target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion) + target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual) endif() if(PYBIND11_WERROR) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 67ab2169..44c961c7 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -422,7 +422,7 @@ protected: template ::value>> static auto make_copy_constructor(const T *value) -> decltype(new T(*value), Constructor(nullptr)) { return [](const void *arg) -> void * { return new T(*((const T *) arg)); }; } template static auto make_move_constructor(const T *value) -> decltype(new T(std::move(*((T *) value))), Constructor(nullptr)) { - return [](const void *arg) -> void * { return (void *) new T(std::move(*((T *) arg))); }; } + return [](const void *arg) -> void * { return (void *) new T(std::move(*const_cast(reinterpret_cast(arg)))); }; } #else /* Visual Studio 2015's SFINAE implementation doesn't yet handle the above robustly in all situations. Use a workaround that only tests for constructibility for now. */ @@ -706,7 +706,7 @@ public: return PyUnicode_DecodeLatin1(str, 1, nullptr); } - operator char*() { return success ? (char *) value.c_str() : nullptr; } + operator char*() { return success ? const_cast(value.c_str()) : nullptr; } operator char&() { return value[0]; } static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); } @@ -729,7 +729,7 @@ public: return PyUnicode_FromWideChar(wstr, 1); } - operator wchar_t*() { return success ? (wchar_t *) value.c_str() : nullptr; } + operator wchar_t*() { return success ? const_cast(value.c_str()) : nullptr; } operator wchar_t&() { return value[0]; } static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); } diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 19facb0f..50eb682d 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -357,8 +357,10 @@ public: } auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( - api.PyArray_Type_, descr.release().ptr(), (int) ndim, (Py_intptr_t *) shape.data(), - (Py_intptr_t *) strides.data(), const_cast(ptr), flags, nullptr)); + api.PyArray_Type_, descr.release().ptr(), (int) ndim, + reinterpret_cast(const_cast(shape.data())), + reinterpret_cast(const_cast(strides.data())), + const_cast(ptr), flags, nullptr)); if (!tmp) pybind11_fail("NumPy: unable to create array!"); if (ptr) { @@ -382,7 +384,7 @@ public: template array(const std::vector& shape, const std::vector& strides, const T* ptr, handle base = handle()) - : array(pybind11::dtype::of(), shape, strides, (void *) ptr, base) { } + : array(pybind11::dtype::of(), shape, strides, (const void *) ptr, base) { } template array(const std::vector &shape, const T *ptr, diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 006a4ff6..f022c0b4 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -129,8 +129,9 @@ protected: detail::process_attributes::precall(call); /* Get a pointer to the capture object */ - capture *cap = (capture *) (sizeof(capture) <= sizeof(call.func.data) - ? &call.func.data : call.func.data[0]); + auto data = (sizeof(capture) <= sizeof(call.func.data) + ? &call.func.data : call.func.data[0]); + capture *cap = const_cast(reinterpret_cast(data)); /* Override policy for rvalues -- always move */ constexpr auto is_rvalue = !std::is_pointer::value @@ -167,7 +168,7 @@ protected: sizeof(capture) == sizeof(void *); if (is_function_ptr) { rec->is_stateless = true; - rec->data[1] = (void *) &typeid(FunctionType); + rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); } } @@ -345,7 +346,7 @@ protected: /* Install docstring */ PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; if (func->m_ml->ml_doc) - std::free((char *) func->m_ml->ml_doc); + std::free(const_cast(func->m_ml->ml_doc)); func->m_ml->ml_doc = strdup(signatures.c_str()); if (rec->is_method) { @@ -366,12 +367,12 @@ protected: std::free((char *) rec->doc); std::free((char *) rec->signature); for (auto &arg: rec->args) { - std::free((char *) arg.name); - std::free((char *) arg.descr); + std::free(const_cast(arg.name)); + std::free(const_cast(arg.descr)); arg.value.dec_ref(); } if (rec->def) { - std::free((char *) rec->def->ml_doc); + std::free(const_cast(rec->def->ml_doc)); delete rec->def; } delete rec; @@ -1666,7 +1667,7 @@ public: exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { std::string full_name = scope.attr("__name__").cast() + std::string(".") + name; - m_ptr = PyErr_NewException((char *) full_name.c_str(), base, NULL); + m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); if (hasattr(scope, name)) pybind11_fail("Error during initialization: multiple incompatible " "definitions with name \"" + std::string(name) + "\"");