Lua exception handling test
[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/Traits.h>
29 #include <folly/dynamic.h>
30
31 namespace folly {
32   template <typename T> T convertTo(const dynamic&);
33   template <typename T> dynamic toDynamic(const T&);
34 }
35
36 /**
37  * convertTo returns a well-typed representation of the input dynamic.
38  *
39  * Example:
40  *
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);
45  *
46  * See docs/DynamicConverter.md for supported types and customization
47  */
48
49 namespace folly {
50
51 ///////////////////////////////////////////////////////////////////////////////
52 // traits
53
54 namespace dynamicconverter_detail {
55
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);
60
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 };
65 };
66
67 template <typename T>
68 using class_is_container =
69     Conjunction<has_iterator<T>, iterator_class_is_container<T>>;
70
71 template <typename T>
72 using is_range = StrictConjunction<has_value_type<T>, has_iterator<T>>;
73
74 template <typename T>
75 using is_container = StrictConjunction<std::is_class<T>, class_is_container<T>>;
76
77 template <typename T>
78 using is_map = StrictConjunction<is_range<T>, has_mapped_type<T>>;
79
80 template <typename T>
81 using is_associative = StrictConjunction<is_range<T>, has_key_type<T>>;
82
83 } // namespace dynamicconverter_detail
84
85 ///////////////////////////////////////////////////////////////////////////////
86 // custom iterators
87
88 /**
89  * We have iterators that dereference to dynamics, but need iterators
90  * that dereference to typename T.
91  *
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
95  *      reference.
96  *   2. For const reasons, we cannot call operator= to refresh the
97  *      cache: we must call the destructor then placement new.
98  */
99
100 namespace dynamicconverter_detail {
101
102 template<typename T>
103 struct Dereferencer {
104   static inline void derefToCache(
105       T* /* mem */, const dynamic::const_item_iterator& /* it */) {
106     throw TypeError("array", dynamic::Type::OBJECT);
107   }
108
109   static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
110     new (mem) T(convertTo<T>(*it));
111   }
112 };
113
114 template<typename F, typename S>
115 struct Dereferencer<std::pair<F, S>> {
116   static inline void
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)
120     );
121   }
122
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));
127   }
128 };
129
130 template <typename T, typename It>
131 class Transformer
132     : public boost::
133           iterator_adaptor<Transformer<T, It>, It, typename T::value_type> {
134   friend class boost::iterator_core_access;
135
136   typedef typename T::value_type ttype;
137
138   mutable ttype cache_;
139   mutable bool valid_;
140
141   void increment() {
142     ++this->base_reference();
143     valid_ = false;
144   }
145
146   ttype& dereference() const {
147     if (LIKELY(!valid_)) {
148       cache_.~ttype();
149       Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
150       valid_ = true;
151     }
152     return cache_;
153   }
154
155  public:
156   explicit Transformer(const It& it)
157       : Transformer::iterator_adaptor_(it), valid_(false) {}
158 };
159
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));
164 }
165
166 } // namespace dynamicconverter_detail
167
168 ///////////////////////////////////////////////////////////////////////////////
169 // DynamicConverter specializations
170
171 /**
172  * Each specialization of DynamicConverter has the function
173  *     'static T convert(const dynamic&);'
174  */
175
176 // default - intentionally unimplemented
177 template <typename T, typename Enable = void> struct DynamicConverter;
178
179 // boolean
180 template <>
181 struct DynamicConverter<bool> {
182   static bool convert(const dynamic& d) {
183     return d.asBool();
184   }
185 };
186
187 // integrals
188 template <typename T>
189 struct DynamicConverter<
190     T,
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());
195   }
196 };
197
198 // enums
199 template <typename T>
200 struct DynamicConverter<
201     T,
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));
206   }
207 };
208
209 // floating point
210 template <typename T>
211 struct DynamicConverter<
212     T,
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());
216   }
217 };
218
219 // fbstring
220 template <>
221 struct DynamicConverter<folly::fbstring> {
222   static folly::fbstring convert(const dynamic& d) {
223     return d.asString();
224   }
225 };
226
227 // std::string
228 template <>
229 struct DynamicConverter<std::string> {
230   static std::string convert(const dynamic& d) {
231     return d.asString();
232   }
233 };
234
235 // std::pair
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));
244     } else {
245       throw TypeError("array (size 2) or object (size 1)", d.type());
246     }
247   }
248 };
249
250 // non-associative containers
251 template <typename C>
252 struct DynamicConverter<
253     C,
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) {
258     if (d.isArray()) {
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>
263                  (d.items().begin()),
264                dynamicconverter_detail::conversionIterator<C>
265                  (d.items().end()));
266     } else {
267       throw TypeError("object or array", d.type());
268     }
269   }
270 };
271
272 // associative containers
273 template <typename C>
274 struct DynamicConverter<
275     C,
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
282     if (d.isArray()) {
283       ret.insert(
284           dynamicconverter_detail::conversionIterator<C>(d.begin()),
285           dynamicconverter_detail::conversionIterator<C>(d.end()));
286     } else if (d.isObject()) {
287       ret.insert(
288           dynamicconverter_detail::conversionIterator<C>(d.items().begin()),
289           dynamicconverter_detail::conversionIterator<C>(d.items().end()));
290     } else {
291       throw TypeError("object or array", d.type());
292     }
293     return ret;
294   }
295 };
296
297 ///////////////////////////////////////////////////////////////////////////////
298 // DynamicConstructor specializations
299
300 /**
301  * Each specialization of DynamicConstructor has the function
302  *     'static dynamic construct(const C&);'
303  */
304
305 // default
306 template <typename C, typename Enable = void>
307 struct DynamicConstructor {
308   static dynamic construct(const C& x) {
309     return dynamic(x);
310   }
311 };
312
313 // identity
314 template <typename C>
315 struct DynamicConstructor<
316     C,
317     typename std::enable_if<std::is_same<C, dynamic>::value>::type> {
318   static dynamic construct(const C& x) {
319     return x;
320   }
321 };
322
323 // maps
324 template <typename C>
325 struct DynamicConstructor<
326     C,
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));
334     }
335     return d;
336   }
337 };
338
339 // other ranges
340 template <typename C>
341 struct DynamicConstructor<
342     C,
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));
352     }
353     return d;
354   }
355 };
356
357 // pair
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));
364     return d;
365   }
366 };
367
368 ///////////////////////////////////////////////////////////////////////////////
369 // implementation
370
371 template <typename T>
372 T convertTo(const dynamic& d) {
373   return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
374 }
375
376 template<typename T>
377 dynamic toDynamic(const T& x) {
378   return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
379 }
380
381 } // namespace folly