diff --git a/tests/test_variant_unique_shared.cpp b/tests/test_variant_unique_shared.cpp new file mode 100644 index 000000000..7d7f0c945 --- /dev/null +++ b/tests/test_variant_unique_shared.cpp @@ -0,0 +1,101 @@ +#include +#include + +#include "pybind11_tests.h" + +namespace pybind11_tests { + +// Could this be a holder for a `class_`-like `vclass`? +// To enable passing of unique_ptr as in pure C++. +template class vptr_holder { + public: + explicit vptr_holder(T *ptr = nullptr) : vptr_{std::unique_ptr(ptr)} {} + explicit vptr_holder(std::unique_ptr u) : vptr_{std::move(u)} {} + explicit vptr_holder(std::shared_ptr s) : vptr_{s} {} + + int ownership_type() const { + if (std::get_if<0>(&vptr_)) { + return 0; + } + if (std::get_if<1>(&vptr_)) { + return 1; + } + return -1; + } + + T *get() { + auto u = std::get_if<0>(&vptr_); + if (u) { + return u->get(); + } + auto s = std::get_if<1>(&vptr_); + if (s) { + return s->get(); + } + return nullptr; + } + + std::unique_ptr get_unique() { + auto u = std::get_if<0>(&vptr_); + if (u) { + return std::move(*u); + } + throw std::runtime_error("get_unique failure."); + } + + std::shared_ptr get_shared() { + auto s = std::get_if<1>(&vptr_); + if (s) { + return *s; + } + auto u = std::get_if<0>(&vptr_); + if (u) { + auto result = std::shared_ptr(std::move(*u)); + vptr_ = result; + return result; + } + throw std::runtime_error("get_shared failure."); + } + + private: + std::variant, std::shared_ptr> vptr_; +}; + +vptr_holder from_raw() { return vptr_holder{new double{3}}; } + +vptr_holder from_unique() { + return vptr_holder{std::unique_ptr(new double{5})}; +} + +vptr_holder from_shared() { + return vptr_holder{std::shared_ptr(new double{7})}; +} + +TEST_SUBMODULE(variant_unique_shared, m) { + + m.def("from_raw", from_raw); + m.def("from_unique", from_unique); + m.def("from_shared", from_shared); + + py::class_>(m, "vptr_holder_double") + .def(py::init<>()) + .def("ownership_type", &vptr_holder::ownership_type) + .def("get_value", + [](vptr_holder &v) { + auto p = v.get(); + if (p) + return *p; + return -1.; + }) + .def("get_unique", + [](vptr_holder &v) { + v.get_unique(); + return; + }) + .def("get_shared", [](vptr_holder &v) { + v.get_shared(); + return; + }); +} + +} // namespace pybind11_tests diff --git a/tests/test_variant_unique_shared.py b/tests/test_variant_unique_shared.py new file mode 100644 index 000000000..84dc2491b --- /dev/null +++ b/tests/test_variant_unique_shared.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +import pytest + +from pybind11_tests import variant_unique_shared as m + + +def test_default_constructed(): + v = m.vptr_holder_double() + assert v.ownership_type() == 0 + assert v.get_value() == -1 + + +def test_from_raw(): + v = m.from_raw() + assert v.ownership_type() == 0 + assert v.get_value() == 3 + + +def test_from_unique(): + v = m.from_unique() + assert v.ownership_type() == 0 + assert v.get_value() == 5 + + +def test_from_shared(): + v = m.from_shared() + assert v.ownership_type() == 1 + assert v.get_value() == 7 + + +def test_promotion_to_shared(): + v = m.from_raw() + v.get_unique() + assert v.ownership_type() == 0 + v.get_shared() # Promotion to shared_ptr. + assert v.ownership_type() == 1 + v.get_shared() # Existing shared_ptr. + with pytest.raises(RuntimeError) as exc_info: + v.get_unique() + assert str(exc_info.value) == "get_unique failure." + v.get_shared() # Still works. + + +def test_shared_from_birth(): + v = m.from_shared() + assert v.ownership_type() == 1 + with pytest.raises(RuntimeError) as exc_info: + v.get_unique() + assert str(exc_info.value) == "get_unique failure." + v.get_shared() # Still works.