Skip to content

Commit 61859c2

Browse files
authored
Merge pull request #430 from jorisv/topic/add_boost_variant
Add `boost::variant` support
2 parents ebde399 + ca7c933 commit 61859c2

File tree

12 files changed

+240
-0
lines changed

12 files changed

+240
-0
lines changed

.clang-format

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
BasedOnStyle: Google
2+
SortIncludes: false
3+
Standard: Cpp03

.github/workflows/jrl-cmakemodules.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
name: JRL-cmakemodules
22
on: [push,pull_request]
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.ref }}
5+
cancel-in-progress: true
36

47
jobs:
58
with-submodules:

.github/workflows/linux.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
name: Check build on linux
22

33
on: ["push", "pull_request"]
4+
concurrency:
5+
group: ${{ github.workflow }}-${{ github.ref }}
6+
cancel-in-progress: true
47

58
jobs:
69
test:

.github/workflows/macos-linux-conda.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
name: Conda-CI
22

33
on: [push,pull_request]
4+
concurrency:
5+
group: ${{ github.workflow }}-${{ github.ref }}
6+
cancel-in-progress: true
47

58
jobs:
69
eigenpy-conda:

.github/workflows/reloc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
name: Ensure relocatable
22

33
on: [push,pull_request]
4+
concurrency:
5+
group: ${{ github.workflow }}-${{ github.ref }}
6+
cancel-in-progress: true
47

58
jobs:
69
relocatable:

.github/workflows/ros_ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ name: ROS-CI
55

66
# This determines when this workflow is run
77
on: [push, pull_request] # on all pushes and PRs
8+
concurrency:
9+
group: ${{ github.workflow }}-${{ github.ref }}
10+
cancel-in-progress: true
811

912
jobs:
1013
CI:

.github/workflows/windows-conda.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
name: Build Eigenpy on Windows via Conda
22
on: [push,pull_request]
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.ref }}
5+
cancel-in-progress: true
36

47
jobs:
58
build:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88

99
### Added
1010
- Support for `Eigen::SparseMatrix` types ([#426](https://github.com/stack-of-tasks/eigenpy/pull/426))
11+
- Support for `boost::variant` types with `BoostVariantConvertor` ([#430](https://github.com/stack-of-tasks/eigenpy/pull/430))
1112

1213
### Fixed
1314
- Fix the issue of missing exposition of Eigen types with __int64 scalar type ([#426](https://github.com/stack-of-tasks/eigenpy/pull/426))

include/eigenpy/boost-variant.hpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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__

unittest/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ add_lib_unit_test(std_vector)
4545
add_lib_unit_test(std_array)
4646
add_lib_unit_test(std_pair)
4747
add_lib_unit_test(user_struct)
48+
add_lib_unit_test(boost_variant)
4849

4950
function(config_bind_optional tagname opttype)
5051
set(MODNAME bind_optional_${tagname})
@@ -132,6 +133,10 @@ add_python_unit_test("py-user-struct" "unittest/python/test_user_struct.py"
132133
"python;unittest")
133134
set_tests_properties("py-user-struct" PROPERTIES DEPENDS ${PYWRAP})
134135

136+
add_python_unit_test("py-boost-variant" "unittest/python/test_boost_variant.py"
137+
"python;unittest")
138+
set_tests_properties("py-boost-variant" PROPERTIES DEPENDS ${PYWRAP})
139+
135140
add_python_unit_test("py-bind-virtual" "unittest/python/test_bind_virtual.py"
136141
"python;unittest")
137142
set_tests_properties("py-bind-virtual" PROPERTIES DEPENDS ${PYWRAP})

0 commit comments

Comments
 (0)