diff --git a/include/eigenpy/variant.hpp b/include/eigenpy/variant.hpp index 55c5ef99fee17d858bfe862c047f0950edf9cb41..1b447ae26ffc7aeeaa8cc1f77be78d6faffa9a06 100644 --- a/include/eigenpy/variant.hpp +++ b/include/eigenpy/variant.hpp @@ -98,6 +98,15 @@ struct VariantValueToObject : VariantVisitorType<PyObject*, Variant> { using Base::operator(); }; +template <typename T> +struct is_class_or_union + : std::integral_constant<bool, std::is_class<T>::value || + std::is_union<T>::value> {}; +template <typename T> +struct is_class_or_union_remove_cvref + : is_class_or_union<typename std::remove_cv< + typename std::remove_reference<T>::type>::type> {}; + /// Convert {boost,std}::variant<class...> alternative reference to a Python /// object. This converter return the alternative reference. The code that /// create the reference holder is taken from \see @@ -113,19 +122,15 @@ struct VariantRefToObject : VariantVisitorType<PyObject*, Variant> { } template <typename T, - typename std::enable_if< - std::is_arithmetic<typename std::remove_cv< - typename std::remove_reference<T>::type>::type>::value, - bool>::type = true> + typename std::enable_if<!is_class_or_union_remove_cvref<T>::value, + bool>::type = true> result_type operator()(T t) const { return boost::python::incref(boost::python::object(t).ptr()); } template <typename T, - typename std::enable_if< - !std::is_arithmetic<typename std::remove_cv< - typename std::remove_reference<T>::type>::type>::value, - bool>::type = true> + typename std::enable_if<is_class_or_union_remove_cvref<T>::value, + bool>::type = true> result_type operator()(T& t) const { return boost::python::detail::make_reference_holder::execute(&t); } @@ -180,6 +185,15 @@ struct ReturnInternalVariant : boost::python::return_internal_reference<> { typedef Variant variant_type; typedef details::VariantConverter<variant_type> result_converter; + + template <class ArgumentPackage> + static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) { + // Don't run return_internal_reference postcall on primitive type + if (PyLong_Check(result) || PyBool_Check(result) || PyFloat_Check(result)) { + return result; + } + return boost::python::return_internal_reference<>::postcall(args_, result); + } }; /// Define a defaults converter to convert a {boost,std}::variant alternative to diff --git a/unittest/python/test_variant.py.in b/unittest/python/test_variant.py.in index e3b1eba0070b0a816636d8edb10b764d1ecac0ef..4d49df1bc72d22071b5b2df89f7ccaa3729c3438 100644 --- a/unittest/python/test_variant.py.in +++ b/unittest/python/test_variant.py.in @@ -6,9 +6,11 @@ V2 = variant_module.V2 VariantHolder = variant_module.VariantHolder VariantNoneHolder = variant_module.VariantNoneHolder VariantArithmeticHolder = variant_module.VariantArithmeticHolder +VariantBoolHolder = variant_module.VariantBoolHolder make_variant = variant_module.make_variant make_variant_none = variant_module.make_variant_none make_variant_arithmetic = variant_module.make_variant_arithmetic +make_variant_bool = variant_module.make_variant_bool variant = make_variant() assert isinstance(variant, V1) @@ -60,6 +62,7 @@ assert variant_none_holder.variant.v == 1 v1 = variant_none_holder.variant v1.v = 10 assert variant_none_holder.variant.v == 10 +# TODOĆ make this work # variant_none_holder.variant = None @@ -67,12 +70,27 @@ assert variant_none_holder.variant.v == 10 v_arithmetic = make_variant_arithmetic() assert isinstance(v_arithmetic, int) -variant_arithemtic_holder = VariantArithmeticHolder() -assert isinstance(variant_arithemtic_holder.variant, int) -variant_arithemtic_holder.variant = 2.0 -assert variant_arithemtic_holder.variant == 2 -assert isinstance(variant_arithemtic_holder.variant, float) -# Arithmetic type doesn't support reference -v1 = variant_arithemtic_holder.variant -v1 = 3 -assert variant_arithemtic_holder.variant != v1 +variant_arithmetic_holder = VariantArithmeticHolder() +assert isinstance(variant_arithmetic_holder.variant, int) +variant_arithmetic_holder.variant = 2 +# Raise an exception if return_internal_postcall is called +assert variant_arithmetic_holder.variant == 2 + +variant_arithmetic_holder.variant = 2.0 +assert isinstance(variant_arithmetic_holder.variant, float) +# Raise an exception if return_internal_postcall is called +assert variant_arithmetic_holder.variant == 2.0 + +v_bool = make_variant_bool() +assert isinstance(v_bool, bool) + +variant_bool_holder = VariantBoolHolder() +assert isinstance(variant_bool_holder.variant, bool) +variant_bool_holder.variant = False +# Raise an exception if return_internal_postcall is called +assert not variant_bool_holder.variant + +variant_bool_holder.variant = 2.0 +assert isinstance(variant_bool_holder.variant, float) +# Raise an exception if return_internal_postcall is called +assert variant_bool_holder.variant == 2.0 diff --git a/unittest/variant.cpp.in b/unittest/variant.cpp.in index 3a99cae3fbba2ad6b7b8c76de1f24622a17c05b6..2f8d8dbd01be97044d81dfc065cf74160b2f0275 100644 --- a/unittest/variant.cpp.in +++ b/unittest/variant.cpp.in @@ -36,12 +36,17 @@ typedef typename MyVariantNoneHelper<VARIANT<V1> >::type MyVariantNone; typedef VARIANT<int, double> MyVariantArithmetic; +// There is a conversion conflict between int and bool +typedef VARIANT<bool, double> MyVariantBool; + MyVariant make_variant() { return V1(); } MyVariantNone make_variant_none() { return MyVariantNone(); } MyVariantArithmetic make_variant_arithmetic() { return MyVariantArithmetic(); } +MyVariantBool make_variant_bool() { return MyVariantBool(); } + struct VariantHolder { MyVariant variant; }; @@ -54,6 +59,10 @@ struct VariantArithmeticHolder { MyVariantArithmetic variant; }; +struct VariantBoolHolder { + MyVariantBool variant; +}; + BOOST_PYTHON_MODULE(@MODNAME@) { using namespace eigenpy; @@ -94,4 +103,16 @@ BOOST_PYTHON_MODULE(@MODNAME@) { bp::make_getter(&VariantArithmeticHolder::variant, ConverterArithmetic::return_internal_reference()), bp::make_setter(&VariantArithmeticHolder::variant)); + + typedef eigenpy::VariantConverter<MyVariantBool> ConverterBool; + ConverterBool::registration(); + bp::def("make_variant_bool", make_variant_bool); + + boost::python::class_<VariantBoolHolder>("VariantBoolHolder", + bp::init<>()) + .add_property( + "variant", + bp::make_getter(&VariantBoolHolder::variant, + ConverterBool::return_internal_reference()), + bp::make_setter(&VariantBoolHolder::variant)); }