From b48ab62e7b36e134462654d134b58ee77e1960b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Thu, 25 Feb 2021 10:59:03 +0100 Subject: [PATCH] script_interface: Add support for python dict Convert dict objects from/to std::unordered_map. --- src/python/espressomd/script_interface.pyx | 17 +++++++- src/script_interface/Variant.hpp | 4 +- src/script_interface/get_value.hpp | 47 ++++++++++++++++++++++ src/script_interface/packed_variant.hpp | 25 +++++++++++- 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx index 6a1037c4319..ec7954067fa 100644 --- a/src/python/espressomd/script_interface.pyx +++ b/src/python/espressomd/script_interface.pyx @@ -184,6 +184,7 @@ cdef Variant python_object_to_variant(value): """Convert Python objects to C++ Variant objects.""" cdef vector[Variant] vec + cdef unordered_map[int, Variant] vmap cdef PObjectRef oref if value is None: @@ -196,7 +197,12 @@ cdef Variant python_object_to_variant(value): oref = value.get_sip() return make_variant(oref.sip) elif isinstance(value, dict): - raise TypeError("No conversion from type dict to Variant") + for k, v in value.items(): + if not isinstance(k, int): + raise TypeError( + f"No conversion from type dict_item([({type(k).__name__}, {type(v).__name__})]) to Variant[std::unordered_map]") + vmap[k] = python_object_to_variant(v) + return make_variant[unordered_map[int, Variant]](vmap) elif hasattr(value, '__iter__') and not(type(value) == str): for e in value: vec.push_back(python_object_to_variant(e)) @@ -217,6 +223,7 @@ cdef variant_to_python_object(const Variant & value) except +: """Convert C++ Variant objects to Python objects.""" cdef vector[Variant] vec + cdef unordered_map[int, Variant] vmap cdef shared_ptr[ObjectHandle] ptr if is_none(value): return None @@ -266,6 +273,14 @@ cdef variant_to_python_object(const Variant & value) except +: res.append(variant_to_python_object(i)) return res + if is_type[unordered_map[int, Variant]](value): + vmap = get_value[unordered_map[int, Variant]](value) + res = {} + + for kv in vmap: + res[kv.first] = variant_to_python_object(kv.second) + + return res raise TypeError("Unknown type") diff --git a/src/script_interface/Variant.hpp b/src/script_interface/Variant.hpp index 07168303b7d..8ee85467a84 100644 --- a/src/script_interface/Variant.hpp +++ b/src/script_interface/Variant.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,8 @@ constexpr const None none{}; using Variant = boost::make_recursive_variant< None, bool, int, size_t, double, std::string, std::vector, std::vector, ObjectRef, std::vector, - Utils::Vector2d, Utils::Vector3d, Utils::Vector4d>::type; + Utils::Vector2d, Utils::Vector3d, Utils::Vector4d, + std::unordered_map>::type; using VariantMap = std::unordered_map; diff --git a/src/script_interface/get_value.hpp b/src/script_interface/get_value.hpp index 53b89715169..23f4492aa11 100644 --- a/src/script_interface/get_value.hpp +++ b/src/script_interface/get_value.hpp @@ -160,6 +160,26 @@ template <> struct get_value_helper, void> { } }; +template +struct GetMapOrEmpty : boost::static_visitor> { + /* Catch all case -> wrong type. */ + template std::unordered_map operator()(U const &) const { + throw boost::bad_get{}; + } + + /* Standard case, correct type */ + std::unordered_map operator()(std::unordered_map const &v) const { + return v; + } +}; + +/* std::unordered_map cases */ +template <> struct get_value_helper, void> { + std::unordered_map operator()(Variant const &v) const { + return boost::apply_visitor(GetMapOrEmpty{}, v); + } +}; + /* This allows direct retrieval of a shared_ptr to the object from an ObjectId variant. If the type is a derived type, the type is also checked. @@ -213,6 +233,33 @@ template T get_value(Variant const &v) { } } +template +std::unordered_map get_map(std::unordered_map const &v) { + std::unordered_map ret; + auto it = v.begin(); + try { + for (; it != v.end(); ++it) { + ret.insert({it->first, detail::get_value_helper{}(it->second)}); + } + } catch (const boost::bad_get &) { + throw Exception("Provided map value of type " + + detail::type_label(it->second) + " is not convertible to " + + Utils::demangle() + + " (raised during the creation of a " + + Utils::demangle>() + ")"); + } + return ret; +} + +template +std::unordered_map make_map(std::unordered_map const &v) { + std::unordered_map ret; + for (auto const &it : v) { + ret.insert({it.first, Variant(it.second)}); + } + return ret; +} + /** * @brief Get a value from a VariantMap by name, or throw * if it does not exist or is not convertible to diff --git a/src/script_interface/packed_variant.hpp b/src/script_interface/packed_variant.hpp index 677e098c4c3..f07ee280a69 100644 --- a/src/script_interface/packed_variant.hpp +++ b/src/script_interface/packed_variant.hpp @@ -54,7 +54,8 @@ inline ObjectId object_id(const ObjectHandle *p) { using PackedVariant = boost::make_recursive_variant< None, bool, int, double, std::string, std::vector, std::vector, ObjectId, std::vector, Utils::Vector2d, - Utils::Vector3d, Utils::Vector4d>::type; + Utils::Vector3d, Utils::Vector4d, + std::unordered_map>::type; using PackedMap = std::vector>; @@ -84,6 +85,17 @@ struct PackVisitor : boost::static_visitor { return ret; } + /* For the map, we recurse into each element. */ + auto operator()(const std::unordered_map &map) const { + std::unordered_map ret{}; + + for (auto const &it : map) { + ret.insert({it.first, boost::apply_visitor(*this, it.second)}); + } + + return ret; + } + /* For object references we store the object reference, and * replace it by just an id. */ PackedVariant operator()(const ObjectRef &so_ptr) const { @@ -121,6 +133,17 @@ struct UnpackVisitor : boost::static_visitor { return ret; } + /* For the map, we recurse into each element. */ + auto operator()(const std::unordered_map &map) const { + std::unordered_map ret{}; + + for (auto const &it : map) { + ret.insert({it.first, boost::apply_visitor(*this, it.second)}); + } + + return ret; + } + /* Regular value are just verbatim copied into the result. */ template Variant operator()(T &&val) const { return std::forward(val);