11
11
using Value = Value_; \
12
12
static constexpr auto Name = descr; \
13
13
template <typename T_> using Cast = movable_cast_t <T_>; \
14
+ template <typename T_> static constexpr bool can_cast () { return true ; } \
14
15
template <typename T_, \
15
16
enable_if_t <std::is_same_v<std::remove_cv_t <T_>, Value>> = 0 > \
16
17
static handle from_cpp (T_ *p, rv_policy policy, cleanup_list *list) { \
@@ -38,11 +39,11 @@ enum cast_flags : uint8_t {
38
39
// Passed to the 'self' argument in a constructor call (__init__)
39
40
construct = (1 << 1 ),
40
41
41
- // Don't accept 'None' Python objects in the base class caster
42
- none_disallowed = ( 1 << 2 ),
43
-
44
- // Indicates that this cast is performed by nb::cast or nb::try_cast
45
- manual = (1 << 3 )
42
+ // Indicates that this cast is performed by nb::cast or nb::try_cast.
43
+ // This implies that objects added to the cleanup list may be
44
+ // released immediately after the caster's final output value is
45
+ // obtained, i.e., before it is used.
46
+ manual = (1 << 2 ),
46
47
};
47
48
48
49
/* *
@@ -88,6 +89,51 @@ using precise_cast_t =
88
89
std::conditional_t <std::is_rvalue_reference_v<T>,
89
90
intrinsic_t <T> &&, intrinsic_t <T> &>>;
90
91
92
+ // / Many type casters delegate to another caster using the pattern:
93
+ // / ~~~ .cc
94
+ // / bool from_python(handle src, uint8_t flags, cleanup_list *cl) noexcept {
95
+ // / SomeCaster c;
96
+ // / if (!c.from_python(src, flags, cl)) return false;
97
+ // / /* do something with */ c.operator T();
98
+ // / return true;
99
+ // / }
100
+ // / ~~~
101
+ // / This function adjusts the flags to avoid issues where the resulting T object
102
+ // / refers into storage that will dangle after SomeCaster is destroyed, and
103
+ // / causes a static assertion failure if that's not sufficient. Use it like:
104
+ // / ~~~ .cc
105
+ // / if (!c.from_python(src, flags_for_local_caster<T>(flags), cl))
106
+ // / return false;
107
+ // / ~~~
108
+ // / where the template argument T is the type you plan to extract.
109
+ template <typename T>
110
+ NB_INLINE uint8_t flags_for_local_caster (uint8_t flags) noexcept {
111
+ constexpr bool is_ref = std::is_pointer_v<T> || std::is_reference_v<T>;
112
+ if constexpr (is_base_caster_v<make_caster<T>>) {
113
+ if constexpr (is_ref) {
114
+ /* References/pointers to a type produced by implicit conversions
115
+ refer to storage owned by the cleanup_list. In a nb::cast() call,
116
+ that storage will be released before the reference can be used;
117
+ to prevent dangling, don't allow implicit conversions there. */
118
+ if (flags & ((uint8_t ) cast_flags::manual))
119
+ flags &= ~((uint8_t ) cast_flags::convert);
120
+ }
121
+ } else {
122
+ /* Any pointer produced by a non-base caster will generally point
123
+ into storage owned by the caster, which won't live long enough.
124
+ Exception: the 'char' caster produces a result that points to
125
+ storage owned by the incoming Python 'str' object, so it's OK. */
126
+ static_assert (!is_ref || std::is_same_v<T, const char *>,
127
+ " nanobind generally cannot produce objects that "
128
+ " contain interior pointers T* (or references T&) if "
129
+ " the pointee T is not handled by nanobind's regular "
130
+ " class binding mechanism. For example, you can write "
131
+ " a function that accepts int*, or std::vector<int>, "
132
+ " but not std::vector<int*>." );
133
+ }
134
+ return flags;
135
+ }
136
+
91
137
template <typename T>
92
138
struct type_caster <T, enable_if_t <std::is_arithmetic_v<T> && !is_std_char_v<T>>> {
93
139
NB_INLINE bool from_python (handle src, uint8_t flags, cleanup_list *) noexcept {
@@ -162,6 +208,7 @@ template <> struct type_caster<void_type> {
162
208
163
209
template <> struct type_caster <void > {
164
210
template <typename T_> using Cast = void *;
211
+ template <typename T_> static constexpr bool can_cast () { return true ; }
165
212
using Value = void *;
166
213
static constexpr auto Name = const_name(" types.CapsuleType" );
167
214
explicit operator void *() { return value; }
@@ -253,10 +300,15 @@ template <> struct type_caster<char> {
253
300
return PyUnicode_FromStringAndSize (&value, 1 );
254
301
}
255
302
303
+ template <typename T_>
304
+ NB_INLINE bool can_cast () const noexcept {
305
+ return std::is_pointer_v<T_> || (value && value[0 ] && value[1 ] == ' \0 ' );
306
+ }
307
+
256
308
explicit operator const char *() { return value; }
257
309
258
310
explicit operator char () {
259
- if (value && value[ 0 ] && value[ 1 ] == ' \0 ' )
311
+ if (can_cast< char >() )
260
312
return value[0 ];
261
313
else
262
314
throw next_overload ();
@@ -270,7 +322,8 @@ template <typename T> struct type_caster<pointer_and_handle<T>> {
270
322
271
323
bool from_python (handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
272
324
Caster c;
273
- if (!c.from_python (src, flags, cleanup))
325
+ if (!c.from_python (src, flags_for_local_caster<T*>(flags), cleanup) ||
326
+ !c.template can_cast <T*>())
274
327
return false ;
275
328
value.h = src;
276
329
value.p = c.operator T*();
@@ -305,13 +358,10 @@ template <typename T, typename... Ts> struct type_caster<typed<T, Ts...>> {
305
358
306
359
bool from_python (handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
307
360
Caster caster;
308
- if (!caster.from_python (src, flags, cleanup))
309
- return false ;
310
- try {
311
- value = caster.operator cast_t <T>();
312
- } catch (...) {
361
+ if (!caster.from_python (src, flags_for_local_caster<T>(flags), cleanup) ||
362
+ !caster.template can_cast <T>())
313
363
return false ;
314
- }
364
+ value = caster. operator cast_t <T>();
315
365
return true ;
316
366
}
317
367
@@ -410,6 +460,11 @@ template <typename Type_> struct type_caster_base : type_caster_base_tag {
410
460
}
411
461
}
412
462
463
+ template <typename T_>
464
+ bool can_cast () const noexcept {
465
+ return std::is_pointer_v<T_> || (value != nullptr );
466
+ }
467
+
413
468
operator Type*() { return value; }
414
469
415
470
operator Type&() {
@@ -433,58 +488,87 @@ template <bool Convert, typename T>
433
488
T cast_impl (handle h) {
434
489
using Caster = detail::make_caster<T>;
435
490
491
+ // A returned reference/pointer would usually refer into the type_caster
492
+ // object, which will be destroyed before the returned value can be used,
493
+ // so we prohibit it by default, with two exceptions that we know are safe:
494
+ //
495
+ // - If we're casting to a bound object type, the returned pointer points
496
+ // into storage owned by that object, not the type caster. Note this is
497
+ // only safe if we don't allow implicit conversions, because the pointer
498
+ // produced after an implicit conversion points into storage owned by
499
+ // a temporary object in the cleanup list, and we have to release those
500
+ // temporaries before we return.
501
+ //
502
+ // - If we're casting to const char*, the caster was provided by nanobind,
503
+ // and we know it will only accept Python 'str' objects, producing
504
+ // a pointer to storage owned by that object.
505
+
506
+ constexpr bool is_ref = std::is_reference_v<T> || std::is_pointer_v<T>;
436
507
static_assert (
437
- !(std::is_reference_v<T> || std::is_pointer_v<T>) ||
438
- detail:: is_base_caster_v<Caster> ||
508
+ !is_ref ||
509
+ is_base_caster_v<Caster> ||
439
510
std::is_same_v<const char *, T>,
440
511
" nanobind::cast(): cannot return a reference to a temporary." );
441
512
442
513
Caster caster;
443
514
bool rv;
444
- if constexpr (Convert) {
445
- cleanup_list cleanup (nullptr );
515
+ if constexpr (Convert && !is_ref) {
516
+ // Release the values in the cleanup list only after we
517
+ // initialize the return object, since the initialization
518
+ // might access those temporaries.
519
+ struct raii_cleanup {
520
+ cleanup_list list{nullptr };
521
+ ~raii_cleanup () { list.release (); }
522
+ } cleanup;
446
523
rv = caster.from_python (h.ptr (),
447
524
((uint8_t ) cast_flags::convert) |
448
525
((uint8_t ) cast_flags::manual),
449
- &cleanup);
450
- cleanup.release (); // 'from_python' is 'noexcept', so this always runs
526
+ &cleanup.list );
527
+ if (!rv)
528
+ detail::raise_cast_error ();
529
+ return caster.operator cast_t <T>();
451
530
} else {
452
531
rv = caster.from_python (h.ptr (), (uint8_t ) cast_flags::manual, nullptr );
532
+ if (!rv)
533
+ detail::raise_cast_error ();
534
+ return caster.operator cast_t <T>();
453
535
}
454
-
455
- if (!rv)
456
- detail::raise_cast_error ();
457
- return caster.operator detail::cast_t <T>();
458
536
}
459
537
460
538
template <bool Convert, typename T>
461
539
bool try_cast_impl (handle h, T &out) noexcept {
462
540
using Caster = detail::make_caster<T>;
463
541
464
- static_assert (!std::is_same_v<const char *, T>,
465
- " nanobind::try_cast(): cannot return a reference to a temporary." );
542
+ // See comments in cast_impl above
543
+ constexpr bool is_ref = std::is_reference_v<T> || std::is_pointer_v<T>;
544
+ static_assert (
545
+ !is_ref ||
546
+ is_base_caster_v<Caster> ||
547
+ std::is_same_v<const char *, T>,
548
+ " nanobind::try_cast(): cannot return a reference to a temporary." );
466
549
467
550
Caster caster;
468
551
bool rv;
469
- if constexpr (Convert) {
552
+ if constexpr (Convert && !is_ref ) {
470
553
cleanup_list cleanup (nullptr );
471
554
rv = caster.from_python (h.ptr (),
472
555
((uint8_t ) cast_flags::convert) |
473
556
((uint8_t ) cast_flags::manual),
474
- &cleanup);
557
+ &cleanup) &&
558
+ caster.template can_cast <T>();
559
+ if (rv) {
560
+ out = caster.operator cast_t <T>();
561
+ }
475
562
cleanup.release (); // 'from_python' is 'noexcept', so this always runs
476
563
} else {
477
- rv = caster.from_python (h.ptr (), (uint8_t ) cast_flags::manual, nullptr );
478
- }
479
-
480
- if (rv) {
481
- try {
482
- out = caster.operator detail::cast_t <T>();
483
- return true ;
484
- } catch (const builtin_exception&) { }
564
+ rv = caster.from_python (h.ptr (), (uint8_t ) cast_flags::manual, nullptr ) &&
565
+ caster.template can_cast <T>();
566
+ if (rv) {
567
+ out = caster.operator cast_t <T>();
568
+ }
485
569
}
486
570
487
- return false ;
571
+ return rv ;
488
572
}
489
573
490
574
NAMESPACE_END (detail)
0 commit comments