2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 // @author Nicholas Ormrod <njormrod@fb.com>
22 #include <type_traits>
24 #include <boost/iterator/iterator_adaptor.hpp>
25 #include <boost/mpl/has_xxx.hpp>
27 #include <folly/Likely.h>
28 #include <folly/Optional.h>
29 #include <folly/Traits.h>
30 #include <folly/dynamic.h>
34 T convertTo(const dynamic&);
36 dynamic toDynamic(const T&);
40 * convertTo returns a well-typed representation of the input dynamic.
44 * dynamic d = dynamic::array(
45 * dynamic::array(1, 2, 3),
46 * dynamic::array(4, 5)); // a vector of vector of int
47 * auto vvi = convertTo<fbvector<fbvector<int>>>(d);
49 * See docs/DynamicConverter.md for supported types and customization
54 ///////////////////////////////////////////////////////////////////////////////
57 namespace dynamicconverter_detail {
59 BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
60 BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
61 BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
62 BOOST_MPL_HAS_XXX_TRAIT_DEF(key_type);
64 template <typename T> struct iterator_class_is_container {
65 typedef std::reverse_iterator<typename T::iterator> some_iterator;
66 enum { value = has_value_type<T>::value &&
67 std::is_constructible<T, some_iterator, some_iterator>::value };
71 using class_is_container =
72 Conjunction<has_iterator<T>, iterator_class_is_container<T>>;
75 using is_range = StrictConjunction<has_value_type<T>, has_iterator<T>>;
78 using is_container = StrictConjunction<std::is_class<T>, class_is_container<T>>;
81 using is_map = StrictConjunction<is_range<T>, has_mapped_type<T>>;
84 using is_associative = StrictConjunction<is_range<T>, has_key_type<T>>;
86 } // namespace dynamicconverter_detail
88 ///////////////////////////////////////////////////////////////////////////////
92 * We have iterators that dereference to dynamics, but need iterators
93 * that dereference to typename T.
95 * Implementation details:
96 * 1. We cache the value of the dereference operator. This is necessary
97 * because boost::iterator_adaptor requires *it to return a
99 * 2. For const reasons, we cannot call operator= to refresh the
100 * cache: we must call the destructor then placement new.
103 namespace dynamicconverter_detail {
105 template <typename T>
106 struct Dereferencer {
107 static inline void derefToCache(
108 Optional<T>* /* mem */,
109 const dynamic::const_item_iterator& /* it */) {
110 throw TypeError("array", dynamic::Type::OBJECT);
113 static inline void derefToCache(
115 const dynamic::const_iterator& it) {
116 mem->emplace(convertTo<T>(*it));
120 template <typename F, typename S>
121 struct Dereferencer<std::pair<F, S>> {
122 static inline void derefToCache(
123 Optional<std::pair<F, S>>* mem,
124 const dynamic::const_item_iterator& it) {
125 mem->emplace(convertTo<F>(it->first), convertTo<S>(it->second));
128 // Intentional duplication of the code in Dereferencer
129 template <typename T>
130 static inline void derefToCache(
132 const dynamic::const_iterator& it) {
133 mem->emplace(convertTo<T>(*it));
137 template <typename T, typename It>
140 iterator_adaptor<Transformer<T, It>, It, typename T::value_type> {
141 friend class boost::iterator_core_access;
143 typedef typename T::value_type ttype;
145 mutable Optional<ttype> cache_;
148 ++this->base_reference();
152 ttype& dereference() const {
154 Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
156 return cache_.value();
160 explicit Transformer(const It& it) : Transformer::iterator_adaptor_(it) {}
163 // conversion factory
164 template <typename T, typename It>
165 inline std::move_iterator<Transformer<T, It>> conversionIterator(const It& it) {
166 return std::make_move_iterator(Transformer<T, It>(it));
169 } // namespace dynamicconverter_detail
171 ///////////////////////////////////////////////////////////////////////////////
172 // DynamicConverter specializations
175 * Each specialization of DynamicConverter has the function
176 * 'static T convert(const dynamic&);'
179 // default - intentionally unimplemented
180 template <typename T, typename Enable = void> struct DynamicConverter;
184 struct DynamicConverter<bool> {
185 static bool convert(const dynamic& d) {
191 template <typename T>
192 struct DynamicConverter<
194 typename std::enable_if<
195 std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> {
196 static T convert(const dynamic& d) {
197 return folly::to<T>(d.asInt());
202 template <typename T>
203 struct DynamicConverter<
205 typename std::enable_if<std::is_enum<T>::value>::type> {
206 static T convert(const dynamic& d) {
207 using type = typename std::underlying_type<T>::type;
208 return static_cast<T>(DynamicConverter<type>::convert(d));
213 template <typename T>
214 struct DynamicConverter<
216 typename std::enable_if<std::is_floating_point<T>::value>::type> {
217 static T convert(const dynamic& d) {
218 return folly::to<T>(d.asDouble());
224 struct DynamicConverter<folly::fbstring> {
225 static folly::fbstring convert(const dynamic& d) {
232 struct DynamicConverter<std::string> {
233 static std::string convert(const dynamic& d) {
239 template <typename F, typename S>
240 struct DynamicConverter<std::pair<F, S>> {
241 static std::pair<F, S> convert(const dynamic& d) {
242 if (d.isArray() && d.size() == 2) {
243 return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
244 } else if (d.isObject() && d.size() == 1) {
245 auto it = d.items().begin();
246 return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
248 throw TypeError("array (size 2) or object (size 1)", d.type());
253 // non-associative containers
254 template <typename C>
255 struct DynamicConverter<
257 typename std::enable_if<
258 dynamicconverter_detail::is_container<C>::value &&
259 !dynamicconverter_detail::is_associative<C>::value>::type> {
260 static C convert(const dynamic& d) {
262 return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
263 dynamicconverter_detail::conversionIterator<C>(d.end()));
264 } else if (d.isObject()) {
265 return C(dynamicconverter_detail::conversionIterator<C>
267 dynamicconverter_detail::conversionIterator<C>
270 throw TypeError("object or array", d.type());
275 // associative containers
276 template <typename C>
277 struct DynamicConverter<
279 typename std::enable_if<
280 dynamicconverter_detail::is_container<C>::value &&
281 dynamicconverter_detail::is_associative<C>::value>::type> {
282 static C convert(const dynamic& d) {
283 C ret; // avoid direct initialization due to unordered_map's constructor
284 // causing memory corruption if the iterator throws an exception
287 dynamicconverter_detail::conversionIterator<C>(d.begin()),
288 dynamicconverter_detail::conversionIterator<C>(d.end()));
289 } else if (d.isObject()) {
291 dynamicconverter_detail::conversionIterator<C>(d.items().begin()),
292 dynamicconverter_detail::conversionIterator<C>(d.items().end()));
294 throw TypeError("object or array", d.type());
300 ///////////////////////////////////////////////////////////////////////////////
301 // DynamicConstructor specializations
304 * Each specialization of DynamicConstructor has the function
305 * 'static dynamic construct(const C&);'
309 template <typename C, typename Enable = void>
310 struct DynamicConstructor {
311 static dynamic construct(const C& x) {
317 template <typename C>
318 struct DynamicConstructor<
320 typename std::enable_if<std::is_same<C, dynamic>::value>::type> {
321 static dynamic construct(const C& x) {
327 template <typename C>
328 struct DynamicConstructor<
330 typename std::enable_if<
331 !std::is_same<C, dynamic>::value &&
332 dynamicconverter_detail::is_map<C>::value>::type> {
333 static dynamic construct(const C& x) {
334 dynamic d = dynamic::object;
335 for (const auto& pair : x) {
336 d.insert(toDynamic(pair.first), toDynamic(pair.second));
343 template <typename C>
344 struct DynamicConstructor<
346 typename std::enable_if<
347 !std::is_same<C, dynamic>::value &&
348 !dynamicconverter_detail::is_map<C>::value &&
349 !std::is_constructible<StringPiece, const C&>::value &&
350 dynamicconverter_detail::is_range<C>::value>::type> {
351 static dynamic construct(const C& x) {
352 dynamic d = dynamic::array;
353 for (const auto& item : x) {
354 d.push_back(toDynamic(item));
361 template <typename A, typename B>
362 struct DynamicConstructor<std::pair<A, B>, void> {
363 static dynamic construct(const std::pair<A, B>& x) {
364 dynamic d = dynamic::array;
365 d.push_back(toDynamic(x.first));
366 d.push_back(toDynamic(x.second));
371 ///////////////////////////////////////////////////////////////////////////////
374 template <typename T>
375 T convertTo(const dynamic& d) {
376 return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
379 template <typename T>
380 dynamic toDynamic(const T& x) {
381 return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);