diff --git a/.github/workflows/macos-linux-conda.yml b/.github/workflows/macos-linux-conda.yml index edc1784413efb81fcd2f91bd24206acf329e5810..3f4f23f66d2b9157034da0f5a7e47b980ab5a1bd 100644 --- a/.github/workflows/macos-linux-conda.yml +++ b/.github/workflows/macos-linux-conda.yml @@ -40,7 +40,7 @@ jobs: with: path: .ccache key: ccache-macos-linux-conda-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.cxx_options }}-${{ matrix.python-version }}-${{ github.sha }} - restore-keys: ccache-macos-linux-conda-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.python-version }}-${{ matrix.cxx_options }}-${{ matrix.python-version }}- + restore-keys: ccache-macos-linux-conda-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.cxx_options }}-${{ matrix.python-version }}- - uses: conda-incubator/setup-miniconda@v3 with: diff --git a/.github/workflows/windows-conda.yml b/.github/workflows/windows-conda.yml index 74d923fd74bf33f73345c07c5468bcdc956d6c43..b3f8ad8150fbe057c7ba2741c63b3537b556078c 100644 --- a/.github/workflows/windows-conda.yml +++ b/.github/workflows/windows-conda.yml @@ -34,8 +34,8 @@ jobs: - uses: actions/cache@v3 with: path: .ccache - key: ccache-windows-conda-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.cxx_options }}-${{ matrix.python-version }}-${{ github.sha }} - restore-keys: ccache-windows-conda-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.cxx_options }}-${{ matrix.python-version }}- + key: ccache-windows-conda-${{ matrix.os }}-${{ github.sha }} + restore-keys: ccache-windows-conda-${{ matrix.os }}- - name: Build Eigenpy shell: cmd /C CALL {0} diff --git a/CHANGELOG.md b/CHANGELOG.md index e568f1ae611aac1e63c96c4542119097ae6900eb..8c936ecb648c4db7910c4a98f7fd66fb5b87b59a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Support for `Eigen::SparseMatrix` types ([#426](https://github.com/stack-of-tasks/eigenpy/pull/426)) - Support for `boost::variant` types with `VariantConverter` ([#430](https://github.com/stack-of-tasks/eigenpy/pull/430)) - Support for `std::variant` types with `VariantConverter` ([#431](https://github.com/stack-of-tasks/eigenpy/pull/431)) +- Support for `std::unique_ptr` as a return types with `StdUniquePtrCallPolicies` and `boost::python::default_call_policies` ([#433](https://github.com/stack-of-tasks/eigenpy/pull/433)) +- Support for `std::unique_ptr` as an internal reference with `ReturnInternalStdUniquePtr` ([#433](https://github.com/stack-of-tasks/eigenpy/pull/433)) ### Fixed - Fix the issue of missing exposition of Eigen types with __int64 scalar type ([#426](https://github.com/stack-of-tasks/eigenpy/pull/426)) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8d8a79e61072d0e53893512824df798a89ffe74..e48c9105e4e3ce848ebb3f29ec899b3ce96876ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,8 @@ search_for_boost_python(REQUIRED) # ---------------------------------------------------- set(${PROJECT_NAME}_UTILS_HEADERS include/eigenpy/utils/scalar-name.hpp include/eigenpy/utils/is-approx.hpp - include/eigenpy/utils/is-aligned.hpp) + include/eigenpy/utils/is-aligned.hpp include/eigenpy/utils/traits.hpp + include/eigenpy/utils/python-compat.hpp) set(${PROJECT_NAME}_SOLVERS_HEADERS include/eigenpy/solvers/solvers.hpp @@ -167,6 +168,7 @@ set(${PROJECT_NAME}_HEADERS include/eigenpy/scipy-allocator.hpp include/eigenpy/scipy-type.hpp include/eigenpy/variant.hpp + include/eigenpy/std-unique-ptr.hpp include/eigenpy/swig.hpp include/eigenpy/version.hpp) diff --git a/include/eigenpy/eigenpy.hpp b/include/eigenpy/eigenpy.hpp index f4edf090d36a2df10d71ad214ad0b73cfd1d8f8b..97407d3495b6a3c196211a8d9c4ab62b5b00f8d5 100644 --- a/include/eigenpy/eigenpy.hpp +++ b/include/eigenpy/eigenpy.hpp @@ -10,6 +10,9 @@ #include "eigenpy/eigen-typedef.hpp" #include "eigenpy/expose.hpp" +/// Custom CallPolicies +#include "eigenpy/std-unique-ptr.hpp" + #define ENABLE_SPECIFIC_MATRIX_TYPE(TYPE) \ ::eigenpy::enableEigenPySpecific<TYPE>(); diff --git a/include/eigenpy/std-unique-ptr.hpp b/include/eigenpy/std-unique-ptr.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dfc9f5a139d2d4c2d873376d2012edf833958649 --- /dev/null +++ b/include/eigenpy/std-unique-ptr.hpp @@ -0,0 +1,145 @@ +// +// Copyright (c) 2024 INRIA +// + +#ifndef __eigenpy_utils_std_unique_ptr_hpp__ +#define __eigenpy_utils_std_unique_ptr_hpp__ + +#include "eigenpy/fwd.hpp" +#include "eigenpy/utils/traits.hpp" +#include "eigenpy/utils/python-compat.hpp" + +#include <boost/python.hpp> + +#include <memory> +#include <type_traits> + +namespace eigenpy { + +namespace details { + +/// Transfer std::unique_ptr ownership to an owning holder +template <typename T> +typename std::enable_if<!is_python_primitive_type<T>::value, PyObject*>::type +unique_ptr_to_python(std::unique_ptr<T>&& x) { + typedef bp::objects::pointer_holder<std::unique_ptr<T>, T> holder_t; + if (!x) { + return bp::detail::none(); + } else { + return bp::objects::make_ptr_instance<T, holder_t>::execute(x); + } +} + +/// Convert and copy the primitive value to python +template <typename T> +typename std::enable_if<is_python_primitive_type<T>::value, PyObject*>::type +unique_ptr_to_python(std::unique_ptr<T>&& x) { + if (!x) { + return bp::detail::none(); + } else { + return bp::to_python_value<const T&>()(*x); + } +} + +/// std::unique_ptr keep the ownership but a reference to the std::unique_ptr +/// value is created +template <typename T> +typename std::enable_if<!is_python_primitive_type<T>::value, PyObject*>::type +internal_unique_ptr_to_python(std::unique_ptr<T>& x) { + if (!x) { + return bp::detail::none(); + } else { + return bp::detail::make_reference_holder::execute(x.get()); + } +} + +/// Convert and copy the primitive value to python +template <typename T> +typename std::enable_if<is_python_primitive_type<T>::value, PyObject*>::type +internal_unique_ptr_to_python(std::unique_ptr<T>& x) { + if (!x) { + return bp::detail::none(); + } else { + return bp::to_python_value<const T&>()(*x); + } +} + +/// result_converter of StdUniquePtrCallPolicies +struct StdUniquePtrResultConverter { + template <typename T> + struct apply { + struct type { + typedef typename T::element_type element_type; + + PyObject* operator()(T&& x) const { + return unique_ptr_to_python(std::forward<T>(x)); + } +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + PyTypeObject const* get_pytype() const { + return bp::to_python_value<const element_type&>().get_pytype(); + } +#endif + }; + }; +}; + +/// result_converter of ReturnInternalStdUniquePtr +struct InternalStdUniquePtrConverter { + template <typename T> + struct apply { + struct type { + typedef typename remove_cvref<T>::type::element_type element_type; + + PyObject* operator()(T x) const { + return internal_unique_ptr_to_python(x); + } +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + PyTypeObject const* get_pytype() const { + return bp::to_python_value<const element_type&>().get_pytype(); + } +#endif + }; + }; +}; + +} // namespace details + +/// CallPolicies to get std::unique_ptr value from a function +/// that return an std::unique_ptr. +/// If the object inside the std::unique_ptr is a class or an union +/// it will be moved. In other case, it will be copied. +struct StdUniquePtrCallPolicies : bp::default_call_policies { + typedef details::StdUniquePtrResultConverter result_converter; +}; + +/// Variant of \see bp::return_internal_reference that extract std::unique_ptr +/// content reference before converting it into a PyObject +struct ReturnInternalStdUniquePtr : bp::return_internal_reference<> { + typedef details::InternalStdUniquePtrConverter result_converter; + + template <class ArgumentPackage> + static PyObject* postcall(ArgumentPackage const& args_, PyObject* result) { + // Don't run return_internal_reference postcall on primitive type + if (PyInt_Check(result) || PyBool_Check(result) || PyFloat_Check(result) || + PyStr_Check(result) || PyComplex_Check(result)) { + return result; + } + return bp::return_internal_reference<>::postcall(args_, result); + } +}; + +} // namespace eigenpy + +namespace boost { +namespace python { + +/// Specialize to_python_value for std::unique_ptr +template <typename T> +struct to_python_value<const std::unique_ptr<T>&> + : eigenpy::details::StdUniquePtrResultConverter::apply< + std::unique_ptr<T> >::type {}; + +} // namespace python +} // namespace boost + +#endif // ifndef __eigenpy_utils_std_unique_ptr_hpp__ diff --git a/include/eigenpy/ufunc.hpp b/include/eigenpy/ufunc.hpp index cb6695ac913879f2dd5ddbf22b53883e82935f3c..129438cf15c8b43e7cffdc95102988fadc9b46f0 100644 --- a/include/eigenpy/ufunc.hpp +++ b/include/eigenpy/ufunc.hpp @@ -9,6 +9,7 @@ #include "eigenpy/register.hpp" #include "eigenpy/user-type.hpp" +#include "eigenpy/utils/python-compat.hpp" namespace eigenpy { namespace internal { @@ -207,11 +208,7 @@ void registerCommonUfunc() { const int type_code = Register::getTypeCode<Scalar>(); PyObject *numpy_str; -#if PY_MAJOR_VERSION >= 3 - numpy_str = PyUnicode_FromString("numpy"); -#else - numpy_str = PyString_FromString("numpy"); -#endif + numpy_str = PyStr_FromString("numpy"); PyObject *numpy; numpy = PyImport_Import(numpy_str); Py_DECREF(numpy_str); diff --git a/include/eigenpy/utils/python-compat.hpp b/include/eigenpy/utils/python-compat.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7ffbc9de474ddaa82fe9e5f7eabacf3c49d2d019 --- /dev/null +++ b/include/eigenpy/utils/python-compat.hpp @@ -0,0 +1,23 @@ +// +// Copyright (c) 2024 INRIA +// +// + +#ifndef __eigenpy_utils_python_compat_hpp__ +#define __eigenpy_utils_python_compat_hpp__ + +#if PY_MAJOR_VERSION >= 3 + +#define PyInt_Check PyLong_Check + +#define PyStr_Check PyUnicode_Check +#define PyStr_FromString PyUnicode_FromString + +#else + +#define PyStr_Check PyString_Check +#define PyStr_FromString PyString_FromString + +#endif + +#endif // ifndef __eigenpy_utils_python_compat_hpp__ diff --git a/include/eigenpy/utils/traits.hpp b/include/eigenpy/utils/traits.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b7525dea9f6457348fc8525f631ec38804aca528 --- /dev/null +++ b/include/eigenpy/utils/traits.hpp @@ -0,0 +1,56 @@ +// +// Copyright (c) 2024 INRIA +// +// + +#ifndef __eigenpy_utils_traits_hpp__ +#define __eigenpy_utils_traits_hpp__ + +#include <type_traits> +#include <string> +#include <complex> + +namespace eigenpy { + +namespace details { + +/// Trait to remove const& +template <typename T> +struct remove_cvref : std::remove_cv<typename std::remove_reference<T>::type> { +}; + +/// Trait to detect if T is a class or an union +template <typename T> +struct is_class_or_union + : std::integral_constant<bool, std::is_class<T>::value || + std::is_union<T>::value> {}; + +/// trait to detect if T is a std::complex managed by Boost Python +template <typename T> +struct is_python_complex : std::false_type {}; + +/// From boost/python/converter/builtin_converters +template <> +struct is_python_complex<std::complex<float> > : std::true_type {}; +template <> +struct is_python_complex<std::complex<double> > : std::true_type {}; +template <> +struct is_python_complex<std::complex<long double> > : std::true_type {}; + +template <typename T> +struct is_python_primitive_type_helper + : std::integral_constant<bool, !is_class_or_union<T>::value || + std::is_same<T, std::string>::value || + std::is_same<T, std::wstring>::value || + is_python_complex<T>::value> {}; + +/// Trait to detect if T is a Python primitive type +template <typename T> +struct is_python_primitive_type + : is_python_primitive_type_helper<typename remove_cvref<T>::type> {}; + +} // namespace details + +} // namespace eigenpy + +#endif // ifndef __eigenpy_utils_traits_hpp__ diff --git a/include/eigenpy/variant.hpp b/include/eigenpy/variant.hpp index 4028f856bc721c56177e019963eccdd6cb950768..bf787c3fc9864d6b2084c1020258492ce4fdc74e 100644 --- a/include/eigenpy/variant.hpp +++ b/include/eigenpy/variant.hpp @@ -6,6 +6,8 @@ #define __eigenpy_utils_variant_hpp__ #include "eigenpy/fwd.hpp" +#include "eigenpy/utils/traits.hpp" +#include "eigenpy/utils/python-compat.hpp" #include <boost/python.hpp> #include <boost/variant.hpp> @@ -146,7 +148,7 @@ struct NumericConvertibleImpl< std::is_integral<T>::value>::type> { static void* convertible(PyObject* obj) { // PyLong return true for bool type - return (PyLong_Check(obj) && !PyBool_Check(obj)) ? obj : nullptr; + return (PyInt_Check(obj) && !PyBool_Check(obj)) ? obj : nullptr; } static PyTypeObject const* expected_pytype() { return &PyLong_Type; } @@ -204,18 +206,6 @@ struct VariantValueToObject : VariantVisitorType<PyObject*, Variant> { using Base::operator(); }; -/// Trait to detect if T is a class or an union -template <typename T> -struct is_class_or_union - : std::integral_constant<bool, std::is_class<T>::value || - std::is_union<T>::value> {}; - -/// Trait to remove cvref and call is_class_or_union -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 @@ -231,14 +221,14 @@ struct VariantRefToObject : VariantVisitorType<PyObject*, Variant> { } template <typename T, - typename std::enable_if<!is_class_or_union_remove_cvref<T>::value, + typename std::enable_if<is_python_primitive_type<T>::value, bool>::type = true> result_type operator()(T t) const { return bp::incref(bp::object(t).ptr()); } template <typename T, - typename std::enable_if<is_class_or_union_remove_cvref<T>::value, + typename std::enable_if<!is_python_primitive_type<T>::value, bool>::type = true> result_type operator()(T& t) const { return bp::detail::make_reference_holder::execute(&t); @@ -312,7 +302,8 @@ struct ReturnInternalVariant : bp::return_internal_reference<> { 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)) { + if (PyInt_Check(result) || PyBool_Check(result) || PyFloat_Check(result) || + PyStr_Check(result) || PyComplex_Check(result)) { return result; } return bp::return_internal_reference<>::postcall(args_, result); diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index e1356f6d4d4ca6557cf4f906b5445d99a8660138..8dc425fa98c7fed420bea094e195661d3c8b8324 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -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(std_unique_ptr) function(config_test test tagname opttype) set(MODNAME ${test}_${tagname}) @@ -131,17 +132,16 @@ add_python_unit_test("py-std-vector" "unittest/python/test_std_vector.py" set_tests_properties("py-std-vector" PROPERTIES DEPENDS ${PYWRAP}) add_python_unit_test("py-std-array" "unittest/python/test_std_array.py" - "python;unittest") -set_tests_properties("py-std-array" PROPERTIES DEPENDS ${PYWRAP}) + "unittest") add_python_unit_test("py-std-pair" "unittest/python/test_std_pair.py" "unittest") -set_tests_properties("py-std-pair" PROPERTIES DEPENDS ${PYWRAP}) add_python_unit_test("py-user-struct" "unittest/python/test_user_struct.py" - "python;unittest") -set_tests_properties("py-user-struct" PROPERTIES DEPENDS ${PYWRAP}) + "unittest") + +add_python_unit_test("py-std-unique-ptr" + "unittest/python/test_std_unique_ptr.py" "unittest") add_python_unit_test("py-bind-virtual" "unittest/python/test_bind_virtual.py" - "python;unittest") -set_tests_properties("py-bind-virtual" PROPERTIES DEPENDS ${PYWRAP}) + "unittest") diff --git a/unittest/python/test_std_unique_ptr.py b/unittest/python/test_std_unique_ptr.py new file mode 100644 index 0000000000000000000000000000000000000000..8b56460f33cefdf10ccb4338ff970d597676c955 --- /dev/null +++ b/unittest/python/test_std_unique_ptr.py @@ -0,0 +1,61 @@ +from std_unique_ptr import ( + make_unique_int, + make_unique_v1, + make_unique_null, + make_unique_str, + make_unique_complex, + V1, + UniquePtrHolder, +) + +v = make_unique_int() +assert isinstance(v, int) +assert v == 10 + +v = make_unique_v1() +assert isinstance(v, V1) +assert v.v == 10 + +v = make_unique_null() +assert v is None + +v = make_unique_str() +assert isinstance(v, str) +assert v == "str" + +v = make_unique_complex() +assert isinstance(v, complex) +assert v == 1 + 0j + +unique_ptr_holder = UniquePtrHolder() + +v = unique_ptr_holder.int_ptr +assert isinstance(v, int) +assert v == 20 +# v is a copy, int_ptr will not be updated +v = 10 +assert unique_ptr_holder.int_ptr == 20 + +v = unique_ptr_holder.v1_ptr +assert isinstance(v, V1) +assert v.v == 200 +# v is a ref, v1_ptr will be updated +v.v = 10 +assert unique_ptr_holder.v1_ptr.v == 10 + +v = unique_ptr_holder.null_ptr +assert v is None + +v = unique_ptr_holder.str_ptr +assert isinstance(v, str) +assert v == "str" +# v is a copy, str_ptr will not be updated +v = "str_updated" +assert unique_ptr_holder.str_ptr == "str" + +v = unique_ptr_holder.complex_ptr +assert isinstance(v, complex) +assert v == 1 + 0j +# v is a copy, complex_ptr will not be updated +v = 1 + 2j +assert unique_ptr_holder.complex_ptr == 1 + 0j diff --git a/unittest/python/test_variant.py.in b/unittest/python/test_variant.py.in index b019514cd112f36a2b5e17d852730afc7d757c48..a7590a3fa8c408c804c3791ed673e0fb338bf293 100644 --- a/unittest/python/test_variant.py.in +++ b/unittest/python/test_variant.py.in @@ -6,7 +6,12 @@ V2 = variant_module.V2 VariantHolder = variant_module.VariantHolder VariantFullHolder = variant_module.VariantFullHolder make_variant = variant_module.make_variant -make_variant_full = variant_module.make_variant_full +make_variant_full_none = variant_module.make_variant_full_none +make_variant_full_float = variant_module.make_variant_full_float +make_variant_full_int = variant_module.make_variant_full_int +make_variant_full_bool = variant_module.make_variant_full_bool +make_variant_full_str = variant_module.make_variant_full_str +make_variant_full_complex = variant_module.make_variant_full_complex variant = make_variant() assert isinstance(variant, V1) @@ -44,9 +49,34 @@ assert isinstance(variant_holder.variant, V2) assert variant_holder.variant.v == v2.v # Test variant that hold a None value -v_full = make_variant_full() +v_full = make_variant_full_none() assert v_full is None +# Test variant that hold a float value +v_full = make_variant_full_float() +assert v_full == 3.14 +assert isinstance(v_full, float) + +# Test variant that hold a int value +v_full = make_variant_full_int() +assert v_full == 3 +assert isinstance(v_full, int) + +# Test variant that hold a bool value +v_full = make_variant_full_bool() +assert not v_full +assert isinstance(v_full, bool) + +# Test variant that hold a str value +v_full = make_variant_full_str() +assert v_full == "str" +assert isinstance(v_full, str) + +# Test variant that hold a complex value +v_full = make_variant_full_complex() +assert v_full == 1 + 0j +assert isinstance(v_full, complex) + variant_full_holder = VariantFullHolder() # Test None @@ -81,3 +111,13 @@ assert isinstance(variant_full_holder.variant, int) variant_full_holder.variant = 3.14 assert variant_full_holder.variant == 3.14 assert isinstance(variant_full_holder.variant, float) + +# Test str +variant_full_holder.variant = "str" +assert variant_full_holder.variant == "str" +assert isinstance(variant_full_holder.variant, str) + +# Test complex +variant_full_holder.variant = 1 + 0j +assert variant_full_holder.variant == 1 + 0j +assert isinstance(variant_full_holder.variant, complex) diff --git a/unittest/std_unique_ptr.cpp b/unittest/std_unique_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a95a5d2477f0e2c577c6a15adb1aa26e31aaa1a7 --- /dev/null +++ b/unittest/std_unique_ptr.cpp @@ -0,0 +1,77 @@ +/// @file +/// @copyright Copyright 2023 CNRS INRIA + +#include <eigenpy/eigenpy.hpp> +#include <eigenpy/std-unique-ptr.hpp> + +#include <memory> +#include <string> +#include <complex> + +namespace bp = boost::python; + +struct V1 { + V1() = default; + V1(double p_v) : v(p_v) {} + + double v = 100; +}; + +std::unique_ptr<int> make_unique_int() { return std::make_unique<int>(10); } + +std::unique_ptr<V1> make_unique_v1() { return std::make_unique<V1>(10); } + +std::unique_ptr<V1> make_unique_null() { return nullptr; } + +std::unique_ptr<std::string> make_unique_str() { + return std::make_unique<std::string>("str"); +} + +std::unique_ptr<std::complex<double> > make_unique_complex() { + return std::make_unique<std::complex<double> >(1., 0.); +} + +struct UniquePtrHolder { + UniquePtrHolder() + : int_ptr(std::make_unique<int>(20)), + v1_ptr(std::make_unique<V1>(200)), + str_ptr(std::make_unique<std::string>("str")), + complex_ptr(std::make_unique<std::complex<double> >(1., 0.)) {} + + std::unique_ptr<int> int_ptr; + std::unique_ptr<V1> v1_ptr; + std::unique_ptr<V1> null_ptr; + std::unique_ptr<std::string> str_ptr; + std::unique_ptr<std::complex<double> > complex_ptr; +}; + +BOOST_PYTHON_MODULE(std_unique_ptr) { + eigenpy::enableEigenPy(); + + bp::class_<V1>("V1", bp::init<>()).def_readwrite("v", &V1::v); + + bp::def("make_unique_int", make_unique_int); + bp::def("make_unique_v1", make_unique_v1); + bp::def("make_unique_null", make_unique_null, + eigenpy::StdUniquePtrCallPolicies()); + bp::def("make_unique_str", make_unique_str); + bp::def("make_unique_complex", make_unique_complex); + + boost::python::class_<UniquePtrHolder, boost::noncopyable>("UniquePtrHolder", + bp::init<>()) + .add_property("int_ptr", + bp::make_getter(&UniquePtrHolder::int_ptr, + eigenpy::ReturnInternalStdUniquePtr())) + .add_property("v1_ptr", + bp::make_getter(&UniquePtrHolder::v1_ptr, + eigenpy::ReturnInternalStdUniquePtr())) + .add_property("null_ptr", + bp::make_getter(&UniquePtrHolder::null_ptr, + eigenpy::ReturnInternalStdUniquePtr())) + .add_property("str_ptr", + bp::make_getter(&UniquePtrHolder::str_ptr, + eigenpy::ReturnInternalStdUniquePtr())) + .add_property("complex_ptr", + bp::make_getter(&UniquePtrHolder::complex_ptr, + eigenpy::ReturnInternalStdUniquePtr())); +} diff --git a/unittest/variant.cpp.in b/unittest/variant.cpp.in index 12f669937ecf760fc72d054a1187a96511f94e20..5d8f75ebdfc150e247504be9f1287cf3551d452f 100644 --- a/unittest/variant.cpp.in +++ b/unittest/variant.cpp.in @@ -4,6 +4,9 @@ #include <eigenpy/eigenpy.hpp> #include <eigenpy/variant.hpp> +#include <string> +#include <complex> + #cmakedefine TEST_TYPE @TEST_TYPE@ #define VARIANT TEST_TYPE @@ -32,12 +35,18 @@ struct MyVariantNoneHelper<std::variant<Alternatives...> > { }; #endif -typedef typename MyVariantNoneHelper<VARIANT<V1, bool, int, double> >::type +typedef typename MyVariantNoneHelper< + VARIANT<V1, bool, int, double, std::string, std::complex<double> > >::type MyVariantFull; MyVariant make_variant() { return V1(); } -MyVariantFull make_variant_full() { return MyVariantFull(); } +MyVariantFull make_variant_full_none() { return MyVariantFull(); } +MyVariantFull make_variant_full_float() { return 3.14; } +MyVariantFull make_variant_full_int() { return 3; } +MyVariantFull make_variant_full_bool() { return false; } +MyVariantFull make_variant_full_str() { return std::string("str"); } +MyVariantFull make_variant_full_complex() { return std::complex<double>(1., 0.); } struct VariantHolder { MyVariant variant; @@ -68,7 +77,12 @@ BOOST_PYTHON_MODULE(@MODNAME@) { typedef eigenpy::VariantConverter<MyVariantFull> ConverterFull; ConverterFull::registration(); - bp::def("make_variant_full", make_variant_full); + bp::def("make_variant_full_none", make_variant_full_none); + bp::def("make_variant_full_float", make_variant_full_float); + bp::def("make_variant_full_int", make_variant_full_int); + bp::def("make_variant_full_bool", make_variant_full_bool); + bp::def("make_variant_full_str", make_variant_full_str); + bp::def("make_variant_full_complex", make_variant_full_complex); boost::python::class_<VariantFullHolder>("VariantFullHolder", bp::init<>()) .add_property("variant",