/*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// @author Nicholas Ormrod <njormrod@fb.com>
-#ifndef DYNAMIC_CONVERTER_H
-#define DYNAMIC_CONVERTER_H
+#pragma once
+
+#include <iterator>
+#include <type_traits>
+
+#include <boost/iterator/iterator_adaptor.hpp>
+#include <boost/mpl/has_xxx.hpp>
+
+#include <folly/Likely.h>
+#include <folly/Optional.h>
+#include <folly/Traits.h>
+#include <folly/dynamic.h>
-#include "folly/dynamic.h"
namespace folly {
- template <typename T> T convertTo(const dynamic&);
-}
+template <typename T>
+T convertTo(const dynamic&);
+template <typename T>
+dynamic toDynamic(const T&);
+} // namespace folly
/**
* convertTo returns a well-typed representation of the input dynamic.
*
* Example:
*
- * dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int
+ * dynamic d = dynamic::array(
+ * dynamic::array(1, 2, 3),
+ * dynamic::array(4, 5)); // a vector of vector of int
* auto vvi = convertTo<fbvector<fbvector<int>>>(d);
*
* See docs/DynamicConverter.md for supported types and customization
*/
-
-#include <type_traits>
-#include <boost/iterator/iterator_adaptor.hpp>
-#include <boost/mpl/has_xxx.hpp>
-#include "folly/Likely.h"
-
namespace folly {
///////////////////////////////////////////////////////////////////////////////
namespace dynamicconverter_detail {
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(mapped_type);
+BOOST_MPL_HAS_XXX_TRAIT_DEF(key_type);
-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 {
- struct dummy {};
+template <typename T> struct iterator_class_is_container {
+ typedef std::reverse_iterator<typename T::iterator> some_iterator;
enum { value = has_value_type<T>::value &&
- has_iterator<T>::value &&
- std::is_constructible<T, dummy, dummy>::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
- : std::conditional<
- std::is_class<T>::value,
- class_is_container<T>,
- std::false_type
- >::type {};
-
-template <typename T> struct is_map_container
- : std::conditional<
- is_container<T>::value,
- container_is_map<T>,
- std::false_type
- >::type {};
+template <typename T>
+using class_is_container =
+ Conjunction<has_iterator<T>, iterator_class_is_container<T>>;
+
+template <typename T>
+using is_range = StrictConjunction<has_value_type<T>, has_iterator<T>>;
+
+template <typename T>
+using is_container = StrictConjunction<std::is_class<T>, class_is_container<T>>;
+
+template <typename T>
+using is_map = StrictConjunction<is_range<T>, has_mapped_type<T>>;
+
+template <typename T>
+using is_associative = StrictConjunction<is_range<T>, has_key_type<T>>;
} // namespace dynamicconverter_detail
namespace dynamicconverter_detail {
+template <typename T>
+struct Dereferencer {
+ static inline void derefToCache(
+ Optional<T>* /* mem */,
+ const dynamic::const_item_iterator& /* it */) {
+ throw TypeError("array", dynamic::Type::OBJECT);
+ }
+
+ static inline void derefToCache(
+ Optional<T>* mem,
+ const dynamic::const_iterator& it) {
+ mem->emplace(convertTo<T>(*it));
+ }
+};
+
template <typename F, typename S>
-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));
-}
+struct Dereferencer<std::pair<F, S>> {
+ static inline void derefToCache(
+ Optional<std::pair<F, S>>* mem,
+ const dynamic::const_item_iterator& it) {
+ mem->emplace(convertTo<F>(it->first), convertTo<S>(it->second));
+ }
-template <typename T>
-inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
- new (mem) T(convertTo<T>(*it));
-}
+ // Intentional duplication of the code in Dereferencer
+ template <typename T>
+ static inline void derefToCache(
+ Optional<T>* mem,
+ const dynamic::const_iterator& it) {
+ mem->emplace(convertTo<T>(*it));
+ }
+};
template <typename T, typename It>
-class Transformer : public boost::iterator_adaptor<
- Transformer<T, It>,
- It,
- typename T::value_type
- > {
+class Transformer
+ : public boost::
+ iterator_adaptor<Transformer<T, It>, It, typename T::value_type> {
friend class boost::iterator_core_access;
typedef typename T::value_type ttype;
- mutable ttype cache_;
- mutable bool valid_;
+ mutable Optional<ttype> cache_;
void increment() {
++this->base_reference();
- valid_ = false;
+ cache_ = none;
}
ttype& dereference() const {
- if (LIKELY(!valid_)) {
- cache_.~ttype();
- derefToCache(&cache_, this->base_reference());
- valid_ = true;
+ if (!cache_) {
+ Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
}
- return cache_;
+ return cache_.value();
}
-public:
- explicit Transformer(const It& it)
- : Transformer::iterator_adaptor_(it), valid_(false) {}
+ public:
+ explicit Transformer(const It& it) : Transformer::iterator_adaptor_(it) {}
};
// conversion factory
template <typename T, typename It>
-static inline std::move_iterator<Transformer<T, It>>
-conversionIterator(const It& it) {
+inline std::move_iterator<Transformer<T, It>> conversionIterator(const It& it) {
return std::make_move_iterator(Transformer<T, It>(it));
}
///////////////////////////////////////////////////////////////////////////////
// DynamicConverter specializations
-template <typename T, typename Enable = void> struct DynamicConverter;
-
/**
* Each specialization of DynamicConverter has the function
- * 'static T convert(const dynamic& d);'
+ * 'static T convert(const dynamic&);'
*/
+// default - intentionally unimplemented
+template <typename T, typename Enable = void> struct DynamicConverter;
+
// boolean
template <>
struct DynamicConverter<bool> {
// integrals
template <typename T>
-struct DynamicConverter<T,
- typename std::enable_if<std::is_integral<T>::value &&
- !std::is_same<T, bool>::value>::type> {
+struct DynamicConverter<
+ T,
+ typename std::enable_if<
+ std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> {
+ static T convert(const dynamic& d) {
+ return folly::to<T>(d.asInt());
+ }
+};
+
+// enums
+template <typename T>
+struct DynamicConverter<
+ T,
+ typename std::enable_if<std::is_enum<T>::value>::type> {
static T convert(const dynamic& d) {
- return static_cast<T>(d.asInt());
+ using type = typename std::underlying_type<T>::type;
+ return static_cast<T>(DynamicConverter<type>::convert(d));
}
};
// floating point
template <typename T>
-struct DynamicConverter<T,
+struct DynamicConverter<
+ T,
typename std::enable_if<std::is_floating_point<T>::value>::type> {
static T convert(const dynamic& d) {
- return static_cast<T>(d.asDouble());
+ return folly::to<T>(d.asDouble());
}
};
template <>
struct DynamicConverter<std::string> {
static std::string convert(const dynamic& d) {
- return d.asString().toStdString();
+ return d.asString();
}
};
// std::pair
template <typename F, typename S>
-struct DynamicConverter<std::pair<F,S>> {
+struct DynamicConverter<std::pair<F, S>> {
static std::pair<F, S> convert(const dynamic& d) {
if (d.isArray() && d.size() == 2) {
return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
}
};
-// map containers
+// non-associative containers
template <typename C>
-struct DynamicConverter<C,
+struct DynamicConverter<
+ C,
typename std::enable_if<
- dynamicconverter_detail::is_map_container<C>::value>::type> {
+ dynamicconverter_detail::is_container<C>::value &&
+ !dynamicconverter_detail::is_associative<C>::value>::type> {
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>
(d.items().begin()),
dynamicconverter_detail::conversionIterator<C>
(d.items().end()));
- } else if (d.isArray()) {
- return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
- dynamicconverter_detail::conversionIterator<C>(d.end()));
} else {
throw TypeError("object or array", d.type());
}
}
};
-// non-map containers
+// associative 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
- > {
+struct DynamicConverter<
+ C,
+ typename std::enable_if<
+ dynamicconverter_detail::is_container<C>::value &&
+ dynamicconverter_detail::is_associative<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()));
+ C ret; // avoid direct initialization due to unordered_map's constructor
+ // causing memory corruption if the iterator throws an exception
+ if (d.isArray()) {
+ ret.insert(
+ dynamicconverter_detail::conversionIterator<C>(d.begin()),
+ dynamicconverter_detail::conversionIterator<C>(d.end()));
+ } else if (d.isObject()) {
+ ret.insert(
+ dynamicconverter_detail::conversionIterator<C>(d.items().begin()),
+ dynamicconverter_detail::conversionIterator<C>(d.items().end()));
} else {
- throw TypeError("array", d.type());
+ throw TypeError("object or array", d.type());
+ }
+ return ret;
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// DynamicConstructor specializations
+
+/**
+ * Each specialization of DynamicConstructor has the function
+ * 'static dynamic construct(const C&);'
+ */
+
+// default
+template <typename C, typename Enable = void>
+struct DynamicConstructor {
+ static dynamic construct(const C& x) {
+ return dynamic(x);
+ }
+};
+
+// identity
+template <typename C>
+struct DynamicConstructor<
+ C,
+ typename std::enable_if<std::is_same<C, dynamic>::value>::type> {
+ static dynamic construct(const C& x) {
+ return x;
+ }
+};
+
+// maps
+template <typename C>
+struct DynamicConstructor<
+ C,
+ typename std::enable_if<
+ !std::is_same<C, dynamic>::value &&
+ dynamicconverter_detail::is_map<C>::value>::type> {
+ static dynamic construct(const C& x) {
+ dynamic d = dynamic::object;
+ for (const auto& pair : x) {
+ d.insert(toDynamic(pair.first), toDynamic(pair.second));
}
+ return d;
+ }
+};
+
+// other ranges
+template <typename C>
+struct DynamicConstructor<
+ C,
+ typename std::enable_if<
+ !std::is_same<C, dynamic>::value &&
+ !dynamicconverter_detail::is_map<C>::value &&
+ !std::is_constructible<StringPiece, const C&>::value &&
+ dynamicconverter_detail::is_range<C>::value>::type> {
+ static dynamic construct(const C& x) {
+ dynamic d = dynamic::array;
+ for (const auto& item : x) {
+ d.push_back(toDynamic(item));
+ }
+ return d;
+ }
+};
+
+// pair
+template <typename A, typename B>
+struct DynamicConstructor<std::pair<A, B>, void> {
+ static dynamic construct(const std::pair<A, B>& x) {
+ dynamic d = dynamic::array;
+ d.push_back(toDynamic(x.first));
+ d.push_back(toDynamic(x.second));
+ return d;
}
};
///////////////////////////////////////////////////////////////////////////////
-// convertTo implementation
+// implementation
template <typename T>
T convertTo(const dynamic& d) {
- return DynamicConverter<T>::convert(d);
+ return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
}
-} // namespace folly
-
-#endif // DYNAMIC_CONVERTER_H
+template <typename T>
+dynamic toDynamic(const T& x) {
+ return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
+}
+} // namespace folly