2 * Copyright 2012 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&);
28 * convertTo returns a well-typed representation of the input dynamic.
32 * dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int
33 * auto vvi = convertTo<fbvector<fbvector<int>>>(d);
35 * See docs/DynamicConverter.md for supported types and customization
39 #include <type_traits>
40 #include <boost/iterator/iterator_adaptor.hpp>
41 #include <boost/mpl/has_xxx.hpp>
42 #include "folly/Likely.h"
46 ///////////////////////////////////////////////////////////////////////////////
49 namespace dynamicconverter_detail {
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);
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> {};
61 template <typename T> struct class_is_container {
63 enum { value = has_value_type<T>::value &&
64 has_iterator<T>::value &&
65 std::is_constructible<T, dummy, dummy>::value };
68 template <typename T> struct container_is_map
70 has_key_type<T>::value && has_mapped_type<T>::value,
71 map_container_has_correct_types<T>,
75 template <typename T> struct is_container
77 std::is_class<T>::value,
78 class_is_container<T>,
82 template <typename T> struct is_map_container
84 is_container<T>::value,
89 } // namespace dynamicconverter_detail
91 ///////////////////////////////////////////////////////////////////////////////
95 * We have iterators that dereference to dynamics, but need iterators
96 * that dereference to typename T.
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
102 * 2. For const reasons, we cannot call operator= to refresh the
103 * cache: we must call the destructor then placement new.
106 namespace dynamicconverter_detail {
108 template <typename F, typename S>
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));
114 template <typename T>
115 inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
116 new (mem) T(convertTo<T>(*it));
119 template <typename T, typename It>
120 class Transformer : public boost::iterator_adaptor<
123 typename T::value_type
125 friend class boost::iterator_core_access;
127 typedef typename T::value_type ttype;
129 mutable ttype cache_;
133 ++this->base_reference();
137 ttype& dereference() const {
138 if (LIKELY(!valid_)) {
140 derefToCache(&cache_, this->base_reference());
147 explicit Transformer(const It& it)
148 : Transformer::iterator_adaptor_(it), valid_(false) {}
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));
158 } // namespace dynamicconverter_detail
160 ///////////////////////////////////////////////////////////////////////////////
161 // DynamicConverter specializations
163 template <typename T, typename Enable = void> struct DynamicConverter;
166 * Each specialization of DynamicConverter has the function
167 * 'static T convert(const dynamic& d);'
172 struct DynamicConverter<bool> {
173 static bool convert(const dynamic& d) {
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());
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());
199 struct DynamicConverter<folly::fbstring> {
200 static folly::fbstring convert(const dynamic& d) {
207 struct DynamicConverter<std::string> {
208 static std::string convert(const dynamic& d) {
209 return d.asString().toStdString();
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));
223 throw TypeError("array (size 2) or object (size 1)", d.type());
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>
237 dynamicconverter_detail::conversionIterator<C>
239 } else if (d.isArray()) {
240 return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
241 dynamicconverter_detail::conversionIterator<C>(d.end()));
243 throw TypeError("object or array", d.type());
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
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()));
261 throw TypeError("array", d.type());
266 ///////////////////////////////////////////////////////////////////////////////
267 // convertTo implementation
269 template <typename T>
270 T convertTo(const dynamic& d) {
271 return DynamicConverter<T>::convert(d);
276 #endif // DYNAMIC_CONVERTER_H