2 * Copyright 2016 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>
21 #include <folly/dynamic.h>
23 template <typename T> T convertTo(const dynamic&);
24 template <typename T> dynamic toDynamic(const T&);
28 * convertTo returns a well-typed representation of the input dynamic.
32 * dynamic d = dynamic::array(
33 * dynamic::array(1, 2, 3),
34 * dynamic::array(4, 5)); // a vector of vector of int
35 * auto vvi = convertTo<fbvector<fbvector<int>>>(d);
37 * See docs/DynamicConverter.md for supported types and customization
41 #include <type_traits>
43 #include <boost/iterator/iterator_adaptor.hpp>
44 #include <boost/mpl/has_xxx.hpp>
45 #include <folly/Likely.h>
49 ///////////////////////////////////////////////////////////////////////////////
52 namespace dynamicconverter_detail {
54 BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
55 BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
56 BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
58 template <typename T> struct iterator_class_is_container {
59 typedef std::reverse_iterator<typename T::iterator> some_iterator;
60 enum { value = has_value_type<T>::value &&
61 std::is_constructible<T, some_iterator, some_iterator>::value };
65 using class_is_container = typename
67 has_iterator<T>::value,
68 iterator_class_is_container<T>,
72 template <typename T> struct class_is_range {
73 enum { value = has_value_type<T>::value &&
74 has_iterator<T>::value };
78 template <typename T> struct is_container
80 std::is_class<T>::value,
81 class_is_container<T>,
85 template <typename T> struct is_range
87 std::is_class<T>::value,
92 template <typename T> struct is_map
93 : std::integral_constant<
95 is_range<T>::value && has_mapped_type<T>::value
98 } // namespace dynamicconverter_detail
100 ///////////////////////////////////////////////////////////////////////////////
104 * We have iterators that dereference to dynamics, but need iterators
105 * that dereference to typename T.
107 * Implementation details:
108 * 1. We cache the value of the dereference operator. This is necessary
109 * because boost::iterator_adaptor requires *it to return a
111 * 2. For const reasons, we cannot call operator= to refresh the
112 * cache: we must call the destructor then placement new.
115 namespace dynamicconverter_detail {
118 struct Dereferencer {
119 static inline void derefToCache(
120 T* /* mem */, const dynamic::const_item_iterator& /* it */) {
121 throw TypeError("array", dynamic::Type::OBJECT);
124 static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
125 new (mem) T(convertTo<T>(*it));
129 template<typename F, typename S>
130 struct Dereferencer<std::pair<F, S>> {
132 derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
133 new (mem) std::pair<F, S>(
134 convertTo<F>(it->first), convertTo<S>(it->second)
138 // Intentional duplication of the code in Dereferencer
139 template <typename T>
140 static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
141 new (mem) T(convertTo<T>(*it));
145 template <typename T, typename It>
146 class Transformer : public boost::iterator_adaptor<
149 typename T::value_type
151 friend class boost::iterator_core_access;
153 typedef typename T::value_type ttype;
155 mutable ttype cache_;
159 ++this->base_reference();
163 ttype& dereference() const {
164 if (LIKELY(!valid_)) {
166 Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
173 explicit Transformer(const It& it)
174 : Transformer::iterator_adaptor_(it), valid_(false) {}
177 // conversion factory
178 template <typename T, typename It>
179 inline std::move_iterator<Transformer<T, It>>
180 conversionIterator(const It& it) {
181 return std::make_move_iterator(Transformer<T, It>(it));
184 } // namespace dynamicconverter_detail
186 ///////////////////////////////////////////////////////////////////////////////
187 // DynamicConverter specializations
190 * Each specialization of DynamicConverter has the function
191 * 'static T convert(const dynamic&);'
194 // default - intentionally unimplemented
195 template <typename T, typename Enable = void> struct DynamicConverter;
199 struct DynamicConverter<bool> {
200 static bool convert(const dynamic& d) {
206 template <typename T>
207 struct DynamicConverter<T,
208 typename std::enable_if<std::is_integral<T>::value &&
209 !std::is_same<T, bool>::value>::type> {
210 static T convert(const dynamic& d) {
211 return folly::to<T>(d.asInt());
216 template <typename T>
217 struct DynamicConverter<T,
218 typename std::enable_if<std::is_enum<T>::value>::type> {
219 static T convert(const dynamic& d) {
220 using type = typename std::underlying_type<T>::type;
221 return static_cast<T>(DynamicConverter<type>::convert(d));
226 template <typename T>
227 struct DynamicConverter<T,
228 typename std::enable_if<std::is_floating_point<T>::value>::type> {
229 static T convert(const dynamic& d) {
230 return folly::to<T>(d.asDouble());
236 struct DynamicConverter<folly::fbstring> {
237 static folly::fbstring convert(const dynamic& d) {
244 struct DynamicConverter<std::string> {
245 static std::string convert(const dynamic& d) {
251 template <typename F, typename S>
252 struct DynamicConverter<std::pair<F,S>> {
253 static std::pair<F, S> convert(const dynamic& d) {
254 if (d.isArray() && d.size() == 2) {
255 return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
256 } else if (d.isObject() && d.size() == 1) {
257 auto it = d.items().begin();
258 return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
260 throw TypeError("array (size 2) or object (size 1)", d.type());
266 template <typename C>
267 struct DynamicConverter<C,
268 typename std::enable_if<
269 dynamicconverter_detail::is_container<C>::value>::type> {
270 static C convert(const dynamic& d) {
272 return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
273 dynamicconverter_detail::conversionIterator<C>(d.end()));
274 } else if (d.isObject()) {
275 return C(dynamicconverter_detail::conversionIterator<C>
277 dynamicconverter_detail::conversionIterator<C>
280 throw TypeError("object or array", d.type());
285 ///////////////////////////////////////////////////////////////////////////////
286 // DynamicConstructor specializations
289 * Each specialization of DynamicConstructor has the function
290 * 'static dynamic construct(const C&);'
294 template <typename C, typename Enable = void>
295 struct DynamicConstructor {
296 static dynamic construct(const C& x) {
303 struct DynamicConstructor<C,
304 typename std::enable_if<
305 dynamicconverter_detail::is_map<C>::value>::type> {
306 static dynamic construct(const C& x) {
307 dynamic d = dynamic::object;
308 for (auto& pair : x) {
309 d.insert(toDynamic(pair.first), toDynamic(pair.second));
317 struct DynamicConstructor<C,
318 typename std::enable_if<
319 !dynamicconverter_detail::is_map<C>::value &&
320 !std::is_constructible<StringPiece, const C&>::value &&
321 dynamicconverter_detail::is_range<C>::value>::type> {
322 static dynamic construct(const C& x) {
323 dynamic d = dynamic::array;
324 for (auto& item : x) {
325 d.push_back(toDynamic(item));
332 template<typename A, typename B>
333 struct DynamicConstructor<std::pair<A, B>, void> {
334 static dynamic construct(const std::pair<A, B>& x) {
335 dynamic d = dynamic::array;
336 d.push_back(toDynamic(x.first));
337 d.push_back(toDynamic(x.second));
342 ///////////////////////////////////////////////////////////////////////////////
345 template <typename T>
346 T convertTo(const dynamic& d) {
347 return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
351 dynamic toDynamic(const T& x) {
352 return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);