Skip to content

Commit 3477463

Browse files
authored
Add C++ support (#18)
* pythoncapi_compat.h uses nullptr and reinterpret_cast<> on C++ * Add tests/test_pythoncapi_compat_cppext.cpp
1 parent 384d3d1 commit 3477463

File tree

8 files changed

+191
-78
lines changed

8 files changed

+191
-78
lines changed

docs/api.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ pythoncapi_compat.h API
55
The ``pythoncapi_compat.h`` header file provides implementations of recent
66
functions for old Python versions.
77

8-
It supports Python 3.5 to Python 3.11, Python 2.7, PyPy 3.6 and PyPy 2.7.
8+
Supported Python versions:
9+
10+
* Python 2.7, Python 3.5 - 3.11
11+
* PyPy 2.7, 3.6 and 3.7
12+
13+
C++ is supported on Python 3.6 and newer.
914

1015
A C99 subset is required, like ``static inline`` functions: see `PEP 7
1116
<https://www.python.org/dev/peps/pep-0007/>`_. ISO C90 is partially supported

docs/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Changelog
22
=========
33

4+
* 2022-02-09: ``pythoncapi_compat.h`` now supports C++ on Python 3.6 and newer:
5+
use ``nullptr`` and ``reinterpret_cast<type>`` cast on C++, and use ``NULL``
6+
and ``(type)`` cast on C.
47
* 2021-10-15: Add ``PyThreadState_EnterTracing()`` and
58
``PyThreadState_LeaveTracing()``.
69
* 2021-04-09: Add ``Py_Is()``, ``Py_IsNone()``, ``Py_IsTrue()``,

pythoncapi_compat.h

Lines changed: 70 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,34 @@ extern "C" {
2727
// the inline keyword in C (only in C++): use __inline instead.
2828
#if (defined(_MSC_VER) && _MSC_VER < 1900 \
2929
&& !defined(__cplusplus) && !defined(inline))
30-
# define inline __inline
31-
# define PYTHONCAPI_COMPAT_MSC_INLINE
32-
// These two macros are undefined at the end of this file
30+
# define PYCAPI_COMPAT_INLINE(TYPE static __inline TYPE
31+
#else
32+
# define PYCAPI_COMPAT_STATIC_INLINE(TYPE) static inline TYPE
3333
#endif
3434

3535

36+
// C++ compatibility
37+
#ifdef __cplusplus
38+
# define PYCAPI_COMPAT_CAST(TYPE, EXPR) reinterpret_cast<TYPE>(EXPR)
39+
# define PYCAPI_COMPAT_NULL nullptr
40+
#else
41+
# define PYCAPI_COMPAT_CAST(TYPE, EXPR) (TYPE)(EXPR)
42+
# define PYCAPI_COMPAT_NULL NULL
43+
#endif
44+
3645
// Cast argument to PyObject* type.
3746
#ifndef _PyObject_CAST
38-
# define _PyObject_CAST(op) ((PyObject*)(op))
47+
# define _PyObject_CAST(op) PYCAPI_COMPAT_CAST(PyObject*, op)
3948
#endif
4049
#ifndef _PyObject_CAST_CONST
41-
# define _PyObject_CAST_CONST(op) ((const PyObject*)(op))
50+
# define _PyObject_CAST_CONST(op) PYCAPI_COMPAT_CAST(const PyObject*, op)
4251
#endif
4352

4453

4554
// bpo-42262 added Py_NewRef() to Python 3.10.0a3
4655
#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef)
47-
static inline PyObject* _Py_NewRef(PyObject *obj)
56+
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
57+
_Py_NewRef(PyObject *obj)
4858
{
4959
Py_INCREF(obj);
5060
return obj;
@@ -55,7 +65,8 @@ static inline PyObject* _Py_NewRef(PyObject *obj)
5565

5666
// bpo-42262 added Py_XNewRef() to Python 3.10.0a3
5767
#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef)
58-
static inline PyObject* _Py_XNewRef(PyObject *obj)
68+
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
69+
_Py_XNewRef(PyObject *obj)
5970
{
6071
Py_XINCREF(obj);
6172
return obj;
@@ -66,7 +77,8 @@ static inline PyObject* _Py_XNewRef(PyObject *obj)
6677

6778
// See https://bugs.python.org/issue42522
6879
#if !defined(_Py_StealRef)
69-
static inline PyObject* __Py_StealRef(PyObject *obj)
80+
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
81+
__Py_StealRef(PyObject *obj)
7082
{
7183
Py_DECREF(obj);
7284
return obj;
@@ -77,7 +89,8 @@ static inline PyObject* __Py_StealRef(PyObject *obj)
7789

7890
// See https://bugs.python.org/issue42522
7991
#if !defined(_Py_XStealRef)
80-
static inline PyObject* __Py_XStealRef(PyObject *obj)
92+
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
93+
__Py_XStealRef(PyObject *obj)
8194
{
8295
Py_XDECREF(obj);
8396
return obj;
@@ -88,7 +101,8 @@ static inline PyObject* __Py_XStealRef(PyObject *obj)
88101

89102
// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4
90103
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT)
91-
static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt)
104+
PYCAPI_COMPAT_STATIC_INLINE(void)
105+
_Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt)
92106
{
93107
ob->ob_refcnt = refcnt;
94108
}
@@ -133,7 +147,7 @@ static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt)
133147

134148
// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4
135149
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE)
136-
static inline void
150+
PYCAPI_COMPAT_STATIC_INLINE(void)
137151
_Py_SET_TYPE(PyObject *ob, PyTypeObject *type)
138152
{
139153
ob->ob_type = type;
@@ -144,7 +158,7 @@ _Py_SET_TYPE(PyObject *ob, PyTypeObject *type)
144158

145159
// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4
146160
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE)
147-
static inline void
161+
PYCAPI_COMPAT_STATIC_INLINE(void)
148162
_Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size)
149163
{
150164
ob->ob_size = size;
@@ -155,85 +169,88 @@ _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size)
155169

