|
| 1 | +// |
| 2 | +// Copyright (c) 2024 INRIA |
| 3 | +// |
| 4 | + |
| 5 | +#ifndef __eigenpy_utils_boost_variant_hpp__ |
| 6 | +#define __eigenpy_utils_boost_variant_hpp__ |
| 7 | + |
| 8 | +#include <boost/python.hpp> |
| 9 | +#include <boost/variant.hpp> |
| 10 | +#include <boost/mpl/for_each.hpp> |
| 11 | + |
| 12 | +namespace eigenpy { |
| 13 | + |
| 14 | +namespace details { |
| 15 | + |
| 16 | +/// Convert boost::variant<class...> alternative to a Python object. |
| 17 | +/// This converter copy the alternative. |
| 18 | +template <typename Variant> |
| 19 | +struct BoostVariantValueToObject : boost::static_visitor<PyObject*> { |
| 20 | + typedef Variant variant_type; |
| 21 | + |
| 22 | + static result_type convert(const variant_type& gm) { |
| 23 | + return apply_visitor(BoostVariantValueToObject(), gm); |
| 24 | + } |
| 25 | + |
| 26 | + template <typename T> |
| 27 | + result_type operator()(T& t) const { |
| 28 | + return boost::python::incref(boost::python::object(t).ptr()); |
| 29 | + } |
| 30 | +}; |
| 31 | + |
| 32 | +/// Convert boost::variant<class...> alternative reference to a Python object. |
| 33 | +/// This converter return the alternative reference. |
| 34 | +/// The code that create the reference holder is taken from |
| 35 | +/// \see boost::python::to_python_indirect. |
| 36 | +template <typename Variant> |
| 37 | +struct BoostVariantRefToObject : boost::static_visitor<PyObject*> { |
| 38 | + typedef Variant variant_type; |
| 39 | + |
| 40 | + static result_type convert(const variant_type& gm) { |
| 41 | + return apply_visitor(BoostVariantRefToObject(), gm); |
| 42 | + } |
| 43 | + |
| 44 | + template <typename T> |
| 45 | + result_type operator()(T& t) const { |
| 46 | + return boost::python::detail::make_reference_holder::execute(&t); |
| 47 | + } |
| 48 | +}; |
| 49 | + |
| 50 | +/// Converter used in \see ReturnInternalBoostVariant. |
| 51 | +/// This is inspired by \see boost::python::reference_existing_object. |
| 52 | +/// It will call \see BoostVariantRefToObject to extract the alternative |
| 53 | +/// reference. |
| 54 | +template <typename Variant> |
| 55 | +struct BoostVariantConverter { |
| 56 | + typedef Variant variant_type; |
| 57 | + |
| 58 | + template <class T> |
| 59 | + struct apply { |
| 60 | + struct type { |
| 61 | + PyObject* operator()(const variant_type& gm) const { |
| 62 | + return BoostVariantRefToObject<variant_type>::convert(gm); |
| 63 | + } |
| 64 | + |
| 65 | +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES |
| 66 | + PyTypeObject const* get_pytype() const { |
| 67 | + return boost::python::converter::registered_pytype< |
| 68 | + variant_type>::get_pytype(); |
| 69 | + } |
| 70 | +#endif |
| 71 | + }; |
| 72 | + }; |
| 73 | +}; |
| 74 | + |
| 75 | +/// Declare a variant alternative implicitly convertible to the variant |
| 76 | +template <typename Variant> |
| 77 | +struct BoostVariantImplicitlyConvertible { |
| 78 | + typedef Variant variant_type; |
| 79 | + |
| 80 | + template <class T> |
| 81 | + void operator()(T) { |
| 82 | + boost::python::implicitly_convertible<T, variant_type>(); |
| 83 | + } |
| 84 | +}; |
| 85 | + |
| 86 | +} // namespace details |
| 87 | + |
| 88 | +/// Variant of \see boost::python::return_internal_reference that |
| 89 | +/// extract boost::variant<class...> alternative reference before |
| 90 | +/// converting it into a PyObject |
| 91 | +template <typename Variant> |
| 92 | +struct ReturnInternalBoostVariant : boost::python::return_internal_reference<> { |
| 93 | + typedef Variant variant_type; |
| 94 | + |
| 95 | + typedef details::BoostVariantConverter<variant_type> result_converter; |
| 96 | +}; |
| 97 | + |
| 98 | +/// Define a defaults converter to convert a boost::variant alternative to a |
| 99 | +/// Python object by copy and to convert implicitly an alternative to a |
| 100 | +/// boost::variant. |
| 101 | +/// |
| 102 | +/// Example: |
| 103 | +/// |
| 104 | +/// typedef boost::variant<Struct1, Struct2> MyVariant; |
| 105 | +/// struct VariantHolder { |
| 106 | +/// MyVariant variant; |
| 107 | +/// }; |
| 108 | +/// ... |
| 109 | +/// void expose() { |
| 110 | +/// boost::python::class_<Struct1>("Struct1", bp::init<>()); |
| 111 | +/// boost::python::class_<Struct2>("Struct1", bp::init<>()) |
| 112 | +/// typedef eigenpy::BoostVariantConvertor<MyVariant> Convertor; |
| 113 | +/// Convertor::registration(); |
| 114 | +/// |
| 115 | +/// boost::python::class_<VariantHolder>("VariantHolder", bp::init<>()) |
| 116 | +/// .add_property("variant", |
| 117 | +/// bp::make_getter(&VariantHolder::variant, |
| 118 | +/// Convertor::return_internal_reference()), |
| 119 | +/// bp::make_setter(&VariantHolder::variant)); |
| 120 | +/// } |
| 121 | +template <typename Variant> |
| 122 | +struct BoostVariantConvertor { |
| 123 | + typedef Variant variant_type; |
| 124 | + typedef ReturnInternalBoostVariant<variant_type> return_internal_reference; |
| 125 | + |
| 126 | + static void registration() { |
| 127 | + typedef details::BoostVariantValueToObject<variant_type> variant_to_value; |
| 128 | + boost::python::to_python_converter<variant_type, variant_to_value>(); |
| 129 | + boost::mpl::for_each<typename variant_type::types>( |
| 130 | + details::BoostVariantImplicitlyConvertible<variant_type>()); |
| 131 | + } |
| 132 | +}; |
| 133 | + |
| 134 | +} // namespace eigenpy |
| 135 | + |
| 136 | +#endif // ifndef __eigenpy_utils_boost_variant_hpp__ |
0 commit comments