@@ -47,13 +47,17 @@ inline void decref(PyObject *) noexcept;
47
47
template <>
48
48
inline void decref<false >(PyObject *obj) noexcept
49
49
{
50
+ // Assert that the reference is valid if it is not null.
51
+ PYDYND_ASSERT_IF (obj != nullptr , Py_REFCNT (obj) > 0 );
50
52
Py_XDECREF (obj);
51
53
}
52
54
53
55
template <>
54
56
inline void decref<true >(PyObject *obj) noexcept
55
57
{
56
58
assert (obj != nullptr );
59
+ // Assert that the reference is valid.
60
+ assert (Py_REFCNT (obj) > 0 );
57
61
Py_DECREF (obj);
58
62
}
59
63
@@ -90,23 +94,33 @@ template <>
90
94
inline void decref_if_owned<true , true >(PyObject *obj) noexcept
91
95
{
92
96
assert (obj != nullptr );
97
+ // Assert that the reference is valid.
98
+ assert (Py_REFCNT (obj) > 0 );
93
99
Py_DECREF (obj);
94
100
}
95
101
96
102
template <>
97
103
inline void decref_if_owned<true , false >(PyObject *obj) noexcept
98
104
{
105
+ // Assert that the reference is valid if it is not null.
106
+ PYDYND_ASSERT_IF (obj != nullptr , Py_REFCNT (obj) > 0 );
99
107
Py_XDECREF (obj);
100
108
}
101
109
110
+ // This is a no-op in non-debug builds.
111
+ // In debug builds, it asserts that the reference is actually valid.
102
112
template <>
103
113
inline void decref_if_owned<false , true >(PyObject *obj) noexcept
104
114
{
115
+ assert (Py_REFCNT (obj) > 0 );
105
116
}
106
117
118
+ // This is a no-op in non-debug builds.
119
+ // In debug builds, it asserts that the reference is actually valid.
107
120
template <>
108
121
inline void decref_if_owned<false , false >(PyObject *obj) noexcept
109
122
{
123
+ PYDYND_ASSERT_IF (obj != nullptr , Py_REFCNT (obj) > 0 );
110
124
}
111
125
112
126
template <bool owns_ref = true , bool not_null = true >
@@ -125,6 +139,8 @@ class py_ref_tmpl {
125
139
PyObject *get () noexcept
126
140
{
127
141
PYDYND_ASSERT_IF (not_null, o != nullptr );
142
+ // Assert that the accessed reference is still valid.
143
+ PYDYND_ASSERT_IF (o != nullptr , Py_REFCNT (o) > 0 );
128
144
return o;
129
145
}
130
146
@@ -133,18 +149,18 @@ class py_ref_tmpl {
133
149
// they are also declared noexcept.
134
150
135
151
/* If:
136
- * this type allows null,
137
- * Or:
138
- * this type doesn't allow null
139
- * and the input type doesn't allow null,
152
+ * This type allows null or the input type does not,
140
153
* Then:
141
154
* Conversions from the other py_ref_tmpl type to this type do not raise exceptions.
142
155
*/
143
156
template <bool other_owns_ref, bool other_not_null,
144
- typename std::enable_if_t <( other_not_null && not_null) || !not_null> * = nullptr >
157
+ typename std::enable_if_t <other_not_null || !not_null> * = nullptr >
145
158
py_ref_tmpl (const py_ref_tmpl<other_owns_ref, other_not_null> &other) noexcept
146
159
{
147
- PYDYND_ASSERT_IF (not_null || other_not_null, other.o != nullptr );
160
+ // If the input is not null, assert that it isn't.
161
+ PYDYND_ASSERT_IF (other_not_null, other.o != nullptr );
162
+ // Assert that the input reference is valid if it is not null.
163
+ PYDYND_ASSERT_IF (other.o != nullptr , Py_REFCNT (other.o ) > 0 );
148
164
o = other.o ;
149
165
incref_if_owned<owns_ref, not_null>(o);
150
166
}
@@ -162,6 +178,8 @@ class py_ref_tmpl {
162
178
py_ref_tmpl (const py_ref_tmpl<other_owns_ref, other_not_null> &other)
163
179
{
164
180
if (other.o != nullptr ) {
181
+ // Assert that the input reference is valid.
182
+ assert (Py_REFCNT (other.o ) > 0 );
165
183
o = other.o ;
166
184
incref_if_owned<owns_ref, not_null>(o);
167
185
}
@@ -180,22 +198,26 @@ class py_ref_tmpl {
180
198
* Then:
181
199
* a move operation is defined and will not raise an exception.
182
200
*/
183
- template <bool other_not_null, typename std::enable_if_t <( other_not_null || !not_null) > * = nullptr >
201
+ template <bool other_not_null, typename std::enable_if_t <other_not_null || !not_null> * = nullptr >
184
202
py_ref_tmpl (py_ref_tmpl<true , other_not_null> &&other) noexcept
185
203
{
186
204
// If this type is a non-null type, the assigned value should not be null.
187
205
// If the other type is a non-null type, the provided value should not be null,
188
206
// unless it is being used uninitialized or after being moved from.
189
207
PYDYND_ASSERT_IF (not_null || other_not_null, other.o != nullptr );
208
+ // If the reference is not null, assert that it is valid.
209
+ PYDYND_ASSERT_IF (other.o != nullptr , Py_REFCNT (other.o ) > 0 );
190
210
// Use get to assert that other.o is not null if other_not_null is true.
191
211
o = other.o ;
212
+ // Make sure other can be destructed without decrefing
213
+ // this object's wrapped pointer.
192
214
other.o = nullptr ;
193
215
// The other type owns its reference.
194
216
// If this one does not, decref the new pointer.
195
217
// If this type is a non-null type, the assigned value should not be null.
196
218
decref_if_owned<!owns_ref, not_null>(o);
197
- // Make sure other can be destructed without decrefing
198
- // this object's wrapped pointer.
219
+ // If the reference is not null, assert that it is still valid.
220
+ PYDYND_ASSERT_IF (o != nullptr , Py_REFCNT (o) > 0 )
199
221
}
200
222
201
223
/* If:
@@ -208,14 +230,18 @@ class py_ref_tmpl {
208
230
py_ref_tmpl (py_ref_tmpl<true , other_not_null> &&other)
209
231
{
210
232
if (other.o != nullptr ) {
233
+ // Assert that the input reference is valid.
234
+ assert (Py_REFCNT (other.o ) > 0 );
211
235
o = other.o ;
236
+ // Make sure other can be destructed without decrefing
237
+ // this object's wrapped pointer.
212
238
other.o = nullptr ;
213
239
// The other type owns its reference.
214
240
// If this one does not, decref the new pointer.
215
241
// The assigned value is already known not be null.
216
242
decref_if_owned<!owns_ref, not_null>(o);
217
- // Make sure other can be destructed without decrefing
218
- // this object's wrapped pointer.
243
+ // Assert that the stored reference is still valid
244
+ assert ( Py_REFCNT (o) > 0 );
219
245
}
220
246
else {
221
247
throw std::invalid_argument (" Cannot convert null valued pointer to non-null reference." );
@@ -239,6 +265,9 @@ class py_ref_tmpl {
239
265
else {
240
266
incref_if_owned<owns_ref, not_null>(o);
241
267
}
268
+ // Regardless of whether or not a reference was consumed,
269
+ // assert that it is valid if it is not null.
270
+ PYDYND_ASSERT_IF (obj != null, Py_REFCNT (obj) > 0 );
242
271
}
243
272
244
273
~py_ref_tmpl ()
@@ -257,18 +286,18 @@ class py_ref_tmpl {
257
286
// Assignment never comsumes a reference.
258
287
259
288
/* If:
260
- * this type allows null,
261
- * Or:
262
- * this type doesn't allow null
263
- * and the input type doesn't allow null,
289
+ * This type allows null or the input type does not,
264
290
* Then:
265
291
* Assignment from the other py_ref_tmpl type to this type may not raise an exception.
266
292
*/
267
293
template <bool other_owns_ref, bool other_not_null,
268
- typename std::enable_if_t <( other_not_null && not_null) || !not_null> * = nullptr >
294
+ typename std::enable_if_t <other_not_null || !not_null> * = nullptr >
269
295
py_ref_tmpl<owns_ref, not_null> &operator =(const py_ref_tmpl<other_owns_ref, other_not_null> &other) noexcept
270
296
{
271
- PYDYND_ASSERT_IF (not_null || other_not_null, other.o != nullptr );
297
+ // Assert that the input reference is not null if the input type does not allow nulls.
298
+ PYDYND_ASSERT_IF (other_not_null, other.o != nullptr );
299
+ // Assert that the input reference is valid if it is not null.
300
+ PYDYND_ASSERT_IF (other.o != nullptr , Py_REFCNT (other.o ) > 0 );
272
301
// Nullcheck when doing decref in case this object
273
302
// is uninitialized or has been moved from.
274
303
decref_if_owned<owns_ref, false >(o);
@@ -288,6 +317,8 @@ class py_ref_tmpl {
288
317
py_ref_tmpl<owns_ref, not_null> &operator =(const py_ref_tmpl<other_owns_ref, other_not_null> &other) noexcept
289
318
{
290
319
if (other.o != nullptr ) {
320
+ // Assert that the input reference is valid.
321
+ assert (Py_REFCNT (other.o ) > 0 );
291
322
// Nullcheck when doing decref in case this object
292
323
// is uninitialized or has been moved from.
293
324
decref_if_owned<owns_ref, false >(o);
@@ -311,10 +342,13 @@ class py_ref_tmpl {
311
342
* Assignment from the other py_ref_tmpl type to this type may not raise an exception.
312
343
*/
313
344
template <bool other_owns_ref, bool other_not_null,
314
- typename std::enable_if_t <( other_not_null && not_null) || !not_null> * = nullptr >
345
+ typename std::enable_if_t <other_not_null || !not_null> * = nullptr >
315
346
py_ref_tmpl<owns_ref, not_null> &operator =(py_ref_tmpl<other_owns_ref, other_not_null> &&other) noexcept
316
347
{
317
- PYDYND_ASSERT_IF (not_null || other_not_null, other.o != nullptr );
348
+ // If the input reference should not be null, assert that that is the case.
349
+ PYDYND_ASSERT_IF (other_not_null, other.o != nullptr );
350
+ // If the input reference is not null, assert that it is valid.
351
+ PYDYND_ASSERT_IF (other.o != nullptr , Py_REFCNT (other.o ) > 0 );
318
352
// Nullcheck when doing decref in case this object
319
353
// is uninitialized or has been moved from.
320
354
decref_if_owned<owns_ref, false >(o);
@@ -335,6 +369,8 @@ class py_ref_tmpl {
335
369
py_ref_tmpl<owns_ref, not_null> &operator =(py_ref_tmpl<other_owns_ref, other_not_null> &&other) noexcept
336
370
{
337
371
if (other.o != nullptr ) {
372
+ // Assert that the input reference is valid.
373
+ assert (Py_REFCNT (other.o ) > 0 );
338
374
// Nullcheck when doing decref in case this object
339
375
// is uninitialized or has been moved from.
340
376
decref_if_owned<owns_ref, false >(o);
@@ -352,16 +388,16 @@ class py_ref_tmpl {
352
388
// Set the encapsulated pointer to NULL.
353
389
PyObject *release () noexcept
354
390
{
391
+ // If the contained reference should not be null, assert that it isn't.
355
392
PYDYND_ASSERT_IF (not_null, o != nullptr );
393
+ // If the contained reference is not null, assert that it is valid.
394
+ PYDYND_ASSERT_IF (o != nullptr , Py_REFCNT (o) > 0 );
356
395
auto ret = o;
357
396
o = nullptr ;
358
397
incref_if_owned<!owns_ref, not_null>(ret);
359
398
return ret;
360
399
}
361
400
362
- // Check if the wrapped pointer is null.
363
- bool is_null () noexcept { return o != nullptr ; }
364
-
365
401
py_ref_tmpl<false , not_null> borrow () noexcept { return py_ref<false , not_null>(o, false ); }
366
402
};
367
403
@@ -383,9 +419,7 @@ inline py_ref capture_if_not_null(PyObject *o)
383
419
if (o == nullptr ) {
384
420
throw std::runtime_error (" Unexpected null pointer." );
385
421
}
386
- auto p = py_ref (o, true );
387
- return p;
388
- // return py_ref(o, true);
422
+ return py_ref (o, true );
389
423
}
390
424
391
425
/* Convert to a non-null reference.
@@ -408,7 +442,10 @@ inline py_ref_tmpl<owns_ref, true> nullcheck(py_ref_tmpl<owns_ref, not_null> &&o
408
442
template <bool owns_ref, bool not_null>
409
443
inline py_ref_tmpl<owns_ref, true > nullcheck (py_ref_tmpl<owns_ref, not_null> &&obj) noexcept
410
444
{
445
+ // Assert that the wrapped pointer is actually not null.
411
446
assert (obj.get () != nullptr );
447
+ // Assert that the wrapped reference is valid if it is not null.
448
+ PYDYND_ASSERT_IF (obj.get () != nullptr , Py_REFCNT (obj.get ()) > 0 );
412
449
return py_ref_tmpl<owns_ref, true >(obj.release (), owns_ref);
413
450
}
414
451
0 commit comments