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