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