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