2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 // @author Nicholas Ormrod <njormrod@fb.com>
22 #include <type_traits>
24 #include <boost/iterator/iterator_adaptor.hpp>
25 #include <boost/mpl/has_xxx.hpp>
27 #include <folly/Likely.h>
28 #include <folly/Traits.h>
29 #include <folly/dynamic.h>
32 template <typename T> T convertTo(const dynamic&);
33 template <typename T> dynamic toDynamic(const T&);
37 * convertTo returns a well-typed representation of the input dynamic.
41 * dynamic d = dynamic::array(
42 * dynamic::array(1, 2, 3),
43 * dynamic::array(4, 5)); // a vector of vector of int
44 * auto vvi = convertTo<fbvector<fbvector<int>>>(d);
46 * See docs/DynamicConverter.md for supported types and customization
51 ///////////////////////////////////////////////////////////////////////////////
54 namespace dynamicconverter_detail {
56 BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
57 BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
58 BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
59 BOOST_MPL_HAS_XXX_TRAIT_DEF(key_type);
61 template <typename T> struct iterator_class_is_container {
62 typedef std::reverse_iterator<typename T::iterator> some_iterator;
63 enum { value = has_value_type<T>::value &&
64 std::is_constructible<T, some_iterator, some_iterator>::value };
68 using class_is_container =
69 Conjunction<has_iterator<T>, iterator_class_is_container<T>>;
72 using is_range = StrictConjunction<has_value_type<T>, has_iterator<T>>;
75 using is_container = StrictConjunction<std::is_class<T>, class_is_container<T>>;
78 using is_map = StrictConjunction<is_range<T>, has_mapped_type<T>>;
81 using is_associative = StrictConjunction<is_range<T>, has_key_type<T>>;
83 } // namespace dynamicconverter_detail
85 ///////////////////////////////////////////////////////////////////////////////
89 * We have iterators that dereference to dynamics, but need iterators
90 * that dereference to typename T.
92 * Implementation details:
93 * 1. We cache the value of the dereference operator. This is necessary
94 * because boost::iterator_adaptor requires *it to return a
96 * 2. For const reasons, we cannot call operator= to refresh the
97 * cache: we must call the destructor then placement new.
100 namespace dynamicconverter_detail {
103 struct Dereferencer {
104 static inline void derefToCache(
105 T* /* mem */, const dynamic::const_item_iterator& /* it */) {
106 throw TypeError("array", dynamic::Type::OBJECT);
109 static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
110 new (mem) T(convertTo<T>(*it));
114 template<typename F, typename S>
115 struct Dereferencer<std::pair<F, S>> {
117 derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
118 new (mem) std::pair<F, S>(
119 convertTo<F>(it->first), convertTo<S>(it->second)
123 // Intentional duplication of the code in Dereferencer
124 template <typename T>
125 static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
126 new (mem) T(convertTo<T>(*it));
130 template <typename T, typename It>
133 iterator_adaptor<Transformer<T, It>, It, typename T::value_type> {
134 friend class boost::iterator_core_access;
136 typedef typename T::value_type ttype;
138 mutable ttype cache_;
142 ++this->base_reference();
146 ttype& dereference() const {
147 if (LIKELY(!valid_)) {
149 Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
156 explicit Transformer(const It& it)
157 : Transformer::iterator_adaptor_(it), valid_(false) {}
160 // conversion factory
161 template <typename T, typename It>
162 inline std::move_iterator<Transformer<T, It>> conversionIterator(const It& it) {
163 return std::make_move_iterator(Transformer<T, It>(it));
166 } // namespace dynamicconverter_detail
168 ///////////////////////////////////////////////////////////////////////////////
169 // DynamicConverter specializations
172 * Each specialization of DynamicConverter has the function
173 * 'static T convert(const dynamic&);'
176 // default - intentionally unimplemented
177 template <typename T, typename Enable = void> struct DynamicConverter;
181 struct DynamicConverter<bool> {
182 static bool convert(const dynamic& d) {
188 template <typename T>
189 struct DynamicConverter<
191 typename std::enable_if<
192 std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> {
193 static T convert(const dynamic& d) {
194 return folly::to<T>(d.asInt());
199 template <typename T>
200 struct DynamicConverter<
202 typename std::enable_if<std::is_enum<T>::value>::type> {
203 static T convert(const dynamic& d) {
204 using type = typename std::underlying_type<T>::type;
205 return static_cast<T>(DynamicConverter<type>::convert(d));
210 template <typename T>
211 struct DynamicConverter<
213 typename std::enable_if<std::is_floating_point<T>::value>::type> {
214 static T convert(const dynamic& d) {
215 return folly::to<T>(d.asDouble());
221 struct DynamicConverter<folly::fbstring> {
222 static folly::fbstring convert(const dynamic& d) {
229 struct DynamicConverter<std::string> {
230 static std::string convert(const dynamic& d) {
236 template <typename F, typename S>
237 struct DynamicConverter<std::pair<F, S>> {
238 static std::pair<F, S> convert(const dynamic& d) {
239 if (d.isArray() && d.size() == 2) {
240 return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
241 } else if (d.isObject() && d.size() == 1) {
242 auto it = d.items().begin();
243 return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
245 throw TypeError("array (size 2) or object (size 1)", d.type());
250 // non-associative containers
251 template <typename C>
252 struct DynamicConverter<
254 typename std::enable_if<
255 dynamicconverter_detail::is_container<C>::value &&
256 !dynamicconverter_detail::is_associative<C>::value>::type> {
257 static C convert(const dynamic& d) {
259 return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
260 dynamicconverter_detail::conversionIterator<C>(d.end()));
261 } else if (d.isObject()) {
262 return C(dynamicconverter_detail::conversionIterator<C>
264 dynamicconverter_detail::conversionIterator<C>
267 throw TypeError("object or array", d.type());
272 // associative containers
273 template <typename C>
274 struct DynamicConverter<
276 typename std::enable_if<
277 dynamicconverter_detail::is_container<C>::value &&
278 dynamicconverter_detail::is_associative<C>::value>::type> {
279 static C convert(const dynamic& d) {
280 C ret; // avoid direct initialization due to unordered_map's constructor
281 // causing memory corruption if the iterator throws an exception
284 dynamicconverter_detail::conversionIterator<C>(d.begin()),
285 dynamicconverter_detail::conversionIterator<C>(d.end()));
286 } else if (d.isObject()) {
288 dynamicconverter_detail::conversionIterator<C>(d.items().begin()),
289 dynamicconverter_detail::conversionIterator<C>(d.items().end()));
291 throw TypeError("object or array", d.type());
297 ///////////////////////////////////////////////////////////////////////////////
298 // DynamicConstructor specializations
301 * Each specialization of DynamicConstructor has the function
302 * 'static dynamic construct(const C&);'
306 template <typename C, typename Enable = void>
307 struct DynamicConstructor {
308 static dynamic construct(const C& x) {
314 template <typename C>
315 struct DynamicConstructor<
317 typename std::enable_if<std::is_same<C, dynamic>::value>::type> {
318 static dynamic construct(const C& x) {
324 template <typename C>
325 struct DynamicConstructor<
327 typename std::enable_if<
328 !std::is_same<C, dynamic>::value &&
329 dynamicconverter_detail::is_map<C>::value>::type> {
330 static dynamic construct(const C& x) {
331 dynamic d = dynamic::object;
332 for (const auto& pair : x) {
333 d.insert(toDynamic(pair.first), toDynamic(pair.second));
340 template <typename C>
341 struct DynamicConstructor<
343 typename std::enable_if<
344 !std::is_same<C, dynamic>::value &&
345 !dynamicconverter_detail::is_map<C>::value &&
346 !std::is_constructible<StringPiece, const C&>::value &&
347 dynamicconverter_detail::is_range<C>::value>::type> {
348 static dynamic construct(const C& x) {
349 dynamic d = dynamic::array;
350 for (const auto& item : x) {
351 d.push_back(toDynamic(item));
358 template<typename A, typename B>
359 struct DynamicConstructor<std::pair<A, B>, void> {
360 static dynamic construct(const std::pair<A, B>& x) {
361 dynamic d = dynamic::array;
362 d.push_back(toDynamic(x.first));
363 d.push_back(toDynamic(x.second));
368 ///////////////////////////////////////////////////////////////////////////////
371 template <typename T>
372 T convertTo(const dynamic& d) {
373 return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
377 dynamic toDynamic(const T& x) {
378 return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);