Skip to content

Commit 6f713be

Browse files
committed
core: Fix None to variant with blank conversion
1 parent 3c32a6e commit 6f713be

File tree

2 files changed

+76
-20
lines changed

2 files changed

+76
-20
lines changed

include/eigenpy/variant.hpp

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,38 @@ struct VariantVisitorType {};
3030
template <typename Variant>
3131
struct VariantAlternatives {};
3232

33+
template <typename Variant>
34+
struct empty_variant {};
35+
36+
template <typename T>
37+
struct is_empty_variant : std::false_type {};
38+
39+
/// Convert None to a {boost,std}::variant with boost::blank or std::monostate
40+
/// value
41+
template <typename Variant>
42+
struct EmptyConvertible {
43+
static void registration() {
44+
bp::converter::registry::push_back(convertible, construct,
45+
bp::type_id<Variant>());
46+
}
47+
48+
// convertible only for None
49+
static void* convertible(PyObject* obj) {
50+
return (obj == Py_None) ? obj : nullptr;
51+
};
52+
53+
// construct in place
54+
static void construct(PyObject*,
55+
bp::converter::rvalue_from_python_stage1_data* data) {
56+
void* storage =
57+
reinterpret_cast<bp::converter::rvalue_from_python_storage<Variant>*>(
58+
data)
59+
->storage.bytes;
60+
new (storage) Variant(typename empty_variant<Variant>::type());
61+
data->convertible = storage;
62+
};
63+
};
64+
3365
#ifdef EIGENPY_WITH_CXX17_SUPPORT
3466

3567
/// std::variant implementation
@@ -45,7 +77,7 @@ struct VariantVisitorType<ResultType, std::variant<Alternatives...> > {
4577
}
4678

4779
result_type operator()(std::monostate) const {
48-
return boost::python::incref(boost::python::object().ptr()); // None
80+
return bp::incref(bp::object().ptr()); // None
4981
}
5082
};
5183

@@ -54,6 +86,14 @@ struct VariantAlternatives<std::variant<Alternatives...> > {
5486
typedef boost::mpl::vector<Alternatives...> types;
5587
};
5688

89+
template <typename... Alternatives>
90+
struct empty_variant<std::variant<Alternatives...> > {
91+
typedef std::monostate type;
92+
};
93+
94+
template <>
95+
struct is_empty_variant<std::monostate> : std::true_type {};
96+
5797
#endif
5898

5999
/// boost::variant implementation
@@ -69,7 +109,7 @@ struct VariantVisitorType<ResultType, boost::variant<Alternatives...> >
69109
}
70110

71111
result_type operator()(boost::blank) const {
72-
return boost::python::incref(boost::python::object().ptr()); // None
112+
return bp::incref(bp::object().ptr()); // None
73113
}
74114
};
75115

@@ -78,6 +118,14 @@ struct VariantAlternatives<boost::variant<Alternatives...> > {
78118
typedef typename boost::variant<Alternatives...>::types types;
79119
};
80120

121+
template <typename... Alternatives>
122+
struct empty_variant<boost::variant<Alternatives...> > {
123+
typedef boost::blank type;
124+
};
125+
126+
template <>
127+
struct is_empty_variant<boost::blank> : std::true_type {};
128+
81129
/// Convert {boost,std}::variant<class...> alternative to a Python object.
82130
/// This converter copy the alternative.
83131
template <typename Variant>
@@ -92,16 +140,19 @@ struct VariantValueToObject : VariantVisitorType<PyObject*, Variant> {
92140

93141
template <typename T>
94142
result_type operator()(T& t) const {
95-
return boost::python::incref(boost::python::object(t).ptr());
143+
return bp::incref(bp::object(t).ptr());
96144
}
97145

98146
using Base::operator();
99147
};
100148

