-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
wrap_pybind.h
180 lines (155 loc) · 6.5 KB
/
wrap_pybind.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/// @file
/// Defines convenience utilities to wrap pybind11 methods and classes.
#pragma once
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "pybind11/pybind11.h"
#include "drake/bindings/pydrake/common/wrap_function.h"
#include "drake/bindings/pydrake/pydrake_pybind.h"
#include "drake/common/copyable_unique_ptr.h"
#include "drake/common/drake_copyable.h"
namespace drake {
namespace pydrake {
#ifndef DRAKE_DOXYGEN_CXX
namespace internal {
// Determines if a type will go through pybind11's generic caster. This
// implies that the type has been declared using `py::class_`, and can have
// a reference passed through. Otherwise, the type uses type-conversion:
// https://pybind11.readthedocs.io/en/stable/advanced/cast/index.html
template <typename T>
constexpr inline bool is_generic_pybind_v =
std::is_base_of_v<py::detail::type_caster_generic,
py::detail::make_caster<T>>;
template <typename T, typename = void>
struct wrap_ref_ptr : public wrap_arg_default<T> {};
template <typename T>
struct wrap_ref_ptr<T&, std::enable_if_t<is_generic_pybind_v<T>>> {
// NOLINTNEXTLINE[runtime/references]: Intentional.
static T* wrap(T& arg) { return &arg; }
static T& unwrap(T* arg_wrapped) { return *arg_wrapped; }
};
template <typename T, typename = void>
struct wrap_callback : public wrap_arg_default<T> {};
template <typename Signature>
struct wrap_callback<const std::function<Signature>&>
: public wrap_arg_function<wrap_ref_ptr, Signature> {};
template <typename Signature>
struct wrap_callback<std::function<Signature>>
: public wrap_callback<const std::function<Signature>&> {};
// Implements a `py::detail::type_caster<>` specialization used to convert
// types using a specific wrapping policy.
// @tparam Wrapper
// Struct which must provide `Type`, `WrappedType`, `unwrap`, `wrap`,
// `wrapped_name`, and `original_name`.
// Fails-fast at runtime if there is an attempt to pass by reference.
template <typename Wrapper>
struct type_caster_wrapped {
using Type = typename Wrapper::Type;
using WrappedType = typename Wrapper::WrappedType;
using WrappedTypeCaster = py::detail::type_caster<WrappedType>;
// Python to C++.
bool load(py::handle src, bool converter) {
WrappedTypeCaster caster;
if (!caster.load(src, converter)) {
return false;
}
value_ = Wrapper::unwrap(caster.operator WrappedType&());
loaded_ = true;
return true;
}
// See `pybind11/eigen.h`, `type_caster<>` implementations.
// N.B. Do not use `PYBIND11_TYPE_CASTER(...)` so we can avoid casting
// garbage values.
operator Type&() {
if (!loaded_) {
throw py::cast_error("Internal error: value not loaded?");
}
return value_;
}
template <typename T>
using cast_op_type = py::detail::movable_cast_op_type<T>;
static constexpr auto name = Wrapper::wrapped_name;
// C++ to Python.
template <typename TType>
static py::handle cast(
TType&& src, py::return_value_policy policy, py::handle parent) {
if (policy == py::return_value_policy::reference ||
policy == py::return_value_policy::reference_internal) {
// N.B. We must declare a local `static constexpr` here to prevent
// linking errors. This does not appear achievable with
// `constexpr char[]`, so we use `py::detail::descr`.
// See `pybind11/pybind11.h`, `cpp_function::initialize(...)` for an
// example.
static constexpr auto original_name = Wrapper::original_name;
throw py::cast_error(
std::string("Can only pass ") + original_name.text + " by value.");
}
return WrappedTypeCaster::cast(
Wrapper::wrap(std::forward<TType>(src)), policy, parent);
}
private:
bool loaded_{false};
Type value_;
};
} // namespace internal
#endif // DRAKE_DOXYGEN_CXX
/// Ensures that any `std::function<>` arguments are wrapped such that any `T&`
/// (which can infer for `T = const U`) is wrapped as `U*` (and conversely
/// unwrapped when returned).
/// Use this when you have a callback in C++ that has a lvalue reference (const
/// or mutable) to a C++ argument or return value.
/// Otherwise, `pybind11` may try and copy the object, will be bad if either
/// the type is a non-copyable or if you are trying to mutate the object; in
/// this case, the copy is mutated, but not the original you care about.
/// For more information, see: https://github.com/pybind/pybind11/issues/1241
template <typename Func>
auto WrapCallbacks(Func&& func) {
return WrapFunction<internal::wrap_callback, false>(std::forward<Func>(func));
}
/// Idempotent to pybind11's `def_readwrite()`, with the exception that the
/// setter is protected with keep_alive on a `member` variable that is a bare
/// pointer. Should not be used for unique_ptr members.
///
/// @tparam PyClass the python class.
/// @tparam Class the C++ class.
/// @tparam T type for the member we wish to apply keep alive semantics.
template <typename PyClass, typename Class, typename T>
void DefReadWriteKeepAlive(
PyClass* cls, const char* name, T Class::*member, const char* doc = "") {
auto getter = [member](const Class* obj) { return obj->*member; };
auto setter = [member](Class* obj, const T& value) { obj->*member = value; };
cls->def_property(name, // BR
py::cpp_function(getter),
py::cpp_function(setter,
// Keep alive, reference: `self` keeps `value` alive.
py::keep_alive<1, 2>()),
doc);
}
/// Idempotent to pybind11's `def_readonly()`, which works for unique_ptr
/// elements; the getter is protected with keep_alive on a `member` variable
/// that is a unique_ptr.
///
/// @tparam PyClass the python class.
/// @tparam Class the C++ class.
/// @tparam T type for the member we wish to apply keep alive semantics.
template <typename PyClass, typename Class, typename T>
void DefReadUniquePtr(PyClass* cls, const char* name,
const std::unique_ptr<T> Class::*member, const char* doc = "") {
auto getter = py::cpp_function(
[member](const Class* obj) { return (obj->*member).get(); },
py_rvp::reference_internal);
cls->def_property_readonly(name, getter, doc);
}
// Variant of DefReadUniquePtr() for copyable_unique_ptr.
template <typename PyClass, typename Class, typename T>
void DefReadUniquePtr(PyClass* cls, const char* name,
const copyable_unique_ptr<T> Class::*member, const char* doc = "") {
auto getter = py::cpp_function(
[member](const Class* obj) { return (obj->*member).get(); },
py_rvp::reference_internal);
cls->def_property_readonly(name, getter, doc);
}
} // namespace pydrake
} // namespace drake