Skip to content

Commit

Permalink
gh-92898: Enhance _testcppext test on cast to PyObject* (GH-93111)
Browse files Browse the repository at this point in the history
* Add StrongRef class.
* Rename and reformat functions of the _Py_CAST() implementation.
(cherry picked from commit 20d30ba)

Co-authored-by: Victor Stinner <vstinner@python.org>
  • Loading branch information
miss-islington and vstinner authored May 26, 2022
1 parent 33336e4 commit 9303a5a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 36 deletions.
48 changes: 23 additions & 25 deletions Include/pyport.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,33 @@
// _Py_CAST(PyObject*, op) can convert a "const PyObject*" to
// "PyObject*".
//
// The type argument must not be constant. For example, in C++,
// _Py_CAST(const PyObject*, expr) fails with a compiler error.
// The type argument must not be a constant type.
#ifdef __cplusplus
# define _Py_STATIC_CAST(type, expr) static_cast<type>(expr)

extern "C++" {
namespace {
template <typename type, typename expr_type>
inline type _Py_reinterpret_cast_impl(expr_type *expr) {
return reinterpret_cast<type>(expr);
}

template <typename type, typename expr_type>
inline type _Py_reinterpret_cast_impl(expr_type const *expr) {
return reinterpret_cast<type>(const_cast<expr_type *>(expr));
}

template <typename type, typename expr_type>
inline type _Py_reinterpret_cast_impl(expr_type &expr) {
return static_cast<type>(expr);
}

template <typename type, typename expr_type>
inline type _Py_reinterpret_cast_impl(expr_type const &expr) {
return static_cast<type>(const_cast<expr_type &>(expr));
}
} // namespace
namespace {
template <typename type, typename expr_type>
inline type _Py_CAST_impl(expr_type *expr) {
return reinterpret_cast<type>(expr);
}

template <typename type, typename expr_type>
inline type _Py_CAST_impl(expr_type const *expr) {
return reinterpret_cast<type>(const_cast<expr_type *>(expr));
}

template <typename type, typename expr_type>
inline type _Py_CAST_impl(expr_type &expr) {
return static_cast<type>(expr);
}

template <typename type, typename expr_type>
inline type _Py_CAST_impl(expr_type const &expr) {
return static_cast<type>(const_cast<expr_type &>(expr));
}
}
}
# define _Py_CAST(type, expr) _Py_reinterpret_cast_impl<type>(expr)
# define _Py_CAST(type, expr) _Py_CAST_impl<type>(expr)

#else
# define _Py_STATIC_CAST(type, expr) ((type)(expr))
Expand Down
42 changes: 31 additions & 11 deletions Lib/test/_testcppext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,35 @@ _testcppext_add(PyObject *Py_UNUSED(module), PyObject *args)
}


// Class to test operator casting an object to PyObject*
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
};


static PyObject *
test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
{
PyObject *obj = Py_BuildValue("(ii)", 1, 2);
if (obj == nullptr) {
return nullptr;
}
Py_ssize_t refcnt = Py_REFCNT(obj);
assert(refcnt >= 1);

// gh-92138: For backward compatibility, functions of Python C API accepts
// "const PyObject*". Check that using it does not emit C++ compiler
Expand All @@ -38,22 +60,20 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
Py_INCREF(const_obj);
Py_DECREF(const_obj);
PyTypeObject *type = Py_TYPE(const_obj);
assert(Py_REFCNT(const_obj) >= 1);

struct PyObjectProxy {
PyObject* obj;
operator PyObject *() { return obj; }
} proxy_obj = { obj };
Py_INCREF(proxy_obj);
Py_DECREF(proxy_obj);
assert(Py_REFCNT(proxy_obj) >= 1);


assert(Py_REFCNT(const_obj) == refcnt);
assert(type == &PyTuple_Type);
assert(PyTuple_GET_SIZE(const_obj) == 2);
PyObject *one = PyTuple_GET_ITEM(const_obj, 0);
assert(PyLong_AsLong(one) == 1);

// gh-92898: StrongRef doesn't inherit from PyObject but has an operator to
// cast to PyObject*.
StrongRef strong_ref(obj);
assert(Py_TYPE(strong_ref) == &PyTuple_Type);
assert(Py_REFCNT(strong_ref) == (refcnt + 1));
Py_INCREF(strong_ref);
Py_DECREF(strong_ref);

Py_DECREF(obj);
Py_RETURN_NONE;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix C++ compiler warnings when casting function arguments to ``PyObject*``.
Patch by Serge Guelton.

0 comments on commit 9303a5a

Please sign in to comment.