149+
/// Trait to detect if T is a class or an union
101150
template <typename T>
102151
struct is_class_or_union
103152
: std::integral_constant<bool, std::is_class<T>::value ||
104153
std::is_union<T>::value> {};
154+
155+
/// Trait to remove cvref and call is_class_or_union
105156
template <typename T>
106157
struct is_class_or_union_remove_cvref
107158
: is_class_or_union<typename std::remove_cv<
@@ -110,7 +161,7 @@ struct is_class_or_union_remove_cvref
110161
/// Convert {boost,std}::variant<class...> alternative reference to a Python
111162
/// object. This converter return the alternative reference. The code that
112163
/// create the reference holder is taken from \see
113-
/// boost::python::to_python_indirect.
164+
/// bp::to_python_indirect.
114165
template <typename Variant>
115166
struct VariantRefToObject : VariantVisitorType<PyObject*, Variant> {
116167
typedef VariantVisitorType<PyObject*, Variant> Base;
@@ -125,22 +176,22 @@ struct VariantRefToObject : VariantVisitorType<PyObject*, Variant> {
125176
typename std::enable_if<!is_class_or_union_remove_cvref<T>::value,
126177
bool>::type = true>
127178
result_type operator()(T t) const {
128-
return boost::python::incref(boost::python::object(t).ptr());
179+
return bp::incref(bp::object(t).ptr());
129180
}
130181

131182
template <typename T,
132183
typename std::enable_if<is_class_or_union_remove_cvref<T>::value,
133184
bool>::type = true>
134185
result_type operator()(T& t) const {
135-
return boost::python::detail::make_reference_holder::execute(&t);
186+
return bp::detail::make_reference_holder::execute(&t);
136187
}
137188

138189
/// Copy the object when it's None
139190
using Base::operator();
140191
};
141192

142193
/// Converter used in \see ReturnInternalVariant.
143-
/// This is inspired by \see boost::python::reference_existing_object.
194+
/// This is inspired by \see bp::reference_existing_object.
144195
/// It will call \see VariantRefToObject to extract the alternative
145196
/// reference.
146197
template <typename Variant>
@@ -156,8 +207,7 @@ struct VariantConverter {
156207

157208
#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
158209
PyTypeObject const* get_pytype() const {
159-
return boost::python::converter::registered_pytype<
160-
variant_type>::get_pytype();
210+
return bp::converter::registered_pytype<variant_type>::get_pytype();
161211
}
162212
#endif
163213
};
@@ -169,19 +219,26 @@ template <typename Variant>
169219
struct VariantImplicitlyConvertible {
170220
typedef Variant variant_type;
171221

172-
template <class T>
222+
template <class T, typename std::enable_if<is_empty_variant<T>::value,
223+
bool>::type = true>
224+
void operator()(T) {
225+
EmptyConvertible<variant_type>::registration();
226+
}
227+
228+
template <class T, typename std::enable_if<!is_empty_variant<T>::value,
229+
bool>::type = true>
173230
void operator()(T) {
174-
boost::python::implicitly_convertible<T, variant_type>();
231+
bp::implicitly_convertible<T, variant_type>();
175232
}
176233
};
177234

178235
} // namespace details
179236

180-
/// Variant of \see boost::python::return_internal_reference that
237+
/// Variant of \see bp::return_internal_reference that
181238
/// extract {boost,std}::variant<class...> alternative reference before
182239
/// converting it into a PyObject
183240
template <typename Variant>
184-
struct ReturnInternalVariant : boost::python::return_internal_reference<> {
241+
struct ReturnInternalVariant : bp::return_internal_reference<> {
185242
typedef Variant variant_type;
186243

187244
typedef details::VariantConverter<variant_type> result_converter;
@@ -192,7 +249,7 @@ struct ReturnInternalVariant : boost::python::return_internal_reference<> {
192249
if (PyLong_Check(result) || PyBool_Check(result) || PyFloat_Check(result)) {
193250
return result;
194251
}
195-
return boost::python::return_internal_reference<>::postcall(args_, result);
252+
return bp::return_internal_reference<>::postcall(args_, result);
196253
}
197254
};
198255

@@ -208,12 +265,12 @@ struct ReturnInternalVariant : boost::python::return_internal_reference<> {
208265
/// };
209266
/// ...
210267
/// void expose() {
211-
/// boost::python::class_<Struct1>("Struct1", bp::init<>());
212-
/// boost::python::class_<Struct2>("Struct1", bp::init<>())
268+
/// bp::class_<Struct1>("Struct1", bp::init<>());
269+
/// bp::class_<Struct2>("Struct1", bp::init<>())
213270
/// typedef eigenpy::VariantConverter<MyVariant> Converter;
214271
/// Converter::registration();
215272
///
216-
/// boost::python::class_<VariantHolder>("VariantHolder", bp::init<>())
273+
/// bp::class_<VariantHolder>("VariantHolder", bp::init<>())
217274
/// .add_property("variant",
218275
/// bp::make_getter(&VariantHolder::variant,
219276
/// Converter::return_internal_reference()),
@@ -228,7 +285,7 @@ struct VariantConverter {
228285
typedef details::VariantValueToObject<variant_type> variant_to_value;
229286
typedef typename details::VariantAlternatives<variant_type>::types types;
230287

231-
boost::python::to_python_converter<variant_type, variant_to_value>();
288+
bp::to_python_converter<variant_type, variant_to_value>();
232289
boost::mpl::for_each<types>(
233290
details::VariantImplicitlyConvertible<variant_type>());
234291
}

unittest/python/test_variant.py.in

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ assert variant_none_holder.variant.v == 1
6262
v1 = variant_none_holder.variant
6363
v1.v = 10
6464
assert variant_none_holder.variant.v == 10
65-
# TODO make this work
66-
# variant_none_holder.variant = None
65+
variant_none_holder.variant = None
6766

6867

6968
# Test variant that hold base type

0 commit comments

Comments
 (0)