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