Skip to content

Commit 20d30ba

Browse files
authored
gh-92898: Enhance _testcppext test on cast to PyObject* (#93111)
* Add StrongRef class. * Rename and reformat functions of the _Py_CAST() implementation.
1 parent 5f8c3fb commit 20d30ba

File tree

3 files changed

+56
-36
lines changed

3 files changed

+56
-36
lines changed

Include/pyport.h

+23-25
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,33 @@
2222
// _Py_CAST(PyObject*, op) can convert a "const PyObject*" to
2323
// "PyObject*".
2424
//
25-
// The type argument must not be constant. For example, in C++,
26-
// _Py_CAST(const PyObject*, expr) fails with a compiler error.
25+
// The type argument must not be a constant type.
2726
#ifdef __cplusplus
2827
# define _Py_STATIC_CAST(type, expr) static_cast<type>(expr)
29-
3028
extern "C++" {
31-
namespace {
32-
template <typename type, typename expr_type>
33-
inline type _Py_reinterpret_cast_impl(expr_type *expr) {
34-
return reinterpret_cast<type>(expr);
35-
}
36-
37-
template <typename type, typename expr_type>
38-
inline type _Py_reinterpret_cast_impl(expr_type const *expr) {
39-
return reinterpret_cast<type>(const_cast<expr_type *>(expr));
40-
}
41-
42-
template <typename type, typename expr_type>
43-
inline type _Py_reinterpret_cast_impl(expr_type &expr) {
44-
return static_cast<type>(expr);
45-
}
46-
47-
template <typename type, typename expr_type>
48-
inline type _Py_reinterpret_cast_impl(expr_type const &expr) {
49-
return static_cast<type>(const_cast<expr_type &>(expr));
50-
}
51-
} // namespace
29+
namespace {
30+
template <typename type, typename expr_type>
31+
inline type _Py_CAST_impl(expr_type *expr) {
32+
return reinterpret_cast<type>(expr);
33+
}
34+
35+
template <typename type, typename expr_type>
36+
inline type _Py_CAST_impl(expr_type const *expr) {
37+
return reinterpret_cast<type>(const_cast<expr_type *>(expr));
38+
}
39+
40+
template <typename type, typename expr_type>
41+
inline type _Py_CAST_impl(expr_type &expr) {
42+
return static_cast<type>(expr);
43+
}
44+
45+
template <typename type, typename expr_type>
46+
inline type _Py_CAST_impl(expr_type const &expr) {
47+
return static_cast<type>(const_cast<expr_type &>(expr));
48+
}
49+
}
5250
}
53-
# define _Py_CAST(type, expr) _Py_reinterpret_cast_impl<type>(expr)
51+
# define _Py_CAST(type, expr) _Py_CAST_impl<type>(expr)
5452

5553
#else
5654
# define _Py_STATIC_CAST(type, expr) ((type)(expr))

Lib/test/_testcppext.cpp

+31-11
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,35 @@ _testcppext_add(PyObject *Py_UNUSED(module), PyObject *args)
2323
}
2424

2525

26+
// Class to test operator casting an object to PyObject*
27+
class StrongRef
28+
{
29+
public:
30+
StrongRef(PyObject *obj) : m_obj(obj) {
31+
Py_INCREF(this->m_obj);
32+
}
33+
34+
~StrongRef() {
35+
Py_DECREF(this->m_obj);
36+
}
37+
38+
// Cast to PyObject*: get a borrowed reference
39+
inline operator PyObject*() const { return this->m_obj; }
40+
41+
private:
42+
PyObject *m_obj; // Strong reference
43+
};
44+
45+
2646
static PyObject *
2747
test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
2848
{
2949
PyObject *obj = Py_BuildValue("(ii)", 1, 2);
3050
if (obj == nullptr) {
3151
return nullptr;
3252
}
53+
Py_ssize_t refcnt = Py_REFCNT(obj);
54+
assert(refcnt >= 1);
3355

3456
// gh-92138: For backward compatibility, functions of Python C API accepts
3557
// "const PyObject*". Check that using it does not emit C++ compiler
@@ -38,22 +60,20 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
3860
Py_INCREF(const_obj);
3961
Py_DECREF(const_obj);
4062
PyTypeObject *type = Py_TYPE(const_obj);
41-
assert(Py_REFCNT(const_obj) >= 1);
42-
43-
struct PyObjectProxy {
44-
PyObject* obj;
45-
operator PyObject *() { return obj; }
46-
} proxy_obj = { obj };
47-
Py_INCREF(proxy_obj);
48-
Py_DECREF(proxy_obj);
49-
assert(Py_REFCNT(proxy_obj) >= 1);
50-
51-
63+
assert(Py_REFCNT(const_obj) == refcnt);
5264
assert(type == &PyTuple_Type);
5365
assert(PyTuple_GET_SIZE(const_obj) == 2);
5466
PyObject *one = PyTuple_GET_ITEM(const_obj, 0);
5567
assert(PyLong_AsLong(one) == 1);
5668

69+
// gh-92898: StrongRef doesn't inherit from PyObject but has an operator to
70+
// cast to PyObject*.
71+
StrongRef strong_ref(obj);
72+
assert(Py_TYPE(strong_ref) == &PyTuple_Type);
73+
assert(Py_REFCNT(strong_ref) == (refcnt + 1));
74+
Py_INCREF(strong_ref);
75+
Py_DECREF(strong_ref);
76+
5777
Py_DECREF(obj);
5878
Py_RETURN_NONE;
5979
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix C++ compiler warnings when casting function arguments to ``PyObject*``.
2+
Patch by Serge Guelton.

0 commit comments

Comments
 (0)