Add a LICENSE file for folly
[folly.git] / folly / dynamic-inl.h
1 /*
2  * Copyright 2012 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #ifndef FOLLY_DYNAMIC_INL_H_
18 #define FOLLY_DYNAMIC_INL_H_
19
20 #include <functional>
21 #include <boost/iterator/iterator_adaptor.hpp>
22 #include <boost/iterator/iterator_facade.hpp>
23 #include "folly/Likely.h"
24 #include "folly/Conv.h"
25 #include "folly/Format.h"
26
27 //////////////////////////////////////////////////////////////////////
28
29 namespace std {
30
31 template<>
32 struct hash< ::folly::dynamic> {
33   size_t operator()(::folly::dynamic const& d) const {
34     return d.hash();
35   }
36 };
37
38 }
39
40 //////////////////////////////////////////////////////////////////////
41
42 // This is a higher-order preprocessor macro to aid going from runtime
43 // types to the compile time type system.
44 #define FB_DYNAMIC_APPLY(type, apply) do {         \
45   switch ((type)) {                             \
46   case NULLT:   apply(void*);          break;   \
47   case ARRAY:   apply(Array);          break;   \
48   case BOOL:    apply(bool);           break;   \
49   case DOUBLE:  apply(double);         break;   \
50   case INT64:   apply(int64_t);        break;   \
51   case OBJECT:  apply(ObjectImpl);     break;   \
52   case STRING:  apply(fbstring);       break;   \
53   default:      CHECK(0); abort();              \
54   }                                             \
55 } while (0)
56
57 //////////////////////////////////////////////////////////////////////
58
59 namespace folly {
60
61 //////////////////////////////////////////////////////////////////////
62
63 namespace detail {
64
65   // This helper is used in destroy() to be able to run destructors on
66   // types like "int64_t" without a compiler error.
67   struct Destroy {
68     template<class T> static void destroy(T* t) { t->~T(); }
69   };
70
71   /*
72    * The enable_if junk here is necessary to avoid ambiguous
73    * conversions relating to bool and double when you implicitly
74    * convert an int or long to a dynamic.
75    */
76   template<class T, class Enable = void> struct ConversionHelper;
77   template<class T>
78   struct ConversionHelper<
79     T,
80     typename std::enable_if<
81       std::is_integral<T>::value && !std::is_same<T,bool>::value
82     >::type
83   > {
84     typedef int64_t type;
85   };
86   template<class T>
87   struct ConversionHelper<
88     T,
89     typename std::enable_if<
90       (!std::is_integral<T>::value || std::is_same<T,bool>::value) &&
91       !std::is_same<T,std::nullptr_t>::value
92     >::type
93   > {
94     typedef T type;
95   };
96   template<class T>
97   struct ConversionHelper<
98     T,
99     typename std::enable_if<
100       std::is_same<T,std::nullptr_t>::value
101     >::type
102   > {
103     typedef void* type;
104   };
105
106   /*
107    * Helper for implementing numeric conversions in operators on
108    * numbers.  Just promotes to double when one of the arguments is
109    * double, or throws if either is not a numeric type.
110    */
111   template<template<class> class Op>
112   dynamic numericOp(dynamic const& a, dynamic const& b) {
113     if (!a.isNumber() || !b.isNumber()) {
114       throw TypeError("numeric", a.type(), b.type());
115     }
116     if (a.type() != b.type()) {
117       auto& integ  = a.isInt() ? a : b;
118       auto& nonint = a.isInt() ? b : a;
119       return Op<double>()(to<double>(integ.asInt()), nonint.asDouble());
120     }
121     if (a.isDouble()) {
122       return Op<double>()(a.asDouble(), b.asDouble());
123     }
124     return Op<int64_t>()(a.asInt(), b.asInt());
125   }
126
127 }
128
129 //////////////////////////////////////////////////////////////////////
130
131 struct TypeError : std::runtime_error {
132   explicit TypeError(const std::string& expected, dynamic::Type actual)
133     : std::runtime_error(to<std::string>("TypeError: expected dynamic "
134         "type `", expected, '\'', ", but had type `",
135         dynamic::typeName(actual), '\''))
136   {}
137   explicit TypeError(const std::string& expected,
138       dynamic::Type actual1, dynamic::Type actual2)
139     : std::runtime_error(to<std::string>("TypeError: expected dynamic "
140         "types `", expected, '\'', ", but had types `",
141         dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
142         '\''))
143   {}
144 };
145
146 /*
147  * We're doing this instead of a simple member typedef to avoid the
148  * undefined behavior of parameterizing std::unordered_map<> with an
149  * incomplete type.
150  *
151  * Note: Later we may add separate order tracking here (a multi-index
152  * type of thing.)
153  */
154 struct dynamic::ObjectImpl : std::unordered_map<dynamic, dynamic> {};
155
156 //////////////////////////////////////////////////////////////////////
157
158 // Helper object for creating objects conveniently.  See object and
159 // the dynamic::dynamic(ObjectMaker&&) ctor.
160 struct dynamic::ObjectMaker {
161   friend struct dynamic;
162
163   explicit ObjectMaker() : val_(dynamic::object) {}
164   explicit ObjectMaker(dynamic const& key, dynamic val)
165     : val_(dynamic::object)
166   {
167     val_.insert(key, std::move(val));
168   }
169   explicit ObjectMaker(dynamic&& key, dynamic val)
170     : val_(dynamic::object)
171   {
172     val_.insert(std::move(key), std::move(val));
173   }
174
175   // Make sure no one tries to save one of these into an lvalue with
176   // auto or anything like that.
177   ObjectMaker(ObjectMaker&&) = default;
178   ObjectMaker(ObjectMaker const&) = delete;
179   ObjectMaker& operator=(ObjectMaker const&) = delete;
180   ObjectMaker& operator=(ObjectMaker&&) = delete;
181
182   // These return rvalue-references instead of lvalue-references to allow
183   // constructs like this to moved instead of copied:
184   //  dynamic a = dynamic::object("a", "b")("c", "d")
185   ObjectMaker&& operator()(dynamic const& key, dynamic val) {
186     val_.insert(key, std::move(val));
187     return std::move(*this);
188   }
189
190   ObjectMaker&& operator()(dynamic&& key, dynamic val) {
191     val_.insert(std::move(key), std::move(val));
192     return std::move(*this);
193   }
194
195 private:
196   dynamic val_;
197 };
198
199 template<class... Args>
200 inline dynamic::ObjectMaker dynamic::object(Args&&... args) {
201   return dynamic::ObjectMaker(std::forward<Args>(args)...);
202 }
203
204 //////////////////////////////////////////////////////////////////////
205
206 struct dynamic::const_item_iterator
207   : boost::iterator_adaptor<dynamic::const_item_iterator,
208                             dynamic::ObjectImpl::const_iterator> {
209   /* implicit */ const_item_iterator(base_type b) : iterator_adaptor_(b) { }
210
211  private:
212   friend class boost::iterator_core_access;
213 };
214
215 struct dynamic::const_key_iterator
216   : boost::iterator_adaptor<dynamic::const_key_iterator,
217                             dynamic::ObjectImpl::const_iterator,
218                             dynamic const> {
219   /* implicit */ const_key_iterator(base_type b) : iterator_adaptor_(b) { }
220
221  private:
222   dynamic const& dereference() const {
223     return base_reference()->first;
224   }
225   friend class boost::iterator_core_access;
226 };
227
228 struct dynamic::const_value_iterator
229   : boost::iterator_adaptor<dynamic::const_value_iterator,
230                             dynamic::ObjectImpl::const_iterator,
231                             dynamic const> {
232   /* implicit */ const_value_iterator(base_type b) : iterator_adaptor_(b) { }
233
234  private:
235   dynamic const& dereference() const {
236     return base_reference()->second;
237   }
238   friend class boost::iterator_core_access;
239 };
240
241 //////////////////////////////////////////////////////////////////////
242
243 inline dynamic::dynamic(ObjectMaker (*)())
244   : type_(OBJECT)
245 {
246   new (getAddress<ObjectImpl>()) ObjectImpl();
247 }
248
249 inline dynamic::dynamic(char const* s)
250   : type_(STRING)
251 {
252   new (&u_.string) fbstring(s);
253 }
254
255 inline dynamic::dynamic(std::string const& s)
256   : type_(STRING)
257 {
258   new (&u_.string) fbstring(s);
259 }
260
261 inline dynamic::dynamic(std::initializer_list<dynamic> il)
262   : type_(ARRAY)
263 {
264   new (&u_.array) Array(il.begin(), il.end());
265 }
266
267 inline dynamic::dynamic(ObjectMaker&& maker)
268   : type_(OBJECT)
269 {
270   new (getAddress<ObjectImpl>())
271     ObjectImpl(std::move(*maker.val_.getAddress<ObjectImpl>()));
272 }
273
274 inline dynamic::dynamic(dynamic const& o)
275   : type_(NULLT)
276 {
277   *this = o;
278 }
279
280 inline dynamic::dynamic(dynamic&& o)
281   : type_(NULLT)
282 {
283   *this = std::move(o);
284 }
285
286 inline dynamic::~dynamic() { destroy(); }
287
288 template<class T>
289 dynamic::dynamic(T t) {
290   typedef typename detail::ConversionHelper<T>::type U;
291   type_ = TypeInfo<U>::type;
292   new (getAddress<U>()) U(std::move(t));
293 }
294
295 template<class Iterator>
296 dynamic::dynamic(Iterator first, Iterator last)
297   : type_(ARRAY)
298 {
299   new (&u_.array) Array(first, last);
300 }
301
302 //////////////////////////////////////////////////////////////////////
303
304 inline dynamic::const_iterator dynamic::begin() const {
305   return get<Array>().begin();
306 }
307 inline dynamic::const_iterator dynamic::end() const {
308   return get<Array>().end();
309 }
310
311 template <class It>
312 struct dynamic::IterableProxy {
313   typedef It const_iterator;
314
315   /* implicit */ IterableProxy(const dynamic::ObjectImpl* o) : o_(o) { }
316
317   It begin() const {
318     return o_->begin();
319   }
320
321   It end() const {
322     return o_->end();
323   }
324
325  private:
326   const dynamic::ObjectImpl* o_;
327 };
328
329 inline dynamic::IterableProxy<dynamic::const_key_iterator> dynamic::keys()
330   const {
331   return &(get<ObjectImpl>());
332 }
333
334 inline dynamic::IterableProxy<dynamic::const_value_iterator> dynamic::values()
335   const {
336   return &(get<ObjectImpl>());
337 }
338
339 inline dynamic::IterableProxy<dynamic::const_item_iterator> dynamic::items()
340   const {
341   return &(get<ObjectImpl>());
342 }
343
344 inline bool dynamic::isString() const { return get_nothrow<fbstring>(); }
345 inline bool dynamic::isObject() const { return get_nothrow<ObjectImpl>(); }
346 inline bool dynamic::isBool()   const { return get_nothrow<bool>(); }
347 inline bool dynamic::isArray()  const { return get_nothrow<Array>(); }
348 inline bool dynamic::isDouble() const { return get_nothrow<double>(); }
349 inline bool dynamic::isInt()    const { return get_nothrow<int64_t>(); }
350 inline bool dynamic::isNull()   const { return get_nothrow<void*>(); }
351 inline bool dynamic::isNumber() const { return isInt() || isDouble(); }
352
353 inline dynamic::Type dynamic::type() const {
354   return type_;
355 }
356
357 inline fbstring dynamic::asString() const { return asImpl<fbstring>(); }
358 inline double   dynamic::asDouble() const { return asImpl<double>(); }
359 inline int64_t  dynamic::asInt()    const { return asImpl<int64_t>(); }
360 inline bool     dynamic::asBool()   const { return asImpl<bool>(); }
361
362 template<class T>
363 struct dynamic::CompareOp {
364   static bool comp(T const& a, T const& b) { return a < b; }
365 };
366 template<>
367 struct dynamic::CompareOp<dynamic::ObjectImpl> {
368   static bool comp(ObjectImpl const& a, ObjectImpl const& b) {
369     // This code never executes; it is just here for the compiler.
370     return false;
371   }
372 };
373
374 inline bool dynamic::operator<(dynamic const& o) const {
375   if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
376     throw TypeError("object", type_);
377   }
378   if (type_ != o.type_) {
379     return type_ < o.type_;
380   }
381
382 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(),   \
383                                           *o.getAddress<T>())
384   FB_DYNAMIC_APPLY(type_, FB_X);
385 #undef FB_X
386 }
387
388 inline bool dynamic::operator==(dynamic const& o) const {
389   if (type() != o.type()) {
390     if (isNumber() && o.isNumber()) {
391       auto& integ = isInt() ? *this : o;
392       auto& doubl = isInt() ? o     : *this;
393       return integ.asInt() == doubl.asDouble();
394     }
395     return false;
396   }
397
398 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
399   FB_DYNAMIC_APPLY(type_, FB_X);
400 #undef FB_X
401 }
402
403 inline dynamic& dynamic::operator+=(dynamic const& o) {
404   if (type() == STRING && o.type() == STRING) {
405     *getAddress<fbstring>() += *o.getAddress<fbstring>();
406     return *this;
407   }
408   *this = detail::numericOp<std::plus>(*this, o);
409   return *this;
410 }
411
412 inline dynamic& dynamic::operator-=(dynamic const& o) {
413   *this = detail::numericOp<std::minus>(*this, o);
414   return *this;
415 }
416
417 inline dynamic& dynamic::operator*=(dynamic const& o) {
418   *this = detail::numericOp<std::multiplies>(*this, o);
419   return *this;
420 }
421
422 inline dynamic& dynamic::operator/=(dynamic const& o) {
423   *this = detail::numericOp<std::divides>(*this, o);
424   return *this;
425 }
426
427 #define FB_DYNAMIC_INTEGER_OP(op)                           \
428   inline dynamic& dynamic::operator op(dynamic const& o) {  \
429     if (!isInt() || !o.isInt()) {                           \
430       throw TypeError("int64", type(), o.type());           \
431     }                                                       \
432     *getAddress<int64_t>() op o.asInt();                    \
433     return *this;                                           \
434   }
435
436 FB_DYNAMIC_INTEGER_OP(%=)
437 FB_DYNAMIC_INTEGER_OP(|=)
438 FB_DYNAMIC_INTEGER_OP(&=)
439 FB_DYNAMIC_INTEGER_OP(^=)
440
441 #undef FB_DYNAMIC_INTEGER_OP
442
443 inline dynamic& dynamic::operator++() {
444   ++get<int64_t>();
445   return *this;
446 }
447
448 inline dynamic& dynamic::operator--() {
449   --get<int64_t>();
450   return *this;
451 }
452
453 inline dynamic& dynamic::operator=(dynamic const& o) {
454   if (&o != this) {
455     destroy();
456 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
457     FB_DYNAMIC_APPLY(o.type_, FB_X);
458 #undef FB_X
459     type_ = o.type_;
460   }
461   return *this;
462 }
463
464 inline dynamic& dynamic::operator=(dynamic&& o) {
465   if (&o != this) {
466     destroy();
467 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
468     FB_DYNAMIC_APPLY(o.type_, FB_X);
469 #undef FB_X
470     type_ = o.type_;
471   }
472   return *this;
473 }
474
475 inline dynamic& dynamic::operator[](dynamic const& k) {
476   if (!isObject() && !isArray()) {
477     throw TypeError("object/array", type());
478   }
479   if (isArray()) {
480     return at(k);
481   }
482   auto& obj = get<ObjectImpl>();
483   auto ret = obj.insert({k, nullptr});
484   return ret.first->second;
485 }
486
487 inline dynamic const& dynamic::operator[](dynamic const& idx) const {
488   return at(idx);
489 }
490
491 inline dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const {
492   auto& obj = get<ObjectImpl>();
493   auto it = obj.find(k);
494   return it == obj.end() ? v : it->second;
495 }
496
497 inline dynamic&& dynamic::getDefault(const dynamic& k, dynamic&& v) const {
498   auto& obj = get<ObjectImpl>();
499   auto it = obj.find(k);
500   if (it != obj.end()) {
501     v = it->second;
502   }
503
504   return std::move(v);
505 }
506
507 template<class K, class V> inline dynamic& dynamic::setDefault(K&& k, V&& v) {
508   auto& obj = get<ObjectImpl>();
509   return obj.insert(std::make_pair(std::forward<K>(k),
510                                    std::forward<V>(v))).first->second;
511 }
512
513 inline dynamic const& dynamic::at(dynamic const& idx) const {
514   return const_cast<dynamic*>(this)->at(idx);
515 }
516
517 inline dynamic& dynamic::at(dynamic const& idx) {
518   if (!isObject() && !isArray()) {
519     throw TypeError("object/array", type());
520   }
521
522   if (auto* parray = get_nothrow<Array>()) {
523     if (idx >= parray->size()) {
524       throw std::out_of_range("out of range in dynamic array");
525     }
526     if (!idx.isInt()) {
527       throw TypeError("int64", idx.type());
528     }
529     return (*parray)[idx.asInt()];
530   }
531
532   auto* pobj = get_nothrow<ObjectImpl>();
533   assert(pobj);
534   auto it = find(idx);
535   if (it == items().end()) {
536     throw std::out_of_range(to<std::string>(
537         "couldn't find key ", idx.asString(), " in dynamic object"));
538   }
539   return const_cast<dynamic&>(it->second);
540 }
541
542 inline bool dynamic::empty() const {
543   if (isNull()) {
544     return true;
545   }
546   return !size();
547 }
548
549 inline std::size_t dynamic::size() const {
550   if (auto* ar = get_nothrow<Array>()) {
551     return ar->size();
552   }
553   if (auto* obj = get_nothrow<ObjectImpl>()) {
554     return obj->size();
555   }
556   if (auto* str = get_nothrow<fbstring>()) {
557     return str->size();
558   }
559   throw TypeError("array/object", type());
560 }
561
562 inline std::size_t dynamic::count(dynamic const& key) const {
563   return find(key) != items().end();
564 }
565
566 inline dynamic::const_item_iterator dynamic::find(dynamic const& key) const {
567   return get<ObjectImpl>().find(key);
568 }
569
570 template<class K, class V> inline void dynamic::insert(K&& key, V&& val) {
571   auto& obj = get<ObjectImpl>();
572   auto rv = obj.insert(std::make_pair(std::forward<K>(key),
573                                       std::forward<V>(val)));
574   if (!rv.second) {
575     // note, the second use of std:forward<V>(val) is only correct
576     // if the first one did not result in a move. obj[key] = val
577     // would be preferrable but doesn't compile because dynamic
578     // is (intentionally) not default constructable
579     rv.first->second = std::forward<V>(val);
580   }
581 }
582
583 inline std::size_t dynamic::erase(dynamic const& key) {
584   auto& obj = get<ObjectImpl>();
585   return obj.erase(key);
586 }
587
588 inline dynamic::const_iterator dynamic::erase(const_iterator it) {
589   return get<Array>().erase(it);
590 }
591
592 inline dynamic::const_iterator
593 dynamic::erase(const_iterator first, const_iterator last) {
594   return get<Array>().erase(first, last);
595 }
596
597 inline dynamic::const_key_iterator dynamic::erase(const_key_iterator it) {
598   return const_key_iterator(get<ObjectImpl>().erase(it.base()));
599 }
600
601 inline dynamic::const_key_iterator dynamic::erase(const_key_iterator first,
602                                                   const_key_iterator last) {
603   return const_key_iterator(get<ObjectImpl>().erase(first.base(),
604                                                     last.base()));
605 }
606
607 inline dynamic::const_value_iterator dynamic::erase(const_value_iterator it) {
608   return const_value_iterator(get<ObjectImpl>().erase(it.base()));
609 }
610
611 inline dynamic::const_value_iterator dynamic::erase(const_value_iterator first,
612                                                     const_value_iterator last) {
613   return const_value_iterator(get<ObjectImpl>().erase(first.base(),
614                                                       last.base()));
615 }
616
617 inline dynamic::const_item_iterator dynamic::erase(const_item_iterator it) {
618   return const_item_iterator(get<ObjectImpl>().erase(it.base()));
619 }
620
621 inline dynamic::const_item_iterator dynamic::erase(const_item_iterator first,
622                                                    const_item_iterator last) {
623   return const_item_iterator(get<ObjectImpl>().erase(first.base(),
624                                                      last.base()));
625 }
626
627 inline void dynamic::resize(std::size_t sz, dynamic const& c) {
628   auto& array = get<Array>();
629   array.resize(sz, c);
630 }
631
632 inline void dynamic::push_back(dynamic const& v) {
633   auto& array = get<Array>();
634   array.push_back(v);
635 }
636
637 inline void dynamic::push_back(dynamic&& v) {
638   auto& array = get<Array>();
639   array.push_back(std::move(v));
640 }
641
642 inline std::size_t dynamic::hash() const {
643   switch (type()) {
644   case OBJECT:
645   case ARRAY:
646   case NULLT:
647     throw TypeError("not null/object/array", type());
648   case INT64:
649     return std::hash<int64_t>()(asInt());
650   case DOUBLE:
651     return std::hash<double>()(asDouble());
652   case BOOL:
653     return std::hash<bool>()(asBool());
654   case STRING:
655     return std::hash<fbstring>()(asString());
656   default:
657     CHECK(0); abort();
658   }
659 }
660
661 //////////////////////////////////////////////////////////////////////
662
663 template<class T> struct dynamic::TypeInfo {
664   static char const name[];
665   static Type const type;
666 };
667
668 template<class T>
669 T dynamic::asImpl() const {
670   switch (type()) {
671   case INT64:    return to<T>(*get_nothrow<int64_t>());
672   case DOUBLE:   return to<T>(*get_nothrow<double>());
673   case BOOL:     return to<T>(*get_nothrow<bool>());
674   case STRING:   return to<T>(*get_nothrow<fbstring>());
675   default:
676     throw TypeError("int/double/bool/string", type());
677   }
678 }
679
680 // Return a T* to our type, or null if we're not that type.
681 template<class T>
682 T* dynamic::get_nothrow() {
683   if (type_ != TypeInfo<T>::type) {
684     return nullptr;
685   }
686   return getAddress<T>();
687 }
688
689 template<class T>
690 T const* dynamic::get_nothrow() const {
691   return const_cast<dynamic*>(this)->get_nothrow<T>();
692 }
693
694 // Return T* for where we can put a T, without type checking.  (Memory
695 // might be uninitialized, even.)
696 template<class T>
697 T* dynamic::getAddress() {
698   return GetAddrImpl<T>::get(u_);
699 }
700
701 template<class T>
702 T const* dynamic::getAddress() const {
703   return const_cast<dynamic*>(this)->getAddress<T>();
704 }
705
706 template<class T> struct dynamic::GetAddrImpl {};
707 template<> struct dynamic::GetAddrImpl<void*> {
708   static void** get(Data& d) { return &d.nul; }
709 };
710 template<> struct dynamic::GetAddrImpl<dynamic::Array> {
711   static Array* get(Data& d) { return &d.array; }
712 };
713 template<> struct dynamic::GetAddrImpl<bool> {
714   static bool* get(Data& d) { return &d.boolean; }
715 };
716 template<> struct dynamic::GetAddrImpl<int64_t> {
717   static int64_t* get(Data& d) { return &d.integer; }
718 };
719 template<> struct dynamic::GetAddrImpl<double> {
720   static double* get(Data& d) { return &d.doubl; }
721 };
722 template<> struct dynamic::GetAddrImpl<fbstring> {
723   static fbstring* get(Data& d) { return &d.string; }
724 };
725 template<> struct dynamic::GetAddrImpl<dynamic::ObjectImpl> {
726   static_assert(sizeof(ObjectImpl) <= sizeof(Data::objectBuffer),
727     "In your implementation, std::unordered_map<> apparently takes different"
728     " amount of space depending on its template parameters.  This is "
729     "weird.  Make objectBuffer bigger if you want to compile dynamic.");
730
731   static ObjectImpl* get(Data& d) {
732     void* data = &d.objectBuffer;
733     return static_cast<ObjectImpl*>(data);
734   }
735 };
736
737 template<class T>
738 T& dynamic::get() {
739   if (auto* p = get_nothrow<T>()) {
740     return *p;
741   }
742   throw TypeError(TypeInfo<T>::name, type());
743 }
744
745 template<class T>
746 T const& dynamic::get() const {
747   return const_cast<dynamic*>(this)->get<T>();
748 }
749
750 inline char const* dynamic::typeName(Type t) {
751 #define FB_X(T) return TypeInfo<T>::name
752   FB_DYNAMIC_APPLY(t, FB_X);
753 #undef FB_X
754 }
755
756 inline void dynamic::destroy() {
757   // This short-circuit speeds up some microbenchmarks.
758   if (type_ == NULLT) return;
759
760 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
761   FB_DYNAMIC_APPLY(type_, FB_X);
762 #undef FB_X
763   type_ = NULLT;
764   u_.nul = nullptr;
765 }
766
767 //////////////////////////////////////////////////////////////////////
768
769 /*
770  * Helper for implementing operator<<.  Throws if the type shouldn't
771  * support it.
772  */
773 template<class T>
774 struct dynamic::PrintImpl {
775   static void print(dynamic const&, std::ostream& out, T const& t) {
776     out << t;
777   }
778 };
779 template<>
780 struct dynamic::PrintImpl<dynamic::ObjectImpl> {
781   static void print(dynamic const& d,
782                     std::ostream& out,
783                     dynamic::ObjectImpl const&) {
784     d.print_as_pseudo_json(out);
785   }
786 };
787 template<>
788 struct dynamic::PrintImpl<dynamic::Array> {
789   static void print(dynamic const& d,
790                     std::ostream& out,
791                     dynamic::Array const&) {
792     d.print_as_pseudo_json(out);
793   }
794 };
795
796 inline void dynamic::print(std::ostream& out) const {
797 #define FB_X(T) PrintImpl<T>::print(*this, out, *getAddress<T>())
798   FB_DYNAMIC_APPLY(type_, FB_X);
799 #undef FB_X
800 }
801
802 inline std::ostream& operator<<(std::ostream& out, dynamic const& d) {
803   d.print(out);
804   return out;
805 }
806
807 //////////////////////////////////////////////////////////////////////
808
809 // Secialization of FormatValue so dynamic objects can be formatted
810 template <>
811 class FormatValue<dynamic> {
812  public:
813   explicit FormatValue(const dynamic& val) : val_(val) { }
814
815   template <class FormatCallback>
816   void format(FormatArg& arg, FormatCallback& cb) const {
817     switch (val_.type()) {
818     case dynamic::NULLT:
819       FormatValue<std::nullptr_t>(nullptr).format(arg, cb);
820       break;
821     case dynamic::BOOL:
822       FormatValue<bool>(val_.asBool()).format(arg, cb);
823       break;
824     case dynamic::INT64:
825       FormatValue<int64_t>(val_.asInt()).format(arg, cb);
826       break;
827     case dynamic::STRING:
828       FormatValue<fbstring>(val_.asString()).format(arg, cb);
829       break;
830     case dynamic::DOUBLE:
831       FormatValue<double>(val_.asDouble()).format(arg, cb);
832       break;
833     case dynamic::ARRAY:
834       FormatValue(val_.at(arg.splitIntKey())).format(arg, cb);
835       break;
836     case dynamic::OBJECT:
837       FormatValue(val_.at(arg.splitKey().toFbstring())).format(arg, cb);
838       break;
839     }
840   }
841
842  private:
843   const dynamic& val_;
844 };
845
846 }
847
848 #undef FB_DYNAMIC_APPLY
849
850 #endif