/* * Copyright 2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // @author Nicholas Ormrod #pragma once #include namespace folly { template T convertTo(const dynamic&); template dynamic toDynamic(const T&); } /** * convertTo returns a well-typed representation of the input dynamic. * * Example: * * dynamic d = dynamic::array( * dynamic::array(1, 2, 3), * dynamic::array(4, 5)); // a vector of vector of int * auto vvi = convertTo>>(d); * * See docs/DynamicConverter.md for supported types and customization */ #include #include #include #include #include namespace folly { /////////////////////////////////////////////////////////////////////////////// // traits namespace dynamicconverter_detail { BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type); BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator); BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type); template struct iterator_class_is_container { typedef std::reverse_iterator some_iterator; enum { value = has_value_type::value && std::is_constructible::value }; }; template using class_is_container = typename std::conditional< has_iterator::value, iterator_class_is_container, std::false_type >::type; template struct class_is_range { enum { value = has_value_type::value && has_iterator::value }; }; template struct is_container : std::conditional< std::is_class::value, class_is_container, std::false_type >::type {}; template struct is_range : std::conditional< std::is_class::value, class_is_range, std::false_type >::type {}; template struct is_map : std::integral_constant< bool, is_range::value && has_mapped_type::value > {}; } // namespace dynamicconverter_detail /////////////////////////////////////////////////////////////////////////////// // custom iterators /** * We have iterators that dereference to dynamics, but need iterators * that dereference to typename T. * * Implementation details: * 1. We cache the value of the dereference operator. This is necessary * because boost::iterator_adaptor requires *it to return a * reference. * 2. For const reasons, we cannot call operator= to refresh the * cache: we must call the destructor then placement new. */ namespace dynamicconverter_detail { template struct Dereferencer { static inline void derefToCache( T* /* mem */, const dynamic::const_item_iterator& /* it */) { throw TypeError("array", dynamic::Type::OBJECT); } static inline void derefToCache(T* mem, const dynamic::const_iterator& it) { new (mem) T(convertTo(*it)); } }; template struct Dereferencer> { static inline void derefToCache(std::pair* mem, const dynamic::const_item_iterator& it) { new (mem) std::pair( convertTo(it->first), convertTo(it->second) ); } // Intentional duplication of the code in Dereferencer template static inline void derefToCache(T* mem, const dynamic::const_iterator& it) { new (mem) T(convertTo(*it)); } }; template class Transformer : public boost::iterator_adaptor< Transformer, It, typename T::value_type > { friend class boost::iterator_core_access; typedef typename T::value_type ttype; mutable ttype cache_; mutable bool valid_; void increment() { ++this->base_reference(); valid_ = false; } ttype& dereference() const { if (LIKELY(!valid_)) { cache_.~ttype(); Dereferencer::derefToCache(&cache_, this->base_reference()); valid_ = true; } return cache_; } public: explicit Transformer(const It& it) : Transformer::iterator_adaptor_(it), valid_(false) {} }; // conversion factory template inline std::move_iterator> conversionIterator(const It& it) { return std::make_move_iterator(Transformer(it)); } } // namespace dynamicconverter_detail /////////////////////////////////////////////////////////////////////////////// // DynamicConverter specializations /** * Each specialization of DynamicConverter has the function * 'static T convert(const dynamic&);' */ // default - intentionally unimplemented template struct DynamicConverter; // boolean template <> struct DynamicConverter { static bool convert(const dynamic& d) { return d.asBool(); } }; // integrals template struct DynamicConverter::value && !std::is_same::value>::type> { static T convert(const dynamic& d) { return folly::to(d.asInt()); } }; // enums template struct DynamicConverter::value>::type> { static T convert(const dynamic& d) { using type = typename std::underlying_type::type; return static_cast(DynamicConverter::convert(d)); } }; // floating point template struct DynamicConverter::value>::type> { static T convert(const dynamic& d) { return folly::to(d.asDouble()); } }; // fbstring template <> struct DynamicConverter { static folly::fbstring convert(const dynamic& d) { return d.asString(); } }; // std::string template <> struct DynamicConverter { static std::string convert(const dynamic& d) { return d.asString(); } }; // std::pair template struct DynamicConverter> { static std::pair convert(const dynamic& d) { if (d.isArray() && d.size() == 2) { return std::make_pair(convertTo(d[0]), convertTo(d[1])); } else if (d.isObject() && d.size() == 1) { auto it = d.items().begin(); return std::make_pair(convertTo(it->first), convertTo(it->second)); } else { throw TypeError("array (size 2) or object (size 1)", d.type()); } } }; // containers template struct DynamicConverter::value>::type> { static C convert(const dynamic& d) { if (d.isArray()) { return C(dynamicconverter_detail::conversionIterator(d.begin()), dynamicconverter_detail::conversionIterator(d.end())); } else if (d.isObject()) { return C(dynamicconverter_detail::conversionIterator (d.items().begin()), dynamicconverter_detail::conversionIterator (d.items().end())); } else { throw TypeError("object or array", d.type()); } } }; /////////////////////////////////////////////////////////////////////////////// // DynamicConstructor specializations /** * Each specialization of DynamicConstructor has the function * 'static dynamic construct(const C&);' */ // default template struct DynamicConstructor { static dynamic construct(const C& x) { return dynamic(x); } }; // maps template struct DynamicConstructor::value>::type> { static dynamic construct(const C& x) { dynamic d = dynamic::object; for (auto& pair : x) { d.insert(toDynamic(pair.first), toDynamic(pair.second)); } return d; } }; // other ranges template struct DynamicConstructor::value && !std::is_constructible::value && dynamicconverter_detail::is_range::value>::type> { static dynamic construct(const C& x) { dynamic d = dynamic::array; for (auto& item : x) { d.push_back(toDynamic(item)); } return d; } }; // pair template struct DynamicConstructor, void> { static dynamic construct(const std::pair& x) { dynamic d = dynamic::array; d.push_back(toDynamic(x.first)); d.push_back(toDynamic(x.second)); return d; } }; /////////////////////////////////////////////////////////////////////////////// // implementation template T convertTo(const dynamic& d) { return DynamicConverter::type>::convert(d); } template dynamic toDynamic(const T& x) { return DynamicConstructor::type>::construct(x); } } // namespace folly