Skip to content
Snippets Groups Projects
Unverified Commit 694d996a authored by Wilson Jallet's avatar Wilson Jallet :clapper: Committed by GitHub
Browse files

Merge pull request #504 from ManifoldFR/topic/generic-maps

[std-map] add more general visitor for map types
parents 9537d8f6 00bbef89
No related branches found
No related tags found
No related merge requests found
Pipeline #44214 passed with warnings
......@@ -7,8 +7,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
- Add more general visitor `GenericMapPythonVisitor` for map types test `boost::unordered_map<std::string, int>` ([#504](https://github.com/stack-of-tasks/eigenpy/pull/504))
- Support for non-[default-contructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible) types in map types ([#504](https://github.com/stack-of-tasks/eigenpy/pull/504))
- Add type_info helpers ([#502](https://github.com/stack-of-tasks/eigenpy/pull/502))
### Changed
- Move `StdMapPythonVisitor` out of `eigenpy::python` namespace, which was a mistake ([#504](https://github.com/stack-of-tasks/eigenpy/pull/504))
## [3.9.0] - 2024-08-31
### Changed
......
......@@ -14,15 +14,16 @@ EigenPy — Versatile and efficient Python bindings between Numpy and Eigen
**EigenPy** is an open-source framework that allows the binding of the famous [Eigen](http://eigen.tuxfamily.org) C++ library in Python via Boost.Python.
**EigenPy** provides:
- full memory sharing between Numpy and Eigen, avoiding memory allocation
- full support Eigen::Ref avoiding memory allocation
- full support of the Eigen::Tensor module
- exposition of the Geometry module of Eigen for easy code prototyping
- standard matrix decomposion routines of Eigen such as the Cholesky decomposition (SVD and QR decompositions [can be added](#contributing))
- full support of SWIG objects
- full support of runtime declaration of Numpy scalar types
- extended API to expose std::vector types
- full support of vectorization between C++ and Python (all the hold objects are properly aligned in memory)
- full memory sharing between Numpy and Eigen, avoiding memory allocation
- full support Eigen::Ref avoiding memory allocation
- full support of the Eigen::Tensor module
- exposition of the Geometry module of Eigen for easy code prototyping
- standard matrix decomposion routines of Eigen such as the Cholesky decomposition (SVD and QR decompositions [can be added](#contributing))
- full support of SWIG objects
- full support of runtime declaration of Numpy scalar types
- extended API to expose several STL types and some of their Boost equivalents: `optional` types, `std::pair`, maps, variants...
- full support of vectorization between C++ and Python (all the hold objects are properly aligned in memory)
## Setup
......
......@@ -70,8 +70,6 @@ struct overload_base_get_item_for_std_map
// Rohan Budhiraja.
///////////////////////////////////////////////////////////////////////////////
namespace python {
namespace bp = boost::python;
/**
......@@ -144,7 +142,7 @@ struct dict_to_map {
bp::throw_error_already_set();
}
typename Container::mapped_type val = valproxy();
map[key] = val;
map.emplace(key, val);
}
// remember the location for later
......@@ -161,24 +159,56 @@ struct dict_to_map {
}
};
/// Policies which handle the non-default constructible case
/// and set_item() using emplace().
template <class Container, bool NoProxy>
struct emplace_set_derived_policies
: bp::map_indexing_suite<
Container, NoProxy,
emplace_set_derived_policies<Container, NoProxy> > {
typedef typename Container::key_type index_type;
typedef typename Container::value_type::second_type data_type;
typedef typename Container::value_type value_type;
using DerivedPolicies =
bp::detail::final_map_derived_policies<Container, NoProxy>;
template <class Class>
static void extension_def(Class& cl) {
// Wrap the map's element (value_type)
std::string elem_name = "map_indexing_suite_";
bp::object class_name(cl.attr("__name__"));
bp::extract<std::string> class_name_extractor(class_name);
elem_name += class_name_extractor();
elem_name += "_entry";
namespace mpl = boost::mpl;
typedef typename mpl::if_<
mpl::and_<boost::is_class<data_type>, mpl::bool_<!NoProxy> >,
bp::return_internal_reference<>, bp::default_call_policies>::type
get_data_return_policy;
bp::class_<value_type>(elem_name.c_str(), bp::no_init)
.def("__repr__", &DerivedPolicies::print_elem)
.def("data", &DerivedPolicies::get_data, get_data_return_policy())
.def("key", &DerivedPolicies::get_key);
}
static void set_item(Container& container, index_type i, data_type const& v) {
container.emplace(i, v);
}
};
/**
* @brief Expose an std::map from a type given as template argument.
* @brief Expose the map-like container, e.g. (std::map).
*
* @param[in] T Type to expose as std::map<T>.
* @param[in] Compare Type for the Compare in std::map<T,Compare,Allocator>.
* @param[in] Allocator Type for the Allocator in
* std::map<T,Compare,Allocator>.
* @param[in] Container Container to expose.
* @param[in] NoProxy When set to false, the elements will be copied when
* returned to Python.
*/
template <class Key, class T, class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >,
bool NoProxy = false>
struct StdMapPythonVisitor
: public bp::map_indexing_suite<
typename std::map<Key, T, Compare, Allocator>, NoProxy>,
public dict_to_map<std::map<Key, T, Compare, Allocator> > {
typedef std::map<Key, T, Compare, Allocator> Container;
template <class Container, bool NoProxy = false>
struct GenericMapVisitor
: public emplace_set_derived_policies<Container, NoProxy>,
public dict_to_map<Container> {
typedef dict_to_map<Container> FromPythonDictConverter;
static void expose(const std::string& class_name,
......@@ -186,15 +216,34 @@ struct StdMapPythonVisitor
namespace bp = bp;
bp::class_<Container>(class_name.c_str(), doc_string.c_str())
.def(StdMapPythonVisitor())
.def(GenericMapVisitor())
.def("todict", &FromPythonDictConverter::todict, bp::arg("self"),
"Returns the std::map as a Python dictionary.")
"Returns the map type as a Python dictionary.")
.def_pickle(PickleMap<Container>());
// Register conversion
FromPythonDictConverter::register_converter();
}
};
/**
* @brief Expose an std::map from a type given as template argument.
*
* @param[in] T Type to expose as std::map<T>.
* @param[in] Compare Type for the Compare in std::map<T,Compare,Allocator>.
* @param[in] Allocator Type for the Allocator in
* std::map<T,Compare,Allocator>.
* @param[in] NoProxy When set to false, the elements will be copied when
* returned to Python.
*/
template <class Key, class T, class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >,
bool NoProxy = false>
struct StdMapPythonVisitor
: GenericMapVisitor<std::map<Key, T, Compare, Allocator>, NoProxy> {};
namespace python {
// fix previous mistake
using ::eigenpy::StdMapPythonVisitor;
} // namespace python
} // namespace eigenpy
......
from std_map import copy, std_map_to_dict
from std_map import copy, copy_boost, std_map_to_dict
t = {"one": 1.0, "two": 2.0}
t2 = {"one": 1, "two": 2, "three": 3}
assert std_map_to_dict(t) == t
assert std_map_to_dict(copy(t)) == t
m = copy_boost(t2)
assert m.todict() == t2
......@@ -3,7 +3,7 @@
#include <eigenpy/eigenpy.hpp>
#include <eigenpy/std-map.hpp>
#include <iostream>
#include <boost/unordered_map.hpp>
namespace bp = boost::python;
......@@ -22,14 +22,32 @@ std::map<std::string, T1> copy(const std::map<std::string, T1>& map) {
return out;
}
template <typename T1>
boost::unordered_map<std::string, T1> copy_boost(
const boost::unordered_map<std::string, T1>& obj) {
return obj;
}
struct X {
X() = delete;
X(int x) : val(x) {}
int val;
};
BOOST_PYTHON_MODULE(std_map) {
eigenpy::enableEigenPy();
eigenpy::python::StdMapPythonVisitor<
eigenpy::StdMapPythonVisitor<
std::string, double, std::less<std::string>,
std::allocator<std::pair<const std::string, double> >,
true>::expose("StdMap_Double");
eigenpy::GenericMapVisitor<boost::unordered_map<std::string, int> >::expose(
"boost_map_int");
eigenpy::GenericMapVisitor<std::map<std::string, X> >::expose("StdMap_X");
bp::def("std_map_to_dict", std_map_to_dict<double>);
bp::def("copy", copy<double>);
bp::def("copy_boost", copy_boost<int>);
}
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