| /* |
| * Copyright 2015 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. |
| */ |
| |
| #ifndef FOLLY_DYNAMIC_INL_H_ |
| #define FOLLY_DYNAMIC_INL_H_ |
| |
| #include <functional> |
| #include <boost/iterator/iterator_adaptor.hpp> |
| #include <boost/iterator/iterator_facade.hpp> |
| #include <folly/Likely.h> |
| #include <folly/Conv.h> |
| #include <folly/Format.h> |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| namespace std { |
| |
| template<> |
| struct hash< ::folly::dynamic> { |
| size_t operator()(::folly::dynamic const& d) const { |
| return d.hash(); |
| } |
| }; |
| |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| // This is a higher-order preprocessor macro to aid going from runtime |
| // types to the compile time type system. |
| #define FB_DYNAMIC_APPLY(type, apply) do { \ |
| switch ((type)) { \ |
| case NULLT: apply(void*); break; \ |
| case ARRAY: apply(Array); break; \ |
| case BOOL: apply(bool); break; \ |
| case DOUBLE: apply(double); break; \ |
| case INT64: apply(int64_t); break; \ |
| case OBJECT: apply(ObjectImpl); break; \ |
| case STRING: apply(fbstring); break; \ |
| default: CHECK(0); abort(); \ |
| } \ |
| } while (0) |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| namespace folly { |
| |
| struct TypeError : std::runtime_error { |
| explicit TypeError(const std::string& expected, dynamic::Type actual); |
| explicit TypeError(const std::string& expected, |
| dynamic::Type actual1, dynamic::Type actual2); |
| ~TypeError(); |
| }; |
| |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| namespace detail { |
| |
| // This helper is used in destroy() to be able to run destructors on |
| // types like "int64_t" without a compiler error. |
| struct Destroy { |
| template<class T> static void destroy(T* t) { t->~T(); } |
| }; |
| |
| /* |
| * The enable_if junk here is necessary to avoid ambiguous |
| * conversions relating to bool and double when you implicitly |
| * convert an int or long to a dynamic. |
| */ |
| template<class T, class Enable = void> struct ConversionHelper; |
| template<class T> |
| struct ConversionHelper< |
| T, |
| typename std::enable_if< |
| std::is_integral<T>::value && !std::is_same<T,bool>::value |
| >::type |
| > { |
| typedef int64_t type; |
| }; |
| template<class T> |
| struct ConversionHelper< |
| T, |
| typename std::enable_if< |
| (!std::is_integral<T>::value || std::is_same<T,bool>::value) && |
| !std::is_same<T,std::nullptr_t>::value |
| >::type |
| > { |
| typedef T type; |
| }; |
| template<class T> |
| struct ConversionHelper< |
| T, |
| typename std::enable_if< |
| std::is_same<T,std::nullptr_t>::value |
| >::type |
| > { |
| typedef void* type; |
| }; |
| |
| /* |
| * Helper for implementing numeric conversions in operators on |
| * numbers. Just promotes to double when one of the arguments is |
| * double, or throws if either is not a numeric type. |
| */ |
| template<template<class> class Op> |
| dynamic numericOp(dynamic const& a, dynamic const& b) { |
| if (!a.isNumber() || !b.isNumber()) { |
| throw TypeError("numeric", a.type(), b.type()); |
| } |
| if (a.type() != b.type()) { |
| auto& integ = a.isInt() ? a : b; |
| auto& nonint = a.isInt() ? b : a; |
| return Op<double>()(to<double>(integ.asInt()), nonint.asDouble()); |
| } |
| if (a.isDouble()) { |
| return Op<double>()(a.asDouble(), b.asDouble()); |
| } |
| return Op<int64_t>()(a.asInt(), b.asInt()); |
| } |
| |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| /* |
| * We're doing this instead of a simple member typedef to avoid the |
| * undefined behavior of parameterizing std::unordered_map<> with an |
| * incomplete type. |
| * |
| * Note: Later we may add separate order tracking here (a multi-index |
| * type of thing.) |
| */ |
| struct dynamic::ObjectImpl : std::unordered_map<dynamic, dynamic> {}; |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| // Helper object for creating objects conveniently. See object and |
| // the dynamic::dynamic(ObjectMaker&&) ctor. |
| struct dynamic::ObjectMaker { |
| friend struct dynamic; |
| |
| explicit ObjectMaker() : val_(dynamic::object) {} |
| explicit ObjectMaker(dynamic const& key, dynamic val) |
| : val_(dynamic::object) |
| { |
| val_.insert(key, std::move(val)); |
| } |
| explicit ObjectMaker(dynamic&& key, dynamic val) |
| : val_(dynamic::object) |
| { |
| val_.insert(std::move(key), std::move(val)); |
| } |
| |
| // Make sure no one tries to save one of these into an lvalue with |
| // auto or anything like that. |
| ObjectMaker(ObjectMaker&&) = default; |
| ObjectMaker(ObjectMaker const&) = delete; |
| ObjectMaker& operator=(ObjectMaker const&) = delete; |
| ObjectMaker& operator=(ObjectMaker&&) = delete; |
| |
| // These return rvalue-references instead of lvalue-references to allow |
| // constructs like this to moved instead of copied: |
| // dynamic a = dynamic::object("a", "b")("c", "d") |
| ObjectMaker&& operator()(dynamic const& key, dynamic val) { |
| val_.insert(key, std::move(val)); |
| return std::move(*this); |
| } |
| |
| ObjectMaker&& operator()(dynamic&& key, dynamic val) { |
| val_.insert(std::move(key), std::move(val)); |
| return std::move(*this); |
| } |
| |
| private: |
| dynamic val_; |
| }; |
| |
| // This looks like a case for perfect forwarding, but our use of |
| // std::initializer_list for constructing dynamic arrays makes it less |
| // functional than doing this manually. |
| inline dynamic::ObjectMaker dynamic::object() { return ObjectMaker(); } |
| inline dynamic::ObjectMaker dynamic::object(dynamic&& a, dynamic&& b) { |
| return ObjectMaker(std::move(a), std::move(b)); |
| } |
| inline dynamic::ObjectMaker dynamic::object(dynamic const& a, dynamic&& b) { |
| return ObjectMaker(a, std::move(b)); |
| } |
| inline dynamic::ObjectMaker dynamic::object(dynamic&& a, dynamic const& b) { |
| return ObjectMaker(std::move(a), b); |
| } |
| inline dynamic::ObjectMaker |
| dynamic::object(dynamic const& a, dynamic const& b) { |
| return ObjectMaker(a, b); |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| struct dynamic::const_item_iterator |
| : boost::iterator_adaptor<dynamic::const_item_iterator, |
| dynamic::ObjectImpl::const_iterator> { |
| /* implicit */ const_item_iterator(base_type b) : iterator_adaptor_(b) { } |
| |
| private: |
| friend class boost::iterator_core_access; |
| }; |
| |
| struct dynamic::const_key_iterator |
| : boost::iterator_adaptor<dynamic::const_key_iterator, |
| dynamic::ObjectImpl::const_iterator, |
| dynamic const> { |
| /* implicit */ const_key_iterator(base_type b) : iterator_adaptor_(b) { } |
| |
| private: |
| dynamic const& dereference() const { |
| return base_reference()->first; |
| } |
| friend class boost::iterator_core_access; |
| }; |
| |
| struct dynamic::const_value_iterator |
| : boost::iterator_adaptor<dynamic::const_value_iterator, |
| dynamic::ObjectImpl::const_iterator, |
| dynamic const> { |
| /* implicit */ const_value_iterator(base_type b) : iterator_adaptor_(b) { } |
| |
| private: |
| dynamic const& dereference() const { |
| return base_reference()->second; |
| } |
| friend class boost::iterator_core_access; |
| }; |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| inline dynamic::dynamic(ObjectMaker (*)()) |
| : type_(OBJECT) |
| { |
| new (getAddress<ObjectImpl>()) ObjectImpl(); |
| } |
| |
| inline dynamic::dynamic(StringPiece s) |
| : type_(STRING) |
| { |
| new (&u_.string) fbstring(s.data(), s.size()); |
| } |
| |
| inline dynamic::dynamic(char const* s) |
| : type_(STRING) |
| { |
| new (&u_.string) fbstring(s); |
| } |
| |
| inline dynamic::dynamic(std::string const& s) |
| : type_(STRING) |
| { |
| new (&u_.string) fbstring(s); |
| } |
| |
| inline dynamic::dynamic(fbstring const& s) |
| : type_(STRING) |
| { |
| new (&u_.string) fbstring(s); |
| } |
| |
| inline dynamic::dynamic(fbstring&& s) |
| : type_(STRING) |
| { |
| new (&u_.string) fbstring(std::move(s)); |
| } |
| |
| inline dynamic::dynamic(std::initializer_list<dynamic> il) |
| : type_(ARRAY) |
| { |
| new (&u_.array) Array(il.begin(), il.end()); |
| } |
| |
| inline dynamic::dynamic(ObjectMaker&& maker) |
| : type_(OBJECT) |
| { |
| new (getAddress<ObjectImpl>()) |
| ObjectImpl(std::move(*maker.val_.getAddress<ObjectImpl>())); |
| } |
| |
| inline dynamic::dynamic(dynamic const& o) |
| : type_(NULLT) |
| { |
| *this = o; |
| } |
| |
| inline dynamic::dynamic(dynamic&& o) noexcept |
| : type_(NULLT) |
| { |
| *this = std::move(o); |
| } |
| |
| inline dynamic::~dynamic() noexcept { destroy(); } |
| |
| template<class T> |
| dynamic::dynamic(T t) { |
| typedef typename detail::ConversionHelper<T>::type U; |
| type_ = TypeInfo<U>::type; |
| new (getAddress<U>()) U(std::move(t)); |
| } |
| |
| template<class Iterator> |
| dynamic::dynamic(Iterator first, Iterator last) |
| : type_(ARRAY) |
| { |
| new (&u_.array) Array(first, last); |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| inline dynamic::const_iterator dynamic::begin() const { |
| return get<Array>().begin(); |
| } |
| inline dynamic::const_iterator dynamic::end() const { |
| return get<Array>().end(); |
| } |
| |
| template <class It> |
| struct dynamic::IterableProxy { |
| typedef It const_iterator; |
| typedef typename It::value_type value_type; |
| |
| /* implicit */ IterableProxy(const dynamic::ObjectImpl* o) : o_(o) { } |
| |
| It begin() const { |
| return o_->begin(); |
| } |
| |
| It end() const { |
| return o_->end(); |
| } |
| |
| private: |
| const dynamic::ObjectImpl* o_; |
| }; |
| |
| inline dynamic::IterableProxy<dynamic::const_key_iterator> dynamic::keys() |
| const { |
| return &(get<ObjectImpl>()); |
| } |
| |
| inline dynamic::IterableProxy<dynamic::const_value_iterator> dynamic::values() |
| const { |
| return &(get<ObjectImpl>()); |
| } |
| |
| inline dynamic::IterableProxy<dynamic::const_item_iterator> dynamic::items() |
| const { |
| return &(get<ObjectImpl>()); |
| } |
| |
| inline bool dynamic::isString() const { return get_nothrow<fbstring>(); } |
| inline bool dynamic::isObject() const { return get_nothrow<ObjectImpl>(); } |
| inline bool dynamic::isBool() const { return get_nothrow<bool>(); } |
| inline bool dynamic::isArray() const { return get_nothrow<Array>(); } |
| inline bool dynamic::isDouble() const { return get_nothrow<double>(); } |
| inline bool dynamic::isInt() const { return get_nothrow<int64_t>(); } |
| inline bool dynamic::isNull() const { return get_nothrow<void*>(); } |
| inline bool dynamic::isNumber() const { return isInt() || isDouble(); } |
| |
| inline dynamic::Type dynamic::type() const { |
| return type_; |
| } |
| |
| inline fbstring dynamic::asString() const { return asImpl<fbstring>(); } |
| inline double dynamic::asDouble() const { return asImpl<double>(); } |
| inline int64_t dynamic::asInt() const { return asImpl<int64_t>(); } |
| inline bool dynamic::asBool() const { return asImpl<bool>(); } |
| |
| inline const fbstring& dynamic::getString() const& { return get<fbstring>(); } |
| inline double dynamic::getDouble() const& { return get<double>(); } |
| inline int64_t dynamic::getInt() const& { return get<int64_t>(); } |
| inline bool dynamic::getBool() const& { return get<bool>(); } |
| |
| inline fbstring& dynamic::getString() & { return get<fbstring>(); } |
| inline double& dynamic::getDouble() & { return get<double>(); } |
| inline int64_t& dynamic::getInt() & { return get<int64_t>(); } |
| inline bool& dynamic::getBool() & { return get<bool>(); } |
| |
| inline fbstring dynamic::getString() && { return std::move(get<fbstring>()); } |
| inline double dynamic::getDouble() && { return get<double>(); } |
| inline int64_t dynamic::getInt() && { return get<int64_t>(); } |
| inline bool dynamic::getBool() && { return get<bool>(); } |
| |
| inline const char* dynamic::data() const& { return get<fbstring>().data(); } |
| inline const char* dynamic::c_str() const& { return get<fbstring>().c_str(); } |
| inline StringPiece dynamic::stringPiece() const { return get<fbstring>(); } |
| |
| template<class T> |
| struct dynamic::CompareOp { |
| static bool comp(T const& a, T const& b) { return a < b; } |
| }; |
| template<> |
| struct dynamic::CompareOp<dynamic::ObjectImpl> { |
| static bool comp(ObjectImpl const&, ObjectImpl const&) { |
| // This code never executes; it is just here for the compiler. |
| return false; |
| } |
| }; |
| |
| inline dynamic& dynamic::operator+=(dynamic const& o) { |
| if (type() == STRING && o.type() == STRING) { |
| *getAddress<fbstring>() += *o.getAddress<fbstring>(); |
| return *this; |
| } |
| *this = detail::numericOp<std::plus>(*this, o); |
| return *this; |
| } |
| |
| inline dynamic& dynamic::operator-=(dynamic const& o) { |
| *this = detail::numericOp<std::minus>(*this, o); |
| return *this; |
| } |
| |
| inline dynamic& dynamic::operator*=(dynamic const& o) { |
| *this = detail::numericOp<std::multiplies>(*this, o); |
| return *this; |
| } |
| |
| inline dynamic& dynamic::operator/=(dynamic const& o) { |
| *this = detail::numericOp<std::divides>(*this, o); |
| return *this; |
| } |
| |
| #define FB_DYNAMIC_INTEGER_OP(op) \ |
| inline dynamic& dynamic::operator op(dynamic const& o) { \ |
| if (!isInt() || !o.isInt()) { \ |
| throw TypeError("int64", type(), o.type()); \ |
| } \ |
| *getAddress<int64_t>() op o.asInt(); \ |
| return *this; \ |
| } |
| |
| FB_DYNAMIC_INTEGER_OP(%=) |
| FB_DYNAMIC_INTEGER_OP(|=) |
| FB_DYNAMIC_INTEGER_OP(&=) |
| FB_DYNAMIC_INTEGER_OP(^=) |
| |
| #undef FB_DYNAMIC_INTEGER_OP |
| |
| inline dynamic& dynamic::operator++() { |
| ++get<int64_t>(); |
| return *this; |
| } |
| |
| inline dynamic& dynamic::operator--() { |
| --get<int64_t>(); |
| return *this; |
| } |
| |
| inline dynamic const& dynamic::operator[](dynamic const& idx) const& { |
| return at(idx); |
| } |
| |
| inline dynamic dynamic::operator[](dynamic const& idx) && { |
| return std::move((*this)[idx]); |
| } |
| |
| template<class K, class V> inline dynamic& dynamic::setDefault(K&& k, V&& v) { |
| auto& obj = get<ObjectImpl>(); |
| return obj.insert(std::make_pair(std::forward<K>(k), |
| std::forward<V>(v))).first->second; |
| } |
| |
| inline dynamic* dynamic::get_ptr(dynamic const& idx) & { |
| return const_cast<dynamic*>(const_cast<dynamic const*>(this)->get_ptr(idx)); |
| } |
| |
| inline dynamic& dynamic::at(dynamic const& idx) & { |
| return const_cast<dynamic&>(const_cast<dynamic const*>(this)->at(idx)); |
| } |
| |
| inline dynamic dynamic::at(dynamic const& idx) && { |
| return std::move(at(idx)); |
| } |
| |
| inline bool dynamic::empty() const { |
| if (isNull()) { |
| return true; |
| } |
| return !size(); |
| } |
| |
| inline std::size_t dynamic::count(dynamic const& key) const { |
| return find(key) != items().end(); |
| } |
| |
| inline dynamic::const_item_iterator dynamic::find(dynamic const& key) const { |
| return get<ObjectImpl>().find(key); |
| } |
| |
| template<class K, class V> inline void dynamic::insert(K&& key, V&& val) { |
| auto& obj = get<ObjectImpl>(); |
| auto rv = obj.insert({ std::forward<K>(key), nullptr }); |
| rv.first->second = std::forward<V>(val); |
| } |
| |
| inline std::size_t dynamic::erase(dynamic const& key) { |
| auto& obj = get<ObjectImpl>(); |
| return obj.erase(key); |
| } |
| |
| inline dynamic::const_iterator dynamic::erase(const_iterator it) { |
| auto& arr = get<Array>(); |
| // std::vector doesn't have an erase method that works on const iterators, |
| // even though the standard says it should, so this hack converts to a |
| // non-const iterator before calling erase. |
| return get<Array>().erase(arr.begin() + (it - arr.begin())); |
| } |
| |
| inline dynamic::const_key_iterator dynamic::erase(const_key_iterator it) { |
| return const_key_iterator(get<ObjectImpl>().erase(it.base())); |
| } |
| |
| inline dynamic::const_key_iterator dynamic::erase(const_key_iterator first, |
| const_key_iterator last) { |
| return const_key_iterator(get<ObjectImpl>().erase(first.base(), |
| last.base())); |
| } |
| |
| inline dynamic::const_value_iterator dynamic::erase(const_value_iterator it) { |
| return const_value_iterator(get<ObjectImpl>().erase(it.base())); |
| } |
| |
| inline dynamic::const_value_iterator dynamic::erase(const_value_iterator first, |
| const_value_iterator last) { |
| return const_value_iterator(get<ObjectImpl>().erase(first.base(), |
| last.base())); |
| } |
| |
| inline dynamic::const_item_iterator dynamic::erase(const_item_iterator it) { |
| return const_item_iterator(get<ObjectImpl>().erase(it.base())); |
| } |
| |
| inline dynamic::const_item_iterator dynamic::erase(const_item_iterator first, |
| const_item_iterator last) { |
| return const_item_iterator(get<ObjectImpl>().erase(first.base(), |
| last.base())); |
| } |
| |
| inline void dynamic::resize(std::size_t sz, dynamic const& c) { |
| auto& array = get<Array>(); |
| array.resize(sz, c); |
| } |
| |
| inline void dynamic::push_back(dynamic const& v) { |
| auto& array = get<Array>(); |
| array.push_back(v); |
| } |
| |
| inline void dynamic::push_back(dynamic&& v) { |
| auto& array = get<Array>(); |
| array.push_back(std::move(v)); |
| } |
| |
| inline void dynamic::pop_back() { |
| auto& array = get<Array>(); |
| array.pop_back(); |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| template<class T> struct dynamic::TypeInfo { |
| static char const name[]; |
| static Type const type; |
| }; |
| |
| #define FB_DEC_TYPE(T) \ |
| template<> char const dynamic::TypeInfo<T>::name[]; \ |
| template<> dynamic::Type const dynamic::TypeInfo<T>::type |
| |
| FB_DEC_TYPE(void*); |
| FB_DEC_TYPE(bool); |
| FB_DEC_TYPE(fbstring); |
| FB_DEC_TYPE(dynamic::Array); |
| FB_DEC_TYPE(double); |
| FB_DEC_TYPE(int64_t); |
| FB_DEC_TYPE(dynamic::ObjectImpl); |
| |
| #undef FB_DEC_TYPE |
| |
| template<class T> |
| T dynamic::asImpl() const { |
| switch (type()) { |
| case INT64: return to<T>(*get_nothrow<int64_t>()); |
| case DOUBLE: return to<T>(*get_nothrow<double>()); |
| case BOOL: return to<T>(*get_nothrow<bool>()); |
| case STRING: return to<T>(*get_nothrow<fbstring>()); |
| default: |
| throw TypeError("int/double/bool/string", type()); |
| } |
| } |
| |
| // Return a T* to our type, or null if we're not that type. |
| template<class T> |
| T* dynamic::get_nothrow() & noexcept { |
| if (type_ != TypeInfo<T>::type) { |
| return nullptr; |
| } |
| return getAddress<T>(); |
| } |
| |
| template<class T> |
| T const* dynamic::get_nothrow() const& noexcept { |
| return const_cast<dynamic*>(this)->get_nothrow<T>(); |
| } |
| |
| // Return T* for where we can put a T, without type checking. (Memory |
| // might be uninitialized, even.) |
| template<class T> |
| T* dynamic::getAddress() noexcept { |
| return GetAddrImpl<T>::get(u_); |
| } |
| |
| template<class T> |
| T const* dynamic::getAddress() const noexcept { |
| return const_cast<dynamic*>(this)->getAddress<T>(); |
| } |
| |
| template<class T> struct dynamic::GetAddrImpl {}; |
| template<> struct dynamic::GetAddrImpl<void*> { |
| static void** get(Data& d) noexcept { return &d.nul; } |
| }; |
| template<> struct dynamic::GetAddrImpl<dynamic::Array> { |
| static Array* get(Data& d) noexcept { return &d.array; } |
| }; |
| template<> struct dynamic::GetAddrImpl<bool> { |
| static bool* get(Data& d) noexcept { return &d.boolean; } |
| }; |
| template<> struct dynamic::GetAddrImpl<int64_t> { |
| static int64_t* get(Data& d) noexcept { return &d.integer; } |
| }; |
| template<> struct dynamic::GetAddrImpl<double> { |
| static double* get(Data& d) noexcept { return &d.doubl; } |
| }; |
| template<> struct dynamic::GetAddrImpl<fbstring> { |
| static fbstring* get(Data& d) noexcept { return &d.string; } |
| }; |
| template<> struct dynamic::GetAddrImpl<dynamic::ObjectImpl> { |
| static_assert(sizeof(ObjectImpl) <= sizeof(Data::objectBuffer), |
| "In your implementation, std::unordered_map<> apparently takes different" |
| " amount of space depending on its template parameters. This is " |
| "weird. Make objectBuffer bigger if you want to compile dynamic."); |
| |
| static ObjectImpl* get(Data& d) noexcept { |
| void* data = &d.objectBuffer; |
| return static_cast<ObjectImpl*>(data); |
| } |
| }; |
| |
| template<class T> |
| T& dynamic::get() { |
| if (auto* p = get_nothrow<T>()) { |
| return *p; |
| } |
| throw TypeError(TypeInfo<T>::name, type()); |
| } |
| |
| template<class T> |
| T const& dynamic::get() const { |
| return const_cast<dynamic*>(this)->get<T>(); |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| /* |
| * Helper for implementing operator<<. Throws if the type shouldn't |
| * support it. |
| */ |
| template<class T> |
| struct dynamic::PrintImpl { |
| static void print(dynamic const&, std::ostream& out, T const& t) { |
| out << t; |
| } |
| }; |
| template<> |
| struct dynamic::PrintImpl<dynamic::ObjectImpl> { |
| static void print(dynamic const& d, |
| std::ostream& out, |
| dynamic::ObjectImpl const&) { |
| } |
| }; |
| template<> |
| struct dynamic::PrintImpl<dynamic::Array> { |
| static void print(dynamic const& d, |
| std::ostream& out, |
| dynamic::Array const&) { |
| } |
| }; |
| |
| inline void dynamic::print(std::ostream& out) const { |
| #define FB_X(T) PrintImpl<T>::print(*this, out, *getAddress<T>()) |
| FB_DYNAMIC_APPLY(type_, FB_X); |
| #undef FB_X |
| } |
| |
| inline std::ostream& operator<<(std::ostream& out, dynamic const& d) { |
| d.print(out); |
| return out; |
| } |
| |
| ////////////////////////////////////////////////////////////////////// |
| |
| // Secialization of FormatValue so dynamic objects can be formatted |
| template <> |
| class FormatValue<dynamic> { |
| public: |
| explicit FormatValue(const dynamic& val) : val_(val) { } |
| |
| template <class FormatCallback> |
| void format(FormatArg& arg, FormatCallback& cb) const { |
| switch (val_.type()) { |
| case dynamic::NULLT: |
| FormatValue<std::nullptr_t>(nullptr).format(arg, cb); |
| break; |
| case dynamic::BOOL: |
| FormatValue<bool>(val_.asBool()).format(arg, cb); |
| break; |
| case dynamic::INT64: |
| FormatValue<int64_t>(val_.asInt()).format(arg, cb); |
| break; |
| case dynamic::STRING: |
| FormatValue<fbstring>(val_.asString()).format(arg, cb); |
| break; |
| case dynamic::DOUBLE: |
| FormatValue<double>(val_.asDouble()).format(arg, cb); |
| break; |
| case dynamic::ARRAY: |
| FormatValue(val_.at(arg.splitIntKey())).format(arg, cb); |
| break; |
| case dynamic::OBJECT: |
| FormatValue(val_.at(arg.splitKey().toFbstring())).format(arg, cb); |
| break; |
| } |
| } |
| |
| private: |
| const dynamic& val_; |
| }; |
| |
| template <class V> |
| class FormatValue<detail::DefaultValueWrapper<dynamic, V>> { |
| public: |
| explicit FormatValue( |
| const detail::DefaultValueWrapper<dynamic, V>& val) |
| : val_(val) { } |
| |
| template <class FormatCallback> |
| void format(FormatArg& arg, FormatCallback& cb) const { |
| auto& c = val_.container; |
| switch (c.type()) { |
| case dynamic::NULLT: |
| case dynamic::BOOL: |
| case dynamic::INT64: |
| case dynamic::STRING: |
| case dynamic::DOUBLE: |
| FormatValue<dynamic>(c).format(arg, cb); |
| break; |
| case dynamic::ARRAY: |
| { |
| int key = arg.splitIntKey(); |
| if (key >= 0 && size_t(key) < c.size()) { |
| FormatValue<dynamic>(c.at(key)).format(arg, cb); |
| } else{ |
| FormatValue<V>(val_.defaultValue).format(arg, cb); |
| } |
| } |
| break; |
| case dynamic::OBJECT: |
| { |
| auto pos = c.find(arg.splitKey()); |
| if (pos != c.items().end()) { |
| FormatValue<dynamic>(pos->second).format(arg, cb); |
| } else { |
| FormatValue<V>(val_.defaultValue).format(arg, cb); |
| } |
| } |
| break; |
| } |
| } |
| |
| private: |
| const detail::DefaultValueWrapper<dynamic, V>& val_; |
| }; |
| |
| } // namespaces |
| |
| #undef FB_DYNAMIC_APPLY |
| |
| #endif |