Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 278 lines (232 sloc) 7.731 kb
5b67454 Added dynamic::convertTo<Type> method
Nicholas Ormrod authored
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<T>::convert(d);
272 }
273
274 } // namespace folly
275
276 #endif // DYNAMIC_CONVERTER_H
277
Something went wrong with that request. Please try again.