Skip to content
Snippets Groups Projects
Unverified Commit 8ddc41ac authored by Joris Vaillant's avatar Joris Vaillant
Browse files

core: Add BoostVariantConvertor

parent ebde399e
No related branches found
No related tags found
No related merge requests found
//
// Copyright (c) 2024 INRIA
//
#ifndef __eigenpy_utils_boost_variant_hpp__
#define __eigenpy_utils_boost_variant_hpp__
#include <boost/python.hpp>
#include <boost/variant.hpp>
#include <boost/mpl/for_each.hpp>
namespace eigenpy {
namespace details {
/// Convert boost::variant<class...> alternative to a Python object.
/// This converter copy the alternative.
template <typename Variant>
struct BoostVariantValueToObject : boost::static_visitor<PyObject*> {
typedef Variant variant_type;
static result_type convert(const variant_type& gm) {
return apply_visitor(BoostVariantValueToObject(), gm);
}
template <typename T>
result_type operator()(T& t) const {
return boost::python::incref(boost::python::object(t).ptr());
}
};
/// Convert boost::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 boost::python::to_python_indirect.
template <typename Variant>
struct BoostVariantRefToObject : boost::static_visitor<PyObject*> {
typedef Variant variant_type;
static result_type convert(const variant_type& gm) {
return apply_visitor(BoostVariantRefToObject(), gm);
}
template <typename T>
result_type operator()(T& t) const {
return boost::python::detail::make_reference_holder::execute(&t);
}
};
/// Converter used in \see ReturnInternalBoostVariant.
/// This is inspired by \see boost::python::reference_existing_object.
/// It will call \see BoostVariantRefToObject to extract the alternative
/// reference.
template <typename Variant>
struct BoostVariantConverter {
typedef Variant variant_type;
template <class T>
struct apply {
struct type {
PyObject* operator()(const variant_type& gm) const {
return BoostVariantRefToObject<variant_type>::convert(gm);
}
#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
PyTypeObject const* get_pytype() const {
return boost::python::converter::registered_pytype<
variant_type>::get_pytype();
}
#endif
};
};
};
/// Declare a variant alternative implicitly convertible to the variant
template <typename Variant>
struct BoostVariantImplicitlyConvertible {
typedef Variant variant_type;
template <class T>
void operator()(T) {
boost::python::implicitly_convertible<T, variant_type>();
}
};
} // namespace details
/// Variant of \see boost::python::return_internal_reference that
/// extract boost::variant<class...> alternative reference before
/// converting it into a PyObject
template <typename Variant>
struct ReturnInternalBoostVariant : boost::python::return_internal_reference<> {
typedef Variant variant_type;
typedef details::BoostVariantConverter<variant_type> result_converter;
};
/// Define a defaults converter to convert a boost::variant alternative to a
/// Python object by copy and to convert implicitly an alternative to a
/// boost::variant.
///
/// Example:
///
/// typedef boost::variant<Struct1, Struct2> MyVariant;
/// struct VariantHolder {
/// MyVariant variant;
/// };
/// ...
/// void expose() {
/// boost::python::class_<Struct1>("Struct1", bp::init<>());
/// boost::python::class_<Struct2>("Struct1", bp::init<>())
/// typedef eigenpy::BoostVariantConvertor<MyVariant> Convertor;
/// Convertor::registration();
///
/// boost::python::class_<VariantHolder>("VariantHolder", bp::init<>())
/// .add_property("variant",
/// bp::make_getter(&VariantHolder::variant,
/// Convertor::return_internal_reference()),
/// bp::make_setter(&VariantHolder::variant));
/// }
template <typename Variant>
struct BoostVariantConvertor {
typedef Variant variant_type;
typedef ReturnInternalBoostVariant<variant_type> return_internal_reference;
static void registration() {
typedef details::BoostVariantValueToObject<variant_type> variant_to_value;
boost::python::to_python_converter<variant_type, variant_to_value>();
boost::mpl::for_each<typename variant_type::types>(
details::BoostVariantImplicitlyConvertible<variant_type>());
}
};
} // namespace eigenpy
#endif // ifndef __eigenpy_utils_boost_variant_hpp__
......@@ -45,6 +45,7 @@ add_lib_unit_test(std_vector)
add_lib_unit_test(std_array)
add_lib_unit_test(std_pair)
add_lib_unit_test(user_struct)
add_lib_unit_test(boost_variant)
function(config_bind_optional tagname opttype)
set(MODNAME bind_optional_${tagname})
......@@ -132,6 +133,10 @@ add_python_unit_test("py-user-struct" "unittest/python/test_user_struct.py"
"python;unittest")
set_tests_properties("py-user-struct" PROPERTIES DEPENDS ${PYWRAP})
add_python_unit_test("py-boost-variant" "unittest/python/test_boost_variant.py"
"python;unittest")
set_tests_properties("py-boost-variant" PROPERTIES DEPENDS ${PYWRAP})
add_python_unit_test("py-bind-virtual" "unittest/python/test_bind_virtual.py"
"python;unittest")
set_tests_properties("py-bind-virtual" PROPERTIES DEPENDS ${PYWRAP})
/// @file
/// @copyright Copyright 2024 CNRS INRIA
#include <eigenpy/eigenpy.hpp>
#include <eigenpy/boost-variant.hpp>
namespace bp = boost::python;
struct V1 {
int v;
};
struct V2 {
char v;
};
typedef boost::variant<V1, V2> MyVariant;
MyVariant make_variant() { return V1(); }
struct VariantHolder {
MyVariant variant;
};
BOOST_PYTHON_MODULE(boost_variant) {
using namespace eigenpy;
enableEigenPy();
bp::class_<V1>("V1", bp::init<>()).def_readwrite("v", &V1::v);
bp::class_<V2>("V2", bp::init<>()).def_readwrite("v", &V2::v);
typedef eigenpy::BoostVariantConvertor<MyVariant> Convertor;
Convertor::registration();
bp::def("make_variant", make_variant);
boost::python::class_<VariantHolder>("VariantHolder", bp::init<>())
.add_property("variant",
bp::make_getter(&VariantHolder::variant,
Convertor::return_internal_reference()),
bp::make_setter(&VariantHolder::variant));
}
from boost_variant import V1, V2, VariantHolder, make_variant
variant = make_variant()
assert isinstance(variant, V1)
v1 = V1()
v1.v = 10
v2 = V2()
v2.v = "c"
variant_holder = VariantHolder()
variant_holder.variant = v1
assert isinstance(variant_holder.variant, V1)
assert variant_holder.variant == v1.v
variant_holder.variant = 100
assert variant_holder.variant == 100
assert v1 == 100
v1 = 1000
assert variant_holder.variant == 1000
assert v1 == 1000
variant_holder.variant = v2
assert isinstance(variant_holder.variant, V1)
assert variant_holder.variant == v2.v
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment