mirror of
https://github.com/pybind/pybind11.git
synced 2026-03-14 20:27:47 +00:00
fix: add support for const-only smart pointers (#5718)
* add support for const pointers in smart pointers * use c++11 compatible code * add template parameter in test * Make the const-removal clearly visible. This simplifies the production code changes significantly. For background see: https://claude.ai/share/4085d9ab-a859-44cc-bb56-450e472f817a * test without leaks * add namespace for test * rename test * fix test compilation * using namespace test_const_only_smart_ptr; * fix smartptr in test * smaller test body * move test * style: pre-commit fixes --------- Co-authored-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -37,7 +37,7 @@ private:
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(initimpl)
|
||||
|
||||
inline void no_nullptr(void *ptr) {
|
||||
inline void no_nullptr(const void *ptr) {
|
||||
if (!ptr) {
|
||||
throw type_error("pybind11::init(): factory function returned nullptr");
|
||||
}
|
||||
@@ -61,7 +61,7 @@ bool is_alias(Cpp<Class> *ptr) {
|
||||
}
|
||||
// Failing fallback version of the above for a no-alias class (always returns false)
|
||||
template <typename /*Class*/>
|
||||
constexpr bool is_alias(void *) {
|
||||
constexpr bool is_alias(const void *) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,12 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
||||
"is not an alias instance");
|
||||
}
|
||||
|
||||
v_h.value_ptr() = ptr;
|
||||
// Cast away constness to store in void* storage.
|
||||
// The value_and_holder storage is fundamentally untyped (void**), so we lose
|
||||
// const-correctness here by design. The const qualifier will be restored
|
||||
// when the pointer is later retrieved and cast back to the original type.
|
||||
// This explicit const_cast makes the const-removal clearly visible.
|
||||
v_h.value_ptr() = const_cast<void *>(static_cast<const void *>(ptr));
|
||||
v_h.type->init_instance(v_h.inst, &holder);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,20 @@ public:
|
||||
T **operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
|
||||
};
|
||||
|
||||
// Simple custom holder that imitates smart pointer, that always stores cpointer to const
|
||||
template <class T>
|
||||
class const_only_shared_ptr {
|
||||
std::shared_ptr<const T> ptr_;
|
||||
|
||||
public:
|
||||
const_only_shared_ptr() = default;
|
||||
explicit const_only_shared_ptr(const T *ptr) : ptr_(ptr) {}
|
||||
const T *get() const { return ptr_.get(); }
|
||||
|
||||
private:
|
||||
// for demonstration purpose only, this imitates smart pointer with a const-only pointer
|
||||
};
|
||||
|
||||
// Custom object with builtin reference counting (see 'object.h' for the implementation)
|
||||
class MyObject1 : public Object {
|
||||
public:
|
||||
@@ -283,6 +297,7 @@ struct holder_helper<ref<T>> {
|
||||
|
||||
// Make pybind aware of the ref-counted wrapper type (s):
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true)
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, const_only_shared_ptr<T>, true)
|
||||
// The following is not required anymore for std::shared_ptr, but it should compile without error:
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr<T>)
|
||||
@@ -397,6 +412,11 @@ TEST_SUBMODULE(smart_ptr, m) {
|
||||
m.def("print_myobject2_4",
|
||||
[](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); });
|
||||
|
||||
m.def("make_myobject2_3",
|
||||
[](int val) { return const_only_shared_ptr<MyObject2>(new MyObject2(val)); });
|
||||
m.def("print_myobject2_5",
|
||||
[](const const_only_shared_ptr<MyObject2> &obj) { py::print(obj.get()->toString()); });
|
||||
|
||||
py::class_<MyObject3, std::shared_ptr<MyObject3>>(m, "MyObject3").def(py::init<int>());
|
||||
m.def("make_myobject3_1", []() { return new MyObject3(8); });
|
||||
m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); });
|
||||
|
||||
@@ -350,3 +350,10 @@ def test_move_only_holder_caster_shared_ptr_with_smart_holder_support_enabled():
|
||||
assert (
|
||||
m.return_std_unique_ptr_example_drvd() == "move_only_holder_caster_traits_test"
|
||||
)
|
||||
|
||||
|
||||
def test_const_only_holder(capture):
|
||||
o = m.make_myobject2_3(4)
|
||||
with capture:
|
||||
m.print_myobject2_5(o)
|
||||
assert capture == "MyObject2[4]\n"
|
||||
|
||||
Reference in New Issue
Block a user