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