Skip to content

Commit 5cfa11a

Browse files
authored
Make nanobind::detail::trampoline constructor constexpr (#1223)
This fixes a bug in MSVC, and allows creating trampolines for classes that have constexpr constructors that take a parameter.
1 parent d145ec5 commit 5cfa11a

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

include/nanobind/trampoline.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ NB_CORE void trampoline_leave(ticket *ticket) noexcept;
2626
template <size_t Size> struct trampoline {
2727
mutable void *data[2 * Size + 1];
2828

29-
NB_INLINE trampoline(void *ptr) { trampoline_new(data, Size, ptr); }
29+
NB_INLINE constexpr trampoline(void *ptr) { trampoline_new(data, Size, ptr); }
3030
NB_INLINE ~trampoline() { trampoline_release(data, Size); }
3131

3232
NB_INLINE handle base() const { return (PyObject *) data[0]; }

tests/test_classes.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,4 +747,34 @@ NB_MODULE(test_classes_ext, m) {
747747
.def_prop_ro_static("x", [](nb::handle /*unused*/) { return 42; });
748748
nb::class_<StaticPropertyOverride2, StaticPropertyOverride>(m, "StaticPropertyOverride2")
749749
.def_prop_ro_static("x", [](nb::handle /*unused*/) { return 43; });
750+
751+
752+
// nanobind::detail::trampoline's constructor must be constexpr otherwise
753+
// the trampoline will not compile under MSVC
754+
struct ConstexprClass {
755+
constexpr ConstexprClass(int i) : something(i) {}
756+
virtual ~ConstexprClass() = default;
757+
758+
virtual int getInt() const {
759+
return 1;
760+
};
761+
762+
int something;
763+
};
764+
765+
struct PyConstexprClass : ConstexprClass {
766+
NB_TRAMPOLINE(ConstexprClass, 1);
767+
768+
int getInt() const override {
769+
NB_OVERRIDE(getInt);
770+
}
771+
};
772+
773+
auto constexpr_class = nb::class_<ConstexprClass, PyConstexprClass>(m, "ConstexprClass")
774+
.def(nb::init<int>())
775+
.def("getInt", &ConstexprClass::getInt);
776+
777+
m.def("constexpr_call_getInt", [](ConstexprClass *c) {
778+
return c->getInt();
779+
});
750780
}

tests/test_classes.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,3 +981,11 @@ class SubSubClass(SubClass):
981981
# Clean up
982982
del x
983983
gc.collect()
984+
985+
def test51_constexpr_trampoline():
986+
class PyConstexprClass(t.ConstexprClass):
987+
def getInt(self):
988+
return 42
989+
990+
c = PyConstexprClass(4)
991+
assert t.constexpr_call_getInt(c) == 42

tests/test_classes_ext.pyi.ref

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,10 @@ class StaticPropertyOverride:
371371
class StaticPropertyOverride2(StaticPropertyOverride):
372372
x: int = ...
373373
"""(arg: object, /) -> int"""
374+
375+
class ConstexprClass:
376+
def __init__(self, arg: int, /) -> None: ...
377+
378+
def getInt(self) -> int: ...
379+
380+
def constexpr_call_getInt(arg: ConstexprClass, /) -> int: ...

0 commit comments

Comments
 (0)