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