Move folly/Hash.h to folly/hash/, leaving a shim
[folly.git] / folly / DynamicConverter.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 // @author Nicholas Ormrod <njormrod@fb.com>
18
19 #pragma once
20
21 #include <iterator>
22 #include <type_traits>
23
24 #include <boost/iterator/iterator_adaptor.hpp>
25 #include <boost/mpl/has_xxx.hpp>
26
27 #include <folly/Likely.h>
28 #include <folly/Optional.h>
29 #include <folly/Traits.h>
30 #include <folly/dynamic.h>
31
32 namespace folly {
33 template <typename T>
34 T convertTo(const dynamic&);
35 template <typename T>
36 dynamic toDynamic(const T&);
37 } // namespace folly
38
39 /**
40  * convertTo returns a well-typed representation of the input dynamic.
41  *
42  * Example:
43  *
44  *   dynamic d = dynamic::array(
45  *       dynamic::array(1, 2, 3),
46  *       dynamic::array(4, 5)); // a vector of vector of int
47  *   auto vvi = convertTo<fbvector<fbvector<int>>>(d);
48  *
49  * See docs/DynamicConverter.md for supported types and customization
50  */
51
52 namespace folly {
53
54 ///////////////////////////////////////////////////////////////////////////////
55 // traits
56
57 namespace dynamicconverter_detail {
58
59 BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
60 BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
61 BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
62 BOOST_MPL_HAS_XXX_TRAIT_DEF(key_type);
63
64 template <typename T> struct iterator_class_is_container {
65   typedef std::reverse_iterator<typename T::iterator> some_iterator;
66   enum { value = has_value_type<T>::value &&
67               std::is_constructible<T, some_iterator, some_iterator>::value };
68 };
69
70 template <typename T>
71 using class_is_container =
72     Conjunction<has_iterator<T>, iterator_class_is_container<T>>;
73
74 template <typename T>
75 using is_range = StrictConjunction<has_value_type<T>, has_iterator<T>>;
76
77 template <typename T>
78 using is_container = StrictConjunction<std::is_class<T>, class_is_container<T>>;
79
80 template <typename T>
81 using is_map = StrictConjunction<is_range<T>, has_mapped_type<T>>;
82
83 template <typename T>
84 using is_associative = StrictConjunction<is_range<T>, has_key_type<T>>;
85
86 } // namespace dynamicconverter_detail
87
88 ///////////////////////////////////////////////////////////////////////////////
89 // custom iterators
90
91 /**
92  * We have iterators that dereference to dynamics, but need iterators
93  * that dereference to typename T.
94  *
95  * Implementation details:
96  *   1. We cache the value of the dereference operator. This is necessary
97  *      because boost::iterator_adaptor requires *it to return a
98  *      reference.
99  *   2. For const reasons, we cannot call operator= to refresh the
100  *      cache: we must call the destructor then placement new.
101  */
102
103 namespace dynamicconverter_detail {
104
105 template <typename T>
106 struct Dereferencer {
107   static inline void derefToCache(
108       Optional<T>* /* mem */,
109       const dynamic::const_item_iterator& /* it */) {
110     throw TypeError("array", dynamic::Type::OBJECT);
111   }
112
113   static inline void derefToCache(
114       Optional<T>* mem,
115       const dynamic::const_iterator& it) {
116     mem->emplace(convertTo<T>(*it));
117   }
118 };
119
120 template <typename F, typename S>
121 struct Dereferencer<std::pair<F, S>> {
122   static inline void derefToCache(
123       Optional<std::pair<F, S>>* mem,
124       const dynamic::const_item_iterator& it) {
125     mem->emplace(convertTo<F>(it->first), convertTo<S>(it->second));
126   }
127
128   // Intentional duplication of the code in Dereferencer
129   template <typename T>
130   static inline void derefToCache(
131       Optional<T>* mem,
132       const dynamic::const_iterator& it) {
133     mem->emplace(convertTo<T>(*it));
134   }
135 };
136
137 template <typename T, typename It>
138 class Transformer
139     : public boost::
140           iterator_adaptor<Transformer<T, It>, It, typename T::value_type> {
141   friend class boost::iterator_core_access;
142
143   typedef typename T::value_type ttype;
144
145   mutable Optional<ttype> cache_;
146
147   void increment() {
148     ++this->base_reference();
149     cache_ = none;
150   }
151
152   ttype& dereference() const {
153     if (!cache_) {
154       Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
155     }
156     return cache_.value();
157   }
158
159  public:
160   explicit Transformer(const It& it) : Transformer::iterator_adaptor_(it) {}
161 };
162
163 // conversion factory
164 template <typename T, typename It>
165 inline std::move_iterator<Transformer<T, It>> conversionIterator(const It& it) {
166   return std::make_move_iterator(Transformer<T, It>(it));
167 }
168
169 } // namespace dynamicconverter_detail
170
171 ///////////////////////////////////////////////////////////////////////////////
172 // DynamicConverter specializations
173
174 /**
175  * Each specialization of DynamicConverter has the function
176  *     'static T convert(const dynamic&);'
177  */
178
179 // default - intentionally unimplemented
180 template <typename T, typename Enable = void> struct DynamicConverter;
181
182 // boolean
183 template <>
184 struct DynamicConverter<bool> {
185   static bool convert(const dynamic& d) {
186     return d.asBool();
187   }
188 };
189
190 // integrals
191 template <typename T>
192 struct DynamicConverter<
193     T,
194     typename std::enable_if<
195         std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> {
196   static T convert(const dynamic& d) {
197     return folly::to<T>(d.asInt());
198   }
199 };
200
201 // enums
202 template <typename T>
203 struct DynamicConverter<
204     T,
205     typename std::enable_if<std::is_enum<T>::value>::type> {
206   static T convert(const dynamic& d) {
207     using type = typename std::underlying_type<T>::type;
208     return static_cast<T>(DynamicConverter<type>::convert(d));
209   }
210 };
211
212 // floating point
213 template <typename T>
214 struct DynamicConverter<
215     T,
216     typename std::enable_if<std::is_floating_point<T>::value>::type> {
217   static T convert(const dynamic& d) {
218     return folly::to<T>(d.asDouble());
219   }
220 };
221
222 // fbstring
223 template <>
224 struct DynamicConverter<folly::fbstring> {
225   static folly::fbstring convert(const dynamic& d) {
226     return d.asString();
227   }
228 };
229
230 // std::string
231 template <>
232 struct DynamicConverter<std::string> {
233   static std::string convert(const dynamic& d) {
234     return d.asString();
235   }
236 };
237
238 // std::pair
239 template <typename F, typename S>
240 struct DynamicConverter<std::pair<F, S>> {
241   static std::pair<F, S> convert(const dynamic& d) {
242     if (d.isArray() && d.size() == 2) {
243       return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
244     } else if (d.isObject() && d.size() == 1) {
245       auto it = d.items().begin();
246       return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
247     } else {
248       throw TypeError("array (size 2) or object (size 1)", d.type());
249     }
250   }
251 };
252
253 // non-associative containers
254 template <typename C>
255 struct DynamicConverter<
256     C,
257     typename std::enable_if<
258         dynamicconverter_detail::is_container<C>::value &&
259         !dynamicconverter_detail::is_associative<C>::value>::type> {
260   static C convert(const dynamic& d) {
261     if (d.isArray()) {
262       return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
263                dynamicconverter_detail::conversionIterator<C>(d.end()));
264     } else if (d.isObject()) {
265       return C(dynamicconverter_detail::conversionIterator<C>
266                  (d.items().begin()),
267                dynamicconverter_detail::conversionIterator<C>
268                  (d.items().end()));
269     } else {
270       throw TypeError("object or array", d.type());
271     }
272   }
273 };
274
275 // associative containers
276 template <typename C>
277 struct DynamicConverter<
278     C,
279     typename std::enable_if<
280         dynamicconverter_detail::is_container<C>::value &&
281         dynamicconverter_detail::is_associative<C>::value>::type> {
282   static C convert(const dynamic& d) {
283     C ret; // avoid direct initialization due to unordered_map's constructor
284            // causing memory corruption if the iterator throws an exception
285     if (d.isArray()) {
286       ret.insert(
287           dynamicconverter_detail::conversionIterator<C>(d.begin()),
288           dynamicconverter_detail::conversionIterator<C>(d.end()));
289     } else if (d.isObject()) {
290       ret.insert(
291           dynamicconverter_detail::conversionIterator<C>(d.items().begin()),
292           dynamicconverter_detail::conversionIterator<C>(d.items().end()));
293     } else {
294       throw TypeError("object or array", d.type());
295     }
296     return ret;
297   }
298 };
299
300 ///////////////////////////////////////////////////////////////////////////////
301 // DynamicConstructor specializations
302
303 /**
304  * Each specialization of DynamicConstructor has the function
305  *     'static dynamic construct(const C&);'
306  */
307
308 // default
309 template <typename C, typename Enable = void>
310 struct DynamicConstructor {
311   static dynamic construct(const C& x) {
312     return dynamic(x);
313   }
314 };
315
316 // identity
317 template <typename C>
318 struct DynamicConstructor<
319     C,
320     typename std::enable_if<std::is_same<C, dynamic>::value>::type> {
321   static dynamic construct(const C& x) {
322     return x;
323   }
324 };
325
326 // maps
327 template <typename C>
328 struct DynamicConstructor<
329     C,
330     typename std::enable_if<
331         !std::is_same<C, dynamic>::value &&
332         dynamicconverter_detail::is_map<C>::value>::type> {
333   static dynamic construct(const C& x) {
334     dynamic d = dynamic::object;
335     for (const auto& pair : x) {
336       d.insert(toDynamic(pair.first), toDynamic(pair.second));
337     }
338     return d;
339   }
340 };
341
342 // other ranges
343 template <typename C>
344 struct DynamicConstructor<
345     C,
346     typename std::enable_if<
347         !std::is_same<C, dynamic>::value &&
348         !dynamicconverter_detail::is_map<C>::value &&
349         !std::is_constructible<StringPiece, const C&>::value &&
350         dynamicconverter_detail::is_range<C>::value>::type> {
351   static dynamic construct(const C& x) {
352     dynamic d = dynamic::array;
353     for (const auto& item : x) {
354       d.push_back(toDynamic(item));
355     }
356     return d;
357   }
358 };
359
360 // pair
361 template <typename A, typename B>
362 struct DynamicConstructor<std::pair<A, B>, void> {
363   static dynamic construct(const std::pair<A, B>& x) {
364     dynamic d = dynamic::array;
365     d.push_back(toDynamic(x.first));
366     d.push_back(toDynamic(x.second));
367     return d;
368   }
369 };
370
371 ///////////////////////////////////////////////////////////////////////////////
372 // implementation
373
374 template <typename T>
375 T convertTo(const dynamic& d) {
376   return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
377 }
378
379 template <typename T>
380 dynamic toDynamic(const T& x) {
381   return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
382 }
383
384 } // namespace folly