156170
// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1
157171
#if PY_VERSION_HEX < 0x030900B1
158-
static inline PyCodeObject*
172+
PYCAPI_COMPAT_STATIC_INLINE(PyCodeObject*)
159173
PyFrame_GetCode(PyFrameObject *frame)
160174
{
161-
assert(frame != NULL);
162-
assert(frame->f_code != NULL);
163-
return (PyCodeObject*)Py_NewRef(frame->f_code);
175+
assert(frame != PYCAPI_COMPAT_NULL);
176+
assert(frame->f_code != PYCAPI_COMPAT_NULL);
177+
return PYCAPI_COMPAT_CAST(PyCodeObject*, Py_NewRef(frame->f_code));
164178
}
165179
#endif
166180

167-
static inline PyCodeObject*
181+
PYCAPI_COMPAT_STATIC_INLINE(PyCodeObject*)
168182
_PyFrame_GetCodeBorrow(PyFrameObject *frame)
169183
{
170-
return (PyCodeObject *)_Py_StealRef(PyFrame_GetCode(frame));
184+
return PYCAPI_COMPAT_CAST(PyCodeObject *,
185+
_Py_StealRef(PyFrame_GetCode(frame)));
171186
}
172187

173188

174189
// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1
175190
#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION)
176-
static inline PyFrameObject*
191+
PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*)
177192
PyFrame_GetBack(PyFrameObject *frame)
178193
{
179-
assert(frame != NULL);
180-
return (PyFrameObject*)Py_XNewRef(frame->f_back);
194+
assert(frame != PYCAPI_COMPAT_NULL);
195+
return PYCAPI_COMPAT_CAST(PyFrameObject*, Py_XNewRef(frame->f_back));
181196
}
182197
#endif
183198

184199
#if !defined(PYPY_VERSION)
185-
static inline PyFrameObject*
200+
PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*)
186201
_PyFrame_GetBackBorrow(PyFrameObject *frame)
187202
{
188-
return (PyFrameObject *)_Py_XStealRef(PyFrame_GetBack(frame));
203+
return PYCAPI_COMPAT_CAST(PyFrameObject *,
204+
_Py_XStealRef(PyFrame_GetBack(frame)));
189205
}
190206
#endif
191207

