RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
boost_shared_ptr.h
Go to the documentation of this file.
1/*
2 RDBoost/boost_shared_ptr.h: Type caster for boost::shared_ptr<T>
3 adapted from nanobind's type caster for std::shared_ptr<T>
4 https://github.com/wjakob/nanobind/blob/master/include/nanobind/stl/shared_ptr.h
5
6 Copyright (c) 2022 Wenzel Jakob
7
8 All rights reserved. Use of this source code is governed by a
9 BSD-style license that can be found in the LICENSE file.
10*/
11
12#pragma once
13
14#include <nanobind/nanobind.h>
15#include <boost/shared_ptr.hpp>
16
17NAMESPACE_BEGIN(NB_NAMESPACE)
18NAMESPACE_BEGIN(detail)
19
20// shared_ptr deleter that reduces the reference count of a Python object
22 void operator()(void *) noexcept {
23 // Don't run the deleter if the interpreter has been shut down
24 if (!is_alive()) return;
25 gil_scoped_acquire guard;
26 Py_DECREF(o);
27 }
28
29 PyObject *o;
30};
31
32/**
33 * Create a std::shared_ptr for `ptr` that owns a reference to the Python
34 * object `h`; if `ptr` is non-null, then the refcount of `h` is incremented
35 * before creating the shared_ptr and decremented by its deleter.
36 *
37 * Usually this is instantiated with T = void, to reduce template bloat.
38 * But if the pointee type uses enable_shared_from_this, we instantiate
39 * with T = that type, in order to allow its internal weak_ptr to share
40 * ownership with the shared_ptr we're creating.
41 *
42 * The next two functions are simultaneously marked as 'inline' (to avoid
43 * linker errors) and 'NB_NOINLINE' (to avoid them being inlined into every
44 * single shared_ptr type_caster, which would enlarge the binding size)
45 */
46template <typename T>
47inline NB_NOINLINE boost::shared_ptr<T> boost_shared_from_python(
48 T *ptr, handle h) noexcept {
49 if (ptr)
50 return boost::shared_ptr<T>(ptr, boost_py_deleter{h.inc_ref().ptr()});
51 else
52 return boost::shared_ptr<T>(nullptr);
53}
54
55inline NB_NOINLINE void boost_shared_from_cpp(boost::shared_ptr<void> &&ptr,
56 PyObject *o) noexcept {
57 keep_alive(o, new boost::shared_ptr<void>(std::move(ptr)),
58 [](void *p) noexcept { delete (boost::shared_ptr<void> *)p; });
59}
60
61template <typename T>
62struct type_caster<boost::shared_ptr<T>> {
63 static constexpr bool IsClass = true;
64 using Caster = make_caster<T>;
65 using Td = std::decay_t<T>;
66
67 NB_TYPE_CASTER(boost::shared_ptr<T>, Caster::Name)
68
69 static_assert(is_base_caster_v<Caster>,
70 "Conversion of ``shared_ptr<T>`` requires that ``T`` is "
71 "handled by nanobind's regular class binding mechanism. "
72 "However, a type caster was registered to intercept this "
73 "particular type, which is not allowed.");
74
75 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
76 Caster caster;
77 if (!caster.from_python(src, flags, cleanup)) return false;
78
79 Td *ptr = caster.operator Td *();
80 if constexpr (has_shared_from_this_v<T>) {
81 if (ptr) {
82 if (auto sp = ptr->weak_from_this().lock()) {
83 // There is already a C++ shared_ptr for this object. Use it.
84 value = boost::static_pointer_cast<T>(std::move(sp));
85 return true;
86 }
87 }
88 // Otherwise create a new one. Use boost_shared_from_python<T>(...)
89 // so that future calls to ptr->shared_from_this() can share
90 // ownership with it.
91 value = boost_shared_from_python(ptr, src);
92 } else {
93 value = boost::static_pointer_cast<T>(
94 boost_shared_from_python(static_cast<void *>(ptr), src));
95 }
96 return true;
97 }
98
99 static handle from_cpp(const Value &value, rv_policy,
100 cleanup_list *cleanup) noexcept {
101 bool is_new = false;
102 handle result;
103
104 Td *ptr = (Td *)value.get();
105 const std::type_info *type = &typeid(Td);
106
107 constexpr bool has_type_hook =
108 !std::is_base_of_v<std::false_type, type_hook<Td>>;
109 if constexpr (has_type_hook) type = type_hook<Td>::get(ptr);
110
111 if constexpr (!std::is_polymorphic_v<Td>) {
112 result = nb_type_put(type, ptr, rv_policy::reference, cleanup, &is_new);
113 } else {
114 const std::type_info *type_p =
115 (!has_type_hook && ptr) ? &typeid(*ptr) : nullptr;
116
117 result = nb_type_put_p(type, type_p, ptr, rv_policy::reference, cleanup,
118 &is_new);
119 }
120
121 if (is_new) {
122 boost::shared_ptr<void> pp;
123 if constexpr (std::is_const_v<T>)
124 pp = boost::static_pointer_cast<void>(
125 boost::const_pointer_cast<Td>(value));
126 else
127 pp = boost::static_pointer_cast<void>(value);
128 boost_shared_from_cpp(std::move(pp), result.ptr());
129 }
130
131 return result;
132 }
133};
134
135NAMESPACE_END(detail)
136NAMESPACE_END(NB_NAMESPACE)
NB_NOINLINE void boost_shared_from_cpp(boost::shared_ptr< void > &&ptr, PyObject *o) noexcept
NB_NOINLINE boost::shared_ptr< T > boost_shared_from_python(T *ptr, handle h) noexcept
void operator()(void *) noexcept
static handle from_cpp(const Value &value, rv_policy, cleanup_list *cleanup) noexcept
bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept