Skip to content

Commit e664962

Browse files
committed
expose move constructor in low level interface
1 parent 8e607fe commit e664962

File tree

6 files changed

+46
-5
lines changed

6 files changed

+46
-5
lines changed

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,15 @@ changes are detailed below.
527527
by initializing it yet again. */
528528
nb::inst_destruct(py_inst);
529529
assert(!nb::inst_ready(py_inst));
530+
531+
/* We can copy- or move-construct 'py_inst' from another instance of the
532+
same type. This calls the C++ copy or move constructor and transitions
533+
'py_inst' back to 'ready' status. Note that this is equivalent to calling
534+
an in-place version of these constructors above but compiles to more
535+
compact code (the 'nb::class_<MyClass>' declaration has already created
536+
bindings for both constructors, and this simply calls those bindings). */
537+
// nb::inst_copy(/* dst = */ py_inst, /* src = */ some_other_instance);
538+
// nb::inst_move(/* dst = */ py_inst, /* src = */ some_other_instance);
530539
```
531540

532541
Note that these functions are all _unsafe_ in the sense that they do not

include/nanobind/nb_class.h

+1
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ inline bool inst_ready(handle h) { return detail::nb_inst_ready(h.ptr()); }
447447
inline void inst_mark_ready(handle h) { detail::nb_inst_mark_ready(h.ptr()); }
448448
inline void inst_destruct(handle h) { detail::nb_inst_destruct(h.ptr()); }
449449
inline void inst_copy(handle dst, handle src) { detail::nb_inst_copy(dst.ptr(), src.ptr()); }
450+
inline void inst_move(handle dst, handle src) { detail::nb_inst_move(dst.ptr(), src.ptr()); }
450451
template <typename T> T *inst_ptr(handle h) { return (T *) detail::nb_inst_ptr(h.ptr()); }
451452

452453
NAMESPACE_END(NB_NAMESPACE)

include/nanobind/nb_lib.h

+3
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ NB_CORE void nb_inst_destruct(PyObject *o) noexcept;
243243
/// Copy-construct 'dst' from 'src' and mark it as ready (must have the same nb_type)
244244
NB_CORE void nb_inst_copy(PyObject *dst, const PyObject *src) noexcept;
245245

246+
/// Move-construct 'dst' from 'src' and mark it as ready (must have the same nb_type)
247+
NB_CORE void nb_inst_move(PyObject *dst, const PyObject *src) noexcept;
248+
246249
/// Check if an instance is ready
247250
NB_CORE bool nb_inst_ready(PyObject *o) noexcept;
248251

src/nb_type.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ PyObject *nb_type_put(const std::type_info *cpp_type, void *value,
611611
}
612612
} else {
613613
memcpy(new_value, value, t->size);
614+
memset(value, 0, t->size);
614615
}
615616
} else {
616617
if (t->flags & (uint32_t) type_flags::is_copy_constructible) {
@@ -825,5 +826,25 @@ void nb_inst_copy(PyObject *dst, const PyObject *src) noexcept {
825826
nbi->ready = nbi->destruct = true;
826827
}
827828

829+
void nb_inst_move(PyObject *dst, const PyObject *src) noexcept {
830+
nb_type *nbt = (nb_type *) Py_TYPE(src);
831+
if (Py_TYPE(src) != Py_TYPE(dst) ||
832+
(nbt->t.flags & (uint32_t) type_flags::is_move_constructible) == 0)
833+
fail("nanobind::detail::nb_inst_move(): invalid arguments!");
834+
835+
nb_inst *nbi = (nb_inst *) dst;
836+
void *src_data = inst_ptr((nb_inst *) src);
837+
void *dst_data = inst_ptr(nbi);
838+
839+
if (nbt->t.flags & (uint32_t) type_flags::has_move) {
840+
nbt->t.move(dst_data, src_data);
841+
} else {
842+
memcpy(dst_data, src_data, nbt->t.size);
843+
memset(src_data, 0, nbt->t.size);
844+
}
845+
846+
nbi->ready = nbi->destruct = true;
847+
}
848+
828849
NAMESPACE_END(detail)
829850
NAMESPACE_END(NB_NAMESPACE)

tests/test_classes.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <nanobind/trampoline.h>
33
#include <nanobind/operators.h>
44
#include <nanobind/stl/string.h>
5+
#include <nanobind/stl/pair.h>
56
#include <memory>
67

78
namespace nb = nanobind;
@@ -347,6 +348,10 @@ NB_MODULE(test_classes_ext, m) {
347348
if (nb::inst_ready(py_inst))
348349
throw std::runtime_error("Internal error! (6)");
349350

350-
return py_inst_2;
351+
nb::inst_move(py_inst, py_inst_2);
352+
if (!nb::inst_ready(py_inst))
353+
throw std::runtime_error("Internal error! (7)");
354+
355+
return std::make_pair(py_inst, py_inst_2);
351356
});
352357
}

tests/test_classes.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -414,11 +414,13 @@ def test20_type_callback():
414414
assert len(o) == 123
415415

416416
def test21_low_level(clean):
417-
s = t.test_lowlevel()
418-
assert s.value() == 123
419-
del s
417+
s1, s2 = t.test_lowlevel()
418+
assert s1.value() == 123 and s2.value() == 0
419+
del s1
420+
del s2
420421
assert_stats(
421422
value_constructed=1,
422423
copy_constructed=1,
423-
destructed=2
424+
move_constructed=1,
425+
destructed=3
424426
)

0 commit comments

Comments
 (0)