2 * Copyright 2015 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>
19 #ifndef DYNAMIC_CONVERTER_H
20 #define DYNAMIC_CONVERTER_H
22 #include <folly/dynamic.h>
24 template <typename T> T convertTo(const dynamic&);
25 template <typename T> dynamic toDynamic(const T&);
29 * convertTo returns a well-typed representation of the input dynamic.
33 * dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int
34 * auto vvi = convertTo<fbvector<fbvector<int>>>(d);
36 * See docs/DynamicConverter.md for supported types and customization
40 #include <type_traits>
42 #include <boost/iterator/iterator_adaptor.hpp>
43 #include <boost/mpl/has_xxx.hpp>
44 #include <folly/Likely.h>
48 ///////////////////////////////////////////////////////////////////////////////
51 namespace dynamicconverter_detail {
53 BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
54 BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
55 BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
57 template <typename T> struct iterator_class_is_container {
58 typedef std::reverse_iterator<typename T::iterator> some_iterator;
59 enum { value = has_value_type<T>::value &&
60 std::is_constructible<T, some_iterator, some_iterator>::value };
64 using class_is_container = typename
66 has_iterator<T>::value,
67 iterator_class_is_container<T>,
71 template <typename T> struct class_is_range {
72 enum { value = has_value_type<T>::value &&
73 has_iterator<T>::value };
77 template <typename T> struct is_container
79 std::is_class<T>::value,
80 class_is_container<T>,
84 template <typename T> struct is_range
86 std::is_class<T>::value,
91 template <typename T> struct is_map
92 : std::integral_constant<
94 is_range<T>::value && has_mapped_type<T>::value
97 } // namespace dynamicconverter_detail
99 ///////////////////////////////////////////////////////////////////////////////
103 * We have iterators that dereference to dynamics, but need iterators
104 * that dereference to typename T.
106 * Implementation details:
107 * 1. We cache the value of the dereference operator. This is necessary
108 * because boost::iterator_adaptor requires *it to return a
110 * 2. For const reasons, we cannot call operator= to refresh the
111 * cache: we must call the destructor then placement new.
114 namespace dynamicconverter_detail {
117 struct Dereferencer {
119 derefToCache(T* mem, const dynamic::const_item_iterator& it) {
120 throw TypeError("array", dynamic::Type::OBJECT);
123 static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
124 new (mem) T(convertTo<T>(*it));
128 template<typename F, typename S>
129 struct Dereferencer<std::pair<F, S>> {
131 derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
132 new (mem) std::pair<F, S>(
133 convertTo<F>(it->first), convertTo<S>(it->second)
137 // Intentional duplication of the code in Dereferencer
138 template <typename T>
139 static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
140 new (mem) T(convertTo<T>(*it));
144 template <typename T, typename It>
145 class Transformer : public boost::iterator_adaptor<
148 typename T::value_type
150 friend class boost::iterator_core_access;
152 typedef typename T::value_type ttype;
154 mutable ttype cache_;
158 ++this->base_reference();
162 ttype& dereference() const {
163 if (LIKELY(!valid_)) {
165 Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
172 explicit Transformer(const It& it)
173 : Transformer::iterator_adaptor_(it), valid_(false) {}
176 // conversion factory
177 template <typename T, typename It>
178 inline std::move_iterator<Transformer<T, It>>
179 conversionIterator(const It& it) {
180 return std::make_move_iterator(Transformer<T, It>(it));
183 } // namespace dynamicconverter_detail
185 ///////////////////////////////////////////////////////////////////////////////
186 // DynamicConverter specializations
189 * Each specialization of DynamicConverter has the function
190 * 'static T convert(const dynamic&);'
193 // default - intentionally unimplemented
194 template <typename T, typename Enable = void> struct DynamicConverter;
198 struct DynamicConverter<bool> {
199 static bool convert(const dynamic& d) {
205 template <typename T>
206 struct DynamicConverter<T,
207 typename std::enable_if<std::is_integral<T>::value &&
208 !std::is_same<T, bool>::value>::type> {
209 static T convert(const dynamic& d) {
210 return folly::to<T>(d.asInt());
215 template <typename T>
216 struct DynamicConverter<T,
217 typename std::enable_if<std::is_floating_point<T>::value>::type> {
218 static T convert(const dynamic& d) {
219 return folly::to<T>(d.asDouble());
225 struct DynamicConverter<folly::fbstring> {
226 static folly::fbstring convert(const dynamic& d) {
233 struct DynamicConverter<std::string> {
234 static std::string convert(const dynamic& d) {
235 return d.asString().toStdString();
240 template <typename F, typename S>
241 struct DynamicConverter<std::pair<F,S>> {
242 static std::pair<F, S> convert(const dynamic& d) {
243 if (d.isArray() && d.size() == 2) {
244 return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
245 } else if (d.isObject() && d.size() == 1) {
246 auto it = d.items().begin();
247 return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
249 throw TypeError("array (size 2) or object (size 1)", d.type());
255 template <typename C>
256 struct DynamicConverter<C,
257 typename std::enable_if<
258 dynamicconverter_detail::is_container<C>::value>::type> {
259 static C convert(const dynamic& d) {
261 return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
262 dynamicconverter_detail::conversionIterator<C>(d.end()));
263 } else if (d.isObject()) {
264 return C(dynamicconverter_detail::conversionIterator<C>
266 dynamicconverter_detail::conversionIterator<C>
269 throw TypeError("object or array", d.type());
274 ///////////////////////////////////////////////////////////////////////////////
275 // DynamicConstructor specializations
278 * Each specialization of DynamicConstructor has the function
279 * 'static dynamic construct(const C&);'
283 template <typename C, typename Enable = void>
284 struct DynamicConstructor {
285 static dynamic construct(const C& x) {
292 struct DynamicConstructor<C,
293 typename std::enable_if<
294 dynamicconverter_detail::is_map<C>::value>::type> {
295 static dynamic construct(const C& x) {
296 dynamic d = dynamic::object;
297 for (auto& pair : x) {
298 d.insert(toDynamic(pair.first), toDynamic(pair.second));
306 struct DynamicConstructor<C,
307 typename std::enable_if<
308 !dynamicconverter_detail::is_map<C>::value &&
309 !std::is_constructible<StringPiece, const C&>::value &&
310 dynamicconverter_detail::is_range<C>::value>::type> {
311 static dynamic construct(const C& x) {
313 for (auto& item : x) {
314 d.push_back(toDynamic(item));
321 template<typename A, typename B>
322 struct DynamicConstructor<std::pair<A, B>, void> {
323 static dynamic construct(const std::pair<A, B>& x) {
325 d.push_back(toDynamic(x.first));
326 d.push_back(toDynamic(x.second));
331 ///////////////////////////////////////////////////////////////////////////////
334 template <typename T>
335 T convertTo(const dynamic& d) {
336 return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
340 dynamic toDynamic(const T& x) {
341 return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
346 #endif // DYNAMIC_CONVERTER_H