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