eigenpy 3.12.0
Bindings between Numpy and Eigen using Boost.Python
Loading...
Searching...
No Matches
variant.hpp
1//
2// Copyright (c) 2024 INRIA
3//
4
5#ifndef __eigenpy_utils_variant_hpp__
6#define __eigenpy_utils_variant_hpp__
7
8#include "eigenpy/fwd.hpp"
9#include "eigenpy/utils/traits.hpp"
10#include "eigenpy/utils/python-compat.hpp"
11
12#include <boost/python.hpp>
13#include <boost/variant.hpp>
14#include <boost/mpl/for_each.hpp>
15#include <boost/mpl/vector.hpp>
16
17#include <type_traits>
18
19#ifdef EIGENPY_WITH_CXX17_SUPPORT
20#include <variant>
21#endif
22
23namespace eigenpy {
24
25namespace details {
26
28template <typename ResultType, typename Variant>
30
32template <typename Variant>
34
35template <typename Variant>
36struct empty_variant {};
37
38template <typename T>
39struct is_empty_variant : std::false_type {};
40
41#ifdef EIGENPY_WITH_CXX17_SUPPORT
42
44template <typename ResultType, typename... Alternatives>
45struct VariantVisitorType<ResultType, std::variant<Alternatives...>> {
46 typedef std::variant<Alternatives...> variant_type;
47 typedef ResultType result_type;
48
49 template <typename Visitor, typename Visitable>
50 static result_type visit(Visitor&& visitor, Visitable&& v) {
51 return std::visit(std::forward<Visitor>(visitor),
52 std::forward<Visitable>(v));
53 }
54
55 result_type operator()(std::monostate) const {
56 return bp::incref(bp::object().ptr()); // None
57 }
58};
59
60template <typename... Alternatives>
61struct VariantAlternatives<std::variant<Alternatives...>> {
62 typedef boost::mpl::vector<Alternatives...> types;
63};
64
65template <typename... Alternatives>
66struct empty_variant<std::variant<Alternatives...>> {
67 typedef std::monostate type;
68};
69
70template <>
71struct is_empty_variant<std::monostate> : std::true_type {};
72
73#endif
74
76template <typename ResultType, typename... Alternatives>
77struct VariantVisitorType<ResultType, boost::variant<Alternatives...>>
78 : boost::static_visitor<ResultType> {
79 typedef boost::variant<Alternatives...> variant_type;
80 typedef ResultType result_type;
81
82 template <typename Visitor, typename Visitable>
83 static result_type visit(Visitor&& visitor, Visitable&& visitable) {
84 return std::forward<Visitable>(visitable).apply_visitor(visitor);
85 }
86
87 result_type operator()(boost::blank) const {
88 return bp::incref(bp::object().ptr()); // None
89 }
90};
91
92template <typename... Alternatives>
93struct VariantAlternatives<boost::variant<Alternatives...>> {
94 typedef typename boost::variant<Alternatives...>::types types;
95};
96
97template <typename... Alternatives>
98struct empty_variant<boost::variant<Alternatives...>> {
99 typedef boost::blank type;
100};
101
102template <>
103struct is_empty_variant<boost::blank> : std::true_type {};
104
107template <typename Variant>
109 static void registration() {
110 bp::converter::registry::push_back(convertible, construct,
111 bp::type_id<Variant>());
112 }
113
114 // convertible only for None
115 static void* convertible(PyObject* obj) {
116 return (obj == Py_None) ? obj : nullptr;
117 };
118
119 // construct in place
120 static void construct(PyObject*,
121 bp::converter::rvalue_from_python_stage1_data* data) {
122 void* storage =
123 reinterpret_cast<bp::converter::rvalue_from_python_storage<Variant>*>(
124 data)
125 ->storage.bytes;
126 new (storage) Variant(typename empty_variant<Variant>::type());
127 data->convertible = storage;
128 };
129};
130
132template <typename T, class Enable = void>
134
135template <typename T>
137 T, typename std::enable_if<std::is_same<T, bool>::value>::type> {
138 static void* convertible(PyObject* obj) {
139 return PyBool_Check(obj) ? obj : nullptr;
140 }
141
142 static PyTypeObject const* expected_pytype() { return &PyBool_Type; }
143};
144
145template <typename T>
147 T, typename std::enable_if<!std::is_same<T, bool>::value &&
148 std::is_integral<T>::value>::type> {
149 static void* convertible(PyObject* obj) {
150 // PyLong return true for bool type
151 return (PyInt_Check(obj) && !PyBool_Check(obj)) ? obj : nullptr;
152 }
153
154 static PyTypeObject const* expected_pytype() { return &PyLong_Type; }
155};
156
157template <typename T>
159 T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
160 static void* convertible(PyObject* obj) {
161 return PyFloat_Check(obj) ? obj : nullptr;
162 }
163
164 static PyTypeObject const* expected_pytype() { return &PyFloat_Type; }
165};
166
168template <typename T, typename Variant>
170 static void registration() {
171 bp::converter::registry::push_back(
172 &convertible, &bp::converter::implicit<T, Variant>::construct,
173 bp::type_id<Variant>()
174#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
175 ,
176 &expected_pytype
177#endif
178 );
179 }
180
181 static void* convertible(PyObject* obj) {
183 }
184 static PyTypeObject const* expected_pytype() {
186 }
187};
188
191template <typename Variant>
192struct VariantValueToObject : VariantVisitorType<PyObject*, Variant> {
194 typedef typename Base::result_type result_type;
195 typedef typename Base::variant_type variant_type;
196
197 static result_type convert(const variant_type& v) {
198 return Base::visit(VariantValueToObject(), v);
199 }
200
201 template <typename T>
202 result_type operator()(T& t) const {
203 return bp::incref(bp::object(t).ptr());
204 }
205
206 using Base::operator();
207};
208
213template <typename Variant>
214struct VariantRefToObject : VariantVisitorType<PyObject*, Variant> {
216 typedef typename Base::result_type result_type;
217 typedef typename Base::variant_type variant_type;
218
219 static result_type convert(const variant_type& v) {
220 return Base::visit(VariantRefToObject(), v);
221 }
222
223 template <typename T,
224 typename std::enable_if<is_python_primitive_type<T>::value,
225 bool>::type = true>
226 result_type operator()(T t) const {
227 return bp::incref(bp::object(t).ptr());
228 }
229
230 template <typename T,
231 typename std::enable_if<!is_python_primitive_type<T>::value,
232 bool>::type = true>
233 result_type operator()(T& t) const {
234 return bp::detail::make_reference_holder::execute(&t);
235 }
236
238 using Base::operator();
239};
240
245template <typename Variant>
247 typedef Variant variant_type;
248
249 template <class T>
250 struct apply {
251 struct type {
252 PyObject* operator()(const variant_type& v) const {
253 return VariantRefToObject<variant_type>::convert(v);
254 }
255
256#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
257 PyTypeObject const* get_pytype() const {
258 return bp::converter::registered_pytype<variant_type>::get_pytype();
259 }
260#endif
261 };
262 };
263};
264
266template <typename Variant>
268 typedef Variant variant_type;
269
270 template <class T, typename std::enable_if<is_empty_variant<T>::value,
271 bool>::type = true>
272 void operator()(T) {
273 EmptyConvertible<variant_type>::registration();
274 }
275
276 template <class T, typename std::enable_if<!is_empty_variant<T>::value &&
277 std::is_arithmetic<T>::value,
278 bool>::type = true>
279 void operator()(T) {
280 NumericConvertible<T, variant_type>::registration();
281 }
282
283 template <class T, typename std::enable_if<!is_empty_variant<T>::value &&
284 !std::is_arithmetic<T>::value,
285 bool>::type = true>
286 void operator()(T) {
287 bp::implicitly_convertible<T, variant_type>();
288 }
289};
290
291} // namespace details
292
296template <typename Variant>
297struct ReturnInternalVariant : bp::return_internal_reference<> {
298 typedef Variant variant_type;
299
300 typedef details::VariantConverter<variant_type> result_converter;
301
302 template <class ArgumentPackage>
303 static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) {
304 // Don't run return_internal_reference postcall on primitive type
305 if (PyInt_Check(result) || PyBool_Check(result) || PyFloat_Check(result) ||
306 PyStr_Check(result) || PyComplex_Check(result)) {
307 return result;
308 }
309 return bp::return_internal_reference<>::postcall(args_, result);
310 }
311};
312
336template <typename Variant>
338 typedef Variant variant_type;
339 typedef ReturnInternalVariant<variant_type> return_internal_reference;
340
341 static void registration() {
342 typedef details::VariantValueToObject<variant_type> variant_to_value;
344
345 bp::to_python_converter<variant_type, variant_to_value>();
346 boost::mpl::for_each<types>(details::VariantConvertible<variant_type>());
347 }
348};
349
350} // namespace eigenpy
351
352#endif // ifndef __eigenpy_utils_variant_hpp__
Implement convertible and expected_pytype for bool, integer and float.
Definition variant.hpp:133
Convert numeric type to Variant without ambiguity.
Definition variant.hpp:169
Allow to get all alternatives in a boost::mpl vector.
Definition variant.hpp:33
Convert an Alternative type to a Variant.
Definition variant.hpp:267
Allow to use std::variant and boost::variant with the same API.
Definition variant.hpp:29