// Analysis of _Py_CAST() to debug: // https://github.com/python/cpython/issues/92898 // // g++ -Wall -Wextra -Wconversion -Wold-style-cast py_cast.cpp -o py_cast #include struct PyObject { int x; }; struct PyLongObject { PyObject ob_base; int size; }; class StrongRef { public: StrongRef(PyObject *obj) : m_obj(obj) { //Py_INCREF(this->m_obj); } ~StrongRef() { //Py_DECREF(this->m_obj); } // Cast to PyObject*: get a borrowed reference inline operator PyObject*() const { return this->m_obj; } private: PyObject *m_obj; // Strong reference }; #if 0 // Old-style C cast: magic, it just works! # define _Py_CAST(type, expr) ((type)expr) #elif 0 // static_cast<>: // * PyObject* => PyObject*: ok // * const PyObject* => PyObject*: ERROR // * PyLongObject* => PyObject*: ERROR // * StrongRef => PyObject*: ok # define _Py_CAST(type, expr) static_cast(expr) #elif 0 // reinterpret_cast<> + const_cast<> // * PyObject* => PyObject*: ok // * const PyObject* => PyObject*: ok // * PyLongObject* => PyObject*: ok // * StrongRef => PyObject*: ERROR # define _Py_CAST(type, expr) const_cast(reinterpret_cast(expr)) #else // templates: // // PyObject* => PyObject*: ok // &PyObject => PyObject*: ok // PyLongObject* => PyObject*: ok // &PyLongObject => PyObject*: ok // const PyObject* => PyObject*: ok // &const PyObject => PyObject*: ok // StrongRef => PyObject*: ok // // https://github.com/python/cpython/pull/92138#issuecomment-1116983125 template type _Py_reinterpret_cast_impl(expr_type* expr) { return reinterpret_cast(expr);} template type _Py_reinterpret_cast_impl(expr_type const * expr) { return reinterpret_cast(const_cast(expr));} template type _Py_reinterpret_cast_impl(expr_type & expr) { return static_cast(expr);} template type _Py_reinterpret_cast_impl(expr_type const & expr) { return static_cast(const_cast(expr));} # define _Py_CAST(type, expr) _Py_reinterpret_cast_impl(expr) #endif int main() { PyLongObject long_obj = {.ob_base = { .x = 1 }, .size = 2}; PyObject obj = { .x = 3 }; PyObject *obj_ptr = &obj; // PyObject* => PyObject* PyObject *cast1 = _Py_CAST(PyObject*, obj_ptr); assert(cast1 != nullptr); // &PyObject => PyObject* PyObject *cast1b = _Py_CAST(PyObject*, &obj); assert(cast1b != nullptr); // PyLongObject* => PyObject* PyLongObject *obj3 = &long_obj; PyObject *cast3 = _Py_CAST(PyObject*, obj3); assert(cast3 != nullptr); // &PyLongObject => PyObject* PyObject *cast3b = _Py_CAST(PyObject*, &long_obj); assert(cast3b != nullptr); // const PyObject* => PyObject* const PyObject *const_obj_ptr = obj_ptr; PyObject *cast2 = _Py_CAST(PyObject*, const_obj_ptr); assert(cast2 != nullptr); // &const PyObject => PyObject* const PyObject const_obj = obj; PyObject *cast2b = _Py_CAST(PyObject*, &const_obj); assert(cast2b != nullptr); // StrongRef => PyObject* StrongRef strong_ref(obj_ptr); PyObject *cast4 = _Py_CAST(PyObject*, strong_ref); assert(cast4 != nullptr); return 0; }