192208

193209
// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5
194210
#if PY_VERSION_HEX < 0x030900A5
195-
static inline PyInterpreterState *
211+
PYCAPI_COMPAT_STATIC_INLINE(PyInterpreterState *)
196212
PyThreadState_GetInterpreter(PyThreadState *tstate)
197213
{
198-
assert(tstate != NULL);
214+
assert(tstate != PYCAPI_COMPAT_NULL);
199215
return tstate->interp;
200216
}
201217
#endif
202218

203219

204220
// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1
205221
#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION)
206-
static inline PyFrameObject*
222+
PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*)
207223
PyThreadState_GetFrame(PyThreadState *tstate)
208224
{
209-
assert(tstate != NULL);
210-
return (PyFrameObject *)Py_XNewRef(tstate->frame);
225+
assert(tstate != PYCAPI_COMPAT_NULL);
226+
return PYCAPI_COMPAT_CAST(PyFrameObject *, Py_XNewRef(tstate->frame));
211227
}
212228
#endif
213229

214230
#if !defined(PYPY_VERSION)
215-
static inline PyFrameObject*
231+
PYCAPI_COMPAT_STATIC_INLINE(PyFrameObject*)
216232
_PyThreadState_GetFrameBorrow(PyThreadState *tstate)
217233
{
218-
return (PyFrameObject *)_Py_XStealRef(PyThreadState_GetFrame(tstate));
234+
return PYCAPI_COMPAT_CAST(PyFrameObject*,
235+
_Py_XStealRef(PyThreadState_GetFrame(tstate)));
219236
}
220237
#endif
221238

222239

