Commit 333ed393 authored by Christopher Berner's avatar Christopher Berner Committed by Jordan DeLong

Add converter for containers of pairs

Summary:
Add specialized dynamic converter for containers of pairs,
which can convert from a list of pairs, or from a object

Test Plan: added a unit test

Reviewed By: njormrod@fb.com

FB internal diff: D650730
parent 8dd82d48
...@@ -50,15 +50,8 @@ namespace folly { ...@@ -50,15 +50,8 @@ namespace folly {
namespace dynamicconverter_detail { namespace dynamicconverter_detail {
BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type); BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
BOOST_MPL_HAS_XXX_TRAIT_DEF(key_type);
BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator); BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
template <typename T> struct map_container_has_correct_types
: std::is_same<std::pair<typename std::add_const<typename T::key_type>::type,
typename T::mapped_type>,
typename T::value_type> {};
template <typename T> struct class_is_container { template <typename T> struct class_is_container {
typedef std::reverse_iterator<T*> some_iterator; typedef std::reverse_iterator<T*> some_iterator;
enum { value = has_value_type<T>::value && enum { value = has_value_type<T>::value &&
...@@ -66,13 +59,6 @@ template <typename T> struct class_is_container { ...@@ -66,13 +59,6 @@ template <typename T> struct class_is_container {
std::is_constructible<T, some_iterator, some_iterator>::value }; std::is_constructible<T, some_iterator, some_iterator>::value };
}; };
template <typename T> struct container_is_map
: std::conditional<
has_key_type<T>::value && has_mapped_type<T>::value,
map_container_has_correct_types<T>,
std::false_type
>::type {};
template <typename T> struct is_container template <typename T> struct is_container
: std::conditional< : std::conditional<
std::is_class<T>::value, std::is_class<T>::value,
...@@ -80,13 +66,6 @@ template <typename T> struct is_container ...@@ -80,13 +66,6 @@ template <typename T> struct is_container
std::false_type std::false_type
>::type {}; >::type {};
template <typename T> struct is_map_container
: std::conditional<
is_container<T>::value,
container_is_map<T>,
std::false_type
>::type {};
} // namespace dynamicconverter_detail } // namespace dynamicconverter_detail
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
...@@ -106,16 +85,33 @@ template <typename T> struct is_map_container ...@@ -106,16 +85,33 @@ template <typename T> struct is_map_container
namespace dynamicconverter_detail { namespace dynamicconverter_detail {
template <typename F, typename S> template<typename T>
inline void struct Dereferencer {
derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) { static inline void
new (mem) std::pair<F, S>(convertTo<F>(it->first), convertTo<S>(it->second)); derefToCache(T* mem, const dynamic::const_item_iterator& it) {
} throw TypeError("array", dynamic::Type::OBJECT);
}
template <typename T> static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
inline void derefToCache(T* mem, const dynamic::const_iterator& it) { new (mem) T(convertTo<T>(*it));
new (mem) T(convertTo<T>(*it)); }
} };
template<typename F, typename S>
struct Dereferencer<std::pair<F, S>> {
static inline void
derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
new (mem) std::pair<F, S>(
convertTo<F>(it->first), convertTo<S>(it->second)
);
}
// Intentional duplication of the code in Dereferencer
template <typename T>
static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
new (mem) T(convertTo<T>(*it));
}
};
template <typename T, typename It> template <typename T, typename It>
class Transformer : public boost::iterator_adaptor< class Transformer : public boost::iterator_adaptor<
...@@ -138,7 +134,7 @@ class Transformer : public boost::iterator_adaptor< ...@@ -138,7 +134,7 @@ class Transformer : public boost::iterator_adaptor<
ttype& dereference() const { ttype& dereference() const {
if (LIKELY(!valid_)) { if (LIKELY(!valid_)) {
cache_.~ttype(); cache_.~ttype();
derefToCache(&cache_, this->base_reference()); Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
valid_ = true; valid_ = true;
} }
return cache_; return cache_;
...@@ -226,44 +222,26 @@ struct DynamicConverter<std::pair<F,S>> { ...@@ -226,44 +222,26 @@ struct DynamicConverter<std::pair<F,S>> {
} }
}; };
// map containers // containers
template <typename C> template <typename C>
struct DynamicConverter<C, struct DynamicConverter<C,
typename std::enable_if< typename std::enable_if<
dynamicconverter_detail::is_map_container<C>::value>::type> { dynamicconverter_detail::is_container<C>::value>::type> {
static C convert(const dynamic& d) { static C convert(const dynamic& d) {
if (LIKELY(d.isObject())) { if (d.isArray()) {
return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
dynamicconverter_detail::conversionIterator<C>(d.end()));
} else if (d.isObject()) {
return C(dynamicconverter_detail::conversionIterator<C> return C(dynamicconverter_detail::conversionIterator<C>
(d.items().begin()), (d.items().begin()),
dynamicconverter_detail::conversionIterator<C> dynamicconverter_detail::conversionIterator<C>
(d.items().end())); (d.items().end()));
} else if (d.isArray()) {
return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
dynamicconverter_detail::conversionIterator<C>(d.end()));
} else { } else {
throw TypeError("object or array", d.type()); throw TypeError("object or array", d.type());
} }
} }
}; };
// non-map containers
template <typename C>
struct DynamicConverter<C,
typename std::enable_if<
dynamicconverter_detail::is_container<C>::value &&
!dynamicconverter_detail::is_map_container<C>::value
>::type
> {
static C convert(const dynamic& d) {
if (LIKELY(d.isArray())) {
return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
dynamicconverter_detail::conversionIterator<C>(d.end()));
} else {
throw TypeError("array", d.type());
}
}
};
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// convertTo implementation // convertTo implementation
......
...@@ -41,19 +41,6 @@ TEST(DynamicConverter, template_metaprogramming) { ...@@ -41,19 +41,6 @@ TEST(DynamicConverter, template_metaprogramming) {
EXPECT_EQ(c1t, true); EXPECT_EQ(c1t, true);
EXPECT_EQ(c2t, true); EXPECT_EQ(c2t, true);
EXPECT_EQ(c3t, true); EXPECT_EQ(c3t, true);
bool m1f = is_map_container<int>::value;
bool m2f = is_map_container<std::vector<int>>::value;
bool m3f = is_map_container<std::set<int>>::value;
bool m1t = is_map_container<std::map<int, int>>::value;
bool m2t = is_map_container<std::unordered_map<int, int>>::value;
EXPECT_EQ(m1f, false);
EXPECT_EQ(m2f, false);
EXPECT_EQ(m3f, false);
EXPECT_EQ(m1t, true);
EXPECT_EQ(m2t, true);
} }
TEST(DynamicConverter, arithmetic_types) { TEST(DynamicConverter, arithmetic_types) {
...@@ -153,6 +140,13 @@ TEST(DynamicConverter, map_keyed_by_string) { ...@@ -153,6 +140,13 @@ TEST(DynamicConverter, map_keyed_by_string) {
EXPECT_EQ(i2, i2b); EXPECT_EQ(i2, i2b);
} }
TEST(DynamicConverter, map_to_vector_of_pairs) {
dynamic d1 = dynamic::object("1", "one")("2", "two");
auto i1 = convertTo<std::vector<std::pair<std::string, std::string>>>(d1);
decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
EXPECT_EQ(i1, i1b);
}
TEST(DynamicConverter, nested_containers) { TEST(DynamicConverter, nested_containers) {
dynamic d1 = { { 1 }, { }, { 2, 3 } }; dynamic d1 = { { 1 }, { }, { 2, 3 } };
auto i1 = convertTo<folly::fbvector<std::vector<uint8_t>>>(d1); auto i1 = convertTo<folly::fbvector<std::vector<uint8_t>>>(d1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment