Skip to content

Commit

Permalink
Add blank/monostate support
Browse files Browse the repository at this point in the history
  • Loading branch information
jorisv committed Jan 30, 2024
1 parent b758b05 commit 339d70f
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 0 deletions.
13 changes: 13 additions & 0 deletions include/eigenpy/variant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ struct VariantVisitorType<ResultType, std::variant<Alternatives...> > {
return std::visit(std::forward<Visitor>(visitor),
std::forward<Visitable>(v));
}

result_type operator()(std::monostate) const {
return boost::python::incref(boost::python::object().ptr()); // None
}
};

template <typename... Alternatives>
Expand All @@ -61,6 +65,10 @@ struct VariantVisitorType<ResultType, boost::variant<Alternatives...> >
static result_type visit(Visitor&& visitor, Visitable&& visitable) {
return std::forward<Visitable>(visitable).apply_visitor(visitor);
}

result_type operator()(boost::blank) const {
return boost::python::incref(boost::python::object().ptr()); // None
}
};

template <typename... Alternatives>
Expand All @@ -84,6 +92,8 @@ struct VariantValueToObject : VariantVisitorType<PyObject*, Variant> {
result_type operator()(T& t) const {
return boost::python::incref(boost::python::object(t).ptr());
}

using Base::operator();
};

/// Convert {boost,std}::variant<class...> alternative reference to a Python
Expand All @@ -104,6 +114,9 @@ struct VariantRefToObject : VariantVisitorType<PyObject*, Variant> {
result_type operator()(T& t) const {
return boost::python::detail::make_reference_holder::execute(&t);
}

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

/// Converter used in \see ReturnInternalVariant.
Expand Down
22 changes: 22 additions & 0 deletions unittest/python/test_variant.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ variant_module = importlib.import_module("@MODNAME@")
V1 = variant_module.V1
V2 = variant_module.V2
VariantHolder = variant_module.VariantHolder
VariantNoneHolder = variant_module.VariantNoneHolder
make_variant = variant_module.make_variant
make_variant_none = variant_module.make_variant_none

variant = make_variant()
assert isinstance(variant, V1)
Expand Down Expand Up @@ -40,3 +42,23 @@ assert v1.v == 1000
variant_holder.variant = v2
assert isinstance(variant_holder.variant, V2)
assert variant_holder.variant.v == v2.v

# Test variant that hold a None value
v_none = make_variant_none()
assert v_none is None
v_none = 1
assert v_none == 1
v_none = None
assert v_none is None

variant_none_holder = VariantNoneHolder()
v_none = variant_none_holder.variant
assert v_none is None

v1 = V1()
v1.v = 1
variant_none_holder.variant = v1
assert variant_none_holder.variant.v == 1
v1 = variant_none_holder.variant
v1.v = 10
assert variant_none_holder.variant.v == 10
33 changes: 33 additions & 0 deletions unittest/variant.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,35 @@ struct V2 {
};
typedef VARIANT<V1, V2> MyVariant;

template <typename Variant>
struct MyVariantNoneHelper {};

template <typename... Alternatives>
struct MyVariantNoneHelper<boost::variant<Alternatives...> > {
typedef VARIANT<boost::blank, Alternatives...> type;
};

#ifdef EIGENPY_WITH_CXX17_SUPPORT
template <typename... Alternatives>
struct MyVariantNoneHelper<std::variant<Alternatives...> > {
typedef VARIANT<std::monostate, Alternatives...> type;
};
#endif

typedef typename MyVariantNoneHelper<VARIANT<V1> >::type MyVariantNone;

MyVariant make_variant() { return V1(); }

MyVariantNone make_variant_none() { return MyVariantNone(); }

struct VariantHolder {
MyVariant variant;
};

struct VariantNoneHolder {
MyVariantNone variant;
};

BOOST_PYTHON_MODULE(@MODNAME@) {
using namespace eigenpy;

Expand All @@ -41,4 +64,14 @@ BOOST_PYTHON_MODULE(@MODNAME@) {
bp::make_getter(&VariantHolder::variant,
Converter::return_internal_reference()),
bp::make_setter(&VariantHolder::variant));

typedef eigenpy::VariantConverter<MyVariantNone> ConverterNone;
ConverterNone::registration();
bp::def("make_variant_none", make_variant_none);

boost::python::class_<VariantNoneHolder>("VariantNoneHolder", bp::init<>())
.add_property("variant",
bp::make_getter(&VariantNoneHolder::variant,
ConverterNone::return_internal_reference()),
bp::make_setter(&VariantNoneHolder::variant));
}

0 comments on commit 339d70f

Please sign in to comment.