223240
// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5
224241
#if PY_VERSION_HEX < 0x030900A5
225-
static inline PyInterpreterState *
242+
PYCAPI_COMPAT_STATIC_INLINE(PyInterpreterState*)
226243
PyInterpreterState_Get(void)
227244
{
228245
PyThreadState *tstate;
229246
PyInterpreterState *interp;
230247

231248
tstate = PyThreadState_GET();
232-
if (tstate == NULL) {
249+
if (tstate == PYCAPI_COMPAT_NULL) {
233250
Py_FatalError("GIL released (tstate is NULL)");
234251
}
235252
interp = tstate->interp;
236-
if (interp == NULL) {
253+
if (interp == PYCAPI_COMPAT_NULL) {
237254
Py_FatalError("no current interpreter");
238255
}
239256
return interp;
@@ -243,17 +260,18 @@ PyInterpreterState_Get(void)
243260

244261
// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6
245262
#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION)
246-
static inline uint64_t
263+
PYCAPI_COMPAT_STATIC_INLINE(uint64_t)
247264
PyThreadState_GetID(PyThreadState *tstate)
248265
{
249-
assert(tstate != NULL);
266+
assert(tstate != PYCAPI_COMPAT_NULL);
250267
return tstate->id;
251268
}
252269
#endif
253270

254271
// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2
255272
#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
256-
static inline void PyThreadState_EnterTracing(PyThreadState *tstate)
273+
PYCAPI_COMPAT_STATIC_INLINE(void)
274+
PyThreadState_EnterTracing(PyThreadState *tstate)
257275
{
258276
tstate->tracing++;
259277
#if PY_VERSION_HEX >= 0x030A00A1
@@ -266,10 +284,11 @@ static inline void PyThreadState_EnterTracing(PyThreadState *tstate)
266284

267285
// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2
268286
#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
269-
static inline void PyThreadState_LeaveTracing(PyThreadState *tstate)
287+
PYCAPI_COMPAT_STATIC_INLINE(void)
288+
PyThreadState_LeaveTracing(PyThreadState *tstate)
270289
{
271-
int use_tracing = (tstate->c_tracefunc != NULL
272-
|| tstate->c_profilefunc != NULL);
290+
int use_tracing = (tstate->c_tracefunc != PYCAPI_COMPAT_NULL
291+
|| tstate->c_profilefunc != PYCAPI_COMPAT_NULL);
273292
tstate->tracing--;
274293
#if PY_VERSION_HEX >= 0x030A00A1
275294
tstate->cframe->use_tracing = use_tracing;
@@ -282,7 +301,7 @@ static inline void PyThreadState_LeaveTracing(PyThreadState *tstate)
282301

283302
// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1
284303
#if PY_VERSION_HEX < 0x030900A1
285-
static inline PyObject*
304+
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
286305
PyObject_CallNoArgs(PyObject *func)
287306
{
288307
return PyObject_CallFunctionObjArgs(func, NULL);
@@ -293,7 +312,7 @@ PyObject_CallNoArgs(PyObject *func)
293312
// bpo-39245 made PyObject_CallOneArg() public (previously called
294313
// _PyObject_CallOneArg) in Python 3.9.0a4
295314
#if PY_VERSION_HEX < 0x030900A4
296-
static inline PyObject*
315+
PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
297316
PyObject_CallOneArg(PyObject *func, PyObject *arg)
298317
{
299318
return PyObject_CallFunctionObjArgs(func, arg, NULL);
@@ -303,7 +322,7 @@ PyObject_CallOneArg(PyObject *func, PyObject *arg)
303322

304323
// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3
305324
#if PY_VERSION_HEX < 0x030A00A3
306-
static inline int
325+
PYCAPI_COMPAT_STATIC_INLINE(int)
307326
PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value)
308327
{
309328
int res;
@@ -319,7 +338,7 @@ PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value)
319338

320339
// bpo-40024 added PyModule_AddType() to Python 3.9.0a5
321340
#if PY_VERSION_HEX < 0x030900A5
322-
static inline int
341+
PYCAPI_COMPAT_STATIC_INLINE(int)
323342
PyModule_AddType(PyObject *module, PyTypeObject *type)
324343
{
325344
const char *name, *dot;
@@ -330,21 +349,21 @@ PyModule_AddType(PyObject *module, PyTypeObject *type)
330349

331350
// inline _PyType_Name()
332351
name = type->tp_name;
333-
assert(name != NULL);
352+
assert(name != PYCAPI_COMPAT_NULL);
334353
dot = strrchr(name, '.');
335-
if (dot != NULL) {
354+
if (dot != PYCAPI_COMPAT_NULL) {
336355
name = dot + 1;
337356
}
338357

339-
return PyModule_AddObjectRef(module, name, (PyObject *)type);
358+
return PyModule_AddObjectRef(module, name, _PyObject_CAST(type));
340359
}
341360
#endif
342361

343362

344363
// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6.
345364
// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2.
346365
#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION)
347-
static inline int
366+
PYCAPI_COMPAT_STATIC_INLINE(int)
348367
PyObject_GC_IsTracked(PyObject* obj)
349368
{
350369
return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj));
@@ -354,17 +373,18 @@ PyObject_GC_IsTracked(PyObject* obj)
354373
// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6.
355374
// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final.
356375
#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION)
357-
static inline int
376+
PYCAPI_COMPAT_STATIC_INLINE(int)
358377
PyObject_GC_IsFinalized(PyObject *obj)
359378
{
360-
return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED((PyGC_Head *)(obj)-1));
379+
PyGC_Head *gc = PYCAPI_COMPAT_CAST(PyGC_Head *, obj) - 1;
380+
return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc));
361381
}
362382
#endif
363383

364384

365385
// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4
366386
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE)
367-
static inline int
387+
PYCAPI_COMPAT_STATIC_INLINE(int)
368388
_Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
369389
return ob->ob_type == type;
370390
}
@@ -382,11 +402,6 @@ _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
382402
#endif
383403

384404

385-
#ifdef PYTHONCAPI_COMPAT_MSC_INLINE
386-
# undef inline
387-
# undef PYTHONCAPI_COMPAT_MSC_INLINE
388-
#endif
389-
390405
#ifdef __cplusplus
391406
}
392407
#endif

runtests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/python3
1+
#!/usr/bin/python3 -u
22
"""
33
Run the test suite on multiple Python versions.
44

0 commit comments

Comments
 (0)