Skip to content

Commit

Permalink
script_interface: Add support for python dict
Browse files Browse the repository at this point in the history
Convert dict objects from/to std::unordered_map<int, Variant>.
  • Loading branch information
jngrad committed Feb 25, 2021
1 parent 4f14b6f commit b48ab62
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 3 deletions.
17 changes: 16 additions & 1 deletion src/python/espressomd/script_interface.pyx
Expand Up @@ -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:
Expand All @@ -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<int, Variant>]")
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))
Expand All @@ -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
Expand Down Expand Up @@ -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")

Expand Down
4 changes: 3 additions & 1 deletion src/script_interface/Variant.hpp
Expand Up @@ -28,6 +28,7 @@
#include <boost/range/algorithm/transform.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/serialization/variant.hpp>
#include <boost/serialization/vector.hpp>

Expand All @@ -52,7 +53,8 @@ constexpr const None none{};
using Variant = boost::make_recursive_variant<
None, bool, int, size_t, double, std::string, std::vector<int>,
std::vector<double>, ObjectRef, std::vector<boost::recursive_variant_>,
Utils::Vector2d, Utils::Vector3d, Utils::Vector4d>::type;
Utils::Vector2d, Utils::Vector3d, Utils::Vector4d,
std::unordered_map<int, boost::recursive_variant_>>::type;

using VariantMap = std::unordered_map<std::string, Variant>;

Expand Down
47 changes: 47 additions & 0 deletions src/script_interface/get_value.hpp
Expand Up @@ -160,6 +160,26 @@ template <> struct get_value_helper<std::vector<double>, void> {
}
};

template <typename K, typename T>
struct GetMapOrEmpty : boost::static_visitor<std::unordered_map<K, T>> {
/* Catch all case -> wrong type. */
template <typename U> std::unordered_map<K, T> operator()(U const &) const {
throw boost::bad_get{};
}

/* Standard case, correct type */
std::unordered_map<K, T> operator()(std::unordered_map<K, T> const &v) const {
return v;
}
};

/* std::unordered_map cases */
template <> struct get_value_helper<std::unordered_map<int, Variant>, void> {
std::unordered_map<int, Variant> operator()(Variant const &v) const {
return boost::apply_visitor(GetMapOrEmpty<int, Variant>{}, 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.
Expand Down Expand Up @@ -213,6 +233,33 @@ template <typename T> T get_value(Variant const &v) {
}
}

template <typename K, typename V>
std::unordered_map<K, V> get_map(std::unordered_map<K, Variant> const &v) {
std::unordered_map<K, V> ret;
auto it = v.begin();
try {
for (; it != v.end(); ++it) {
ret.insert({it->first, detail::get_value_helper<V>{}(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<V>() +
" (raised during the creation of a " +
Utils::demangle<std::unordered_map<K, V>>() + ")");
}
return ret;
}

template <typename K, typename V>
std::unordered_map<K, Variant> make_map(std::unordered_map<K, V> const &v) {
std::unordered_map<K, Variant> 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
Expand Down
25 changes: 24 additions & 1 deletion src/script_interface/packed_variant.hpp
Expand Up @@ -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<int>, std::vector<double>,
ObjectId, std::vector<boost::recursive_variant_>, Utils::Vector2d,
Utils::Vector3d, Utils::Vector4d>::type;
Utils::Vector3d, Utils::Vector4d,
std::unordered_map<int, boost::recursive_variant_>>::type;

using PackedMap = std::vector<std::pair<std::string, PackedVariant>>;

Expand Down Expand Up @@ -84,6 +85,17 @@ struct PackVisitor : boost::static_visitor<PackedVariant> {
return ret;
}

/* For the map, we recurse into each element. */
auto operator()(const std::unordered_map<int, Variant> &map) const {
std::unordered_map<int, PackedVariant> 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 {
Expand Down Expand Up @@ -121,6 +133,17 @@ struct UnpackVisitor : boost::static_visitor<Variant> {
return ret;
}

/* For the map, we recurse into each element. */
auto operator()(const std::unordered_map<int, PackedVariant> &map) const {
std::unordered_map<int, Variant> 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 <class T> Variant operator()(T &&val) const {
return std::forward<T>(val);
Expand Down

0 comments on commit b48ab62

Please sign in to comment.