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