]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/json/test/snippets.cpp
5d7893f3ae3fd372b430789434779505c3203f10
[ceph.git] / ceph / src / boost / libs / json / test / snippets.cpp
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/json
8 //
9
10 #include <boost/json.hpp>
11
12 #include <algorithm>
13 #include <cmath>
14 #include <complex>
15 #include <iostream>
16 #include <map>
17 #include <numeric>
18 #include <string>
19 #include <unordered_map>
20 #include <vector>
21
22 #include "test_suite.hpp"
23
24 #ifdef _MSC_VER
25 # pragma warning(push)
26 # pragma warning(disable: 4101)
27 #elif defined(__clang__)
28 # pragma clang diagnostic push
29 # pragma clang diagnostic ignored "-Wunused"
30 # pragma clang diagnostic ignored "-Wmismatched-tags"
31 #elif defined(__GNUC__)
32 # pragma GCC diagnostic push
33 # pragma GCC diagnostic ignored "-Wunused"
34 #endif
35
36 //[snippet_conv_3
37
38 template< std::size_t N >
39 struct static_string { };
40
41 namespace std
42 {
43
44 template< std::size_t N >
45 class hash< static_string< N > >
46 {
47 public:
48 std::size_t
49 operator()(const static_string< N >& str ) const noexcept
50 {
51 return std::hash< std::string >()( str );
52 }
53 };
54
55 } // std
56
57 //]
58
59 BOOST_JSON_NS_BEGIN
60
61 //[snippet_conv_4
62
63 template< class T >
64 void
65 tag_invoke( const value_from_tag&, value& jv, std::complex< T > const& t)
66 {
67 // Store a complex number as a 2-element array
68 // with the real part followed by the imaginary part
69 jv = { t.real(), t.imag() };
70 }
71
72 template< class T >
73 std::complex< T >
74 tag_invoke( const value_to_tag< std::complex< T > >&, value const& jv )
75 {
76 return std::complex< T >(
77 jv.as_array().at(0).to_number< T >(),
78 jv.as_array().at(1).to_number< T >());
79 }
80
81 //]
82
83 namespace {
84
85 void
86 usingStrings()
87 {
88 {
89 //[snippet_strings_1
90
91 string str1; // empty string, uses the default memory resource
92
93 string str2( make_shared_resource<monotonic_resource>() ); // empty string, uses a counted monotonic resource
94
95 //]
96 }
97 {
98 //[snippet_strings_2
99
100 std::string sstr1 = "helloworld";
101 std::string sstr2 = "world";
102
103 json::string jstr1 = "helloworld";
104 json::string jstr2 = "world";
105
106 assert( jstr2.insert(0, jstr1.subview(0, 5)) == "helloworld" );
107
108 // this is equivalent to
109 assert( sstr2.insert(0, sstr1, 0, 5) == "helloworld" );
110
111 //]
112 }
113 {
114 //[snippet_strings_3
115
116 std::string sstr = "hello";
117
118 json::string jstr = "hello";
119
120 assert(sstr.append({'w', 'o', 'r', 'l', 'd'}) == "helloworld");
121
122 // such syntax is inefficient, and the same can
123 // be achieved with a character array.
124
125 assert(jstr.append("world") == "helloworld");
126
127 //]
128 }
129
130 {
131 //[snippet_strings_4
132
133 json::string str = "Boost.JSON";
134 json::string_view sv = str;
135
136 // all of these call compare(string_view)
137 str.compare(sv);
138
139 str.compare(sv.substr(0, 5));
140
141 str.compare(str);
142
143 str.compare("Boost");
144
145 //]
146 }
147 }
148
149 //----------------------------------------------------------
150
151 void
152 usingValues()
153 {
154 {
155 //[snippet_value_1
156
157 value jv1;
158 value jv2( nullptr );
159
160 assert( jv1.is_null() );
161 assert( jv2.is_null() );
162
163 //]
164 }
165 {
166 //[snippet_value_2
167
168 value jv( object_kind );
169
170 assert( jv.kind() == kind::object );
171 assert( jv.is_object() );
172 assert( ! jv.is_number() );
173
174 //]
175 }
176 {
177 auto f = []{
178 //[snippet_value_3
179
180 value jv( object_kind );
181
182 if( auto p = jv.if_object() )
183 return p->size();
184
185 //]
186 return std::size_t(0);
187 };
188 (void)f;
189 }
190 {
191 //[snippet_value_4
192
193 value jv;
194 jv = value( array_kind );
195
196 assert( jv.is_array() );
197
198 jv.emplace_string();
199
200 assert( jv.is_string() );
201
202 //]
203 }
204 {
205 //[snippet_value_5
206
207 value jv;
208 jv.emplace_string() = "Hello, world!";
209
210 int64_t& num = jv.emplace_int64();
211 num = 1;
212
213 assert( jv.is_int64() );
214
215 //]
216 }
217 {
218 try
219 {
220 //[snippet_value_6
221
222 value jv( true );
223 jv.as_bool() = true;
224
225 jv.as_string() = "Hello, world!"; // throws an exception
226
227 //]
228 }
229 catch(...)
230 {
231 }
232 }
233 {
234 //[snippet_value_7
235
236 value jv( string_kind );
237 if( string* str = jv.if_string() )
238 *str = "Hello, world!";
239
240 //]
241 }
242 {
243 //[snippet_value_8
244
245 value jv( string_kind );
246
247 // The compiler's static analysis can see that
248 // a null pointer is never dereferenced.
249 *jv.if_string() = "Hello, world!";
250
251 //]
252 }
253 }
254
255 //----------------------------------------------------------
256
257 void
258 usingInitLists()
259 {
260 {
261 //[snippet_init_list_1
262
263 value jv = {
264 { "name", "John Doe" },
265 { "active", true },
266 { "associated-accounts", nullptr },
267 { "total-balance", 330.00 },
268 { "account-balances", { 84, 120, 126 } } };
269
270 //]
271 }
272
273 {
274 //[snippet_init_list_2
275
276 value jv = { true, 2, "hello", nullptr };
277
278 assert( jv.is_array() );
279
280 assert( jv.as_array().size() == 4 );
281
282 assert( serialize(jv) == "[true,2,\"hello\",null]" );
283
284 //]
285 }
286
287 {
288 //[snippet_init_list_3
289
290 value jv = { true, 2, "hello", { "bye", nullptr, false } };
291
292 assert( jv.is_array() );
293
294 assert( jv.as_array().back().is_array() );
295
296 assert( serialize(jv) == "[true,2,\"hello\",[\"bye\",null,false]]" );
297
298 //]
299 }
300
301 {
302 //[snippet_init_list_4
303
304 // Should this be an array or an object?
305 value jv = { { "hello", 42 }, { "world", 43 } };
306
307 //]
308 }
309
310 {
311 //[snippet_init_list_5
312
313 value jv1 = { { "hello", 42 }, { "world", 43 } };
314
315 assert( jv1.is_object() );
316
317 assert( jv1.as_object().size() == 2 );
318
319 assert( serialize(jv1) == R"({"hello":42,"world":43})" );
320
321 // All of the following are arrays
322
323 value jv2 = { { "make", "Tesla" }, { "model", 3 }, "black" };
324
325 value jv3 = { { "library", "JSON" }, { "Boost", "C++", "Fast", "JSON" } };
326
327 value jv4 = { { "color", "blue" }, { 1, "red" } };
328
329 assert( jv2.is_array() && jv3.is_array() && jv4.is_array() );
330
331 //]
332 }
333
334 {
335 //[snippet_init_list_6
336
337 value jv = { { "hello", 42 }, array{ "world", 43 } };
338
339 assert( jv.is_array() );
340
341 array& ja = jv.as_array();
342
343 assert( ja[0].is_array() && ja[1].is_array());
344
345 assert ( serialize(jv) == R"([["hello",42],["world",43]])" );
346
347 //]
348
349 (void)ja;
350 }
351
352 {
353 //[snippet_init_list_7
354
355 value jv = { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } };
356
357 assert( jv.is_object() );
358
359 assert( serialize(jv) == "{\"mercury\":36,\"venus\":67,\"earth\":93}" );
360
361 array ja = { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } };
362
363 assert( serialize(ja) == "[[\"mercury\",36],[\"venus\",67],[\"earth\",93]]" );
364
365 //]
366
367 (void)ja;
368 }
369
370 {
371 //[snippet_init_list_8
372
373 object jo = { { "mercury", { { "distance", 36 } } }, { "venus", { 67, "million miles" } }, { "earth", 93 } };
374
375 assert( jo["mercury"].is_object() );
376
377 assert( jo["venus"].is_array() );
378
379 //]
380 }
381
382 {
383 //[snippet_init_list_9
384
385 object jo1 = { { "john", 100 }, { "dave", 500 }, { "joe", 300 } };
386
387 value jv = { { "clients", std::move(jo1) } };
388
389 object& jo2 = jv.as_object()["clients"].as_object();
390
391 assert( ! jo2.empty() && jo1.empty() );
392
393 assert( serialize(jv) == R"({"clients":{"john":100,"dave":500,"joe":300}})" );
394
395 //]
396
397 (void)jo2;
398 }
399 }
400
401 //----------------------------------------------------------
402
403 void
404 usingArrays()
405 {
406 {
407 //[snippet_arrays_1
408
409 array arr1; // empty array, uses the default memory resource
410
411 array arr2( make_shared_resource<monotonic_resource>() ); // empty array, uses a counted monotonic resource
412
413 //]
414 }
415 {
416 //[snippet_arrays_2
417
418 array arr( { "Hello", 42, true } );
419
420 //]
421 }
422 try
423 {
424 //[snippet_arrays_3
425
426 array arr;
427
428 arr.emplace_back( "Hello" );
429 arr.emplace_back( 42 );
430 arr.emplace_back( true );
431
432 //]
433
434 //[snippet_arrays_4
435
436 assert( arr[0].as_string() == "Hello" );
437
438 // The following line throws std::out_of_range, since the index is out of range
439 arr.at( 3 ) = nullptr;
440
441 //]
442 }
443 catch (...)
444 {
445 }
446 }
447
448 //----------------------------------------------------------
449
450 void
451 usingObjects()
452 {
453 {
454 //[snippet_objects_1
455
456 object obj1; // empty object, uses the default memory resource
457
458 object obj2( make_shared_resource<monotonic_resource>() ); // empty object, uses a counted monotonic resource
459
460 //]
461 }
462 {
463 //[snippet_objects_2
464
465 object obj( {{"key1", "value1" }, { "key2", 42 }, { "key3", false }} );
466
467 //]
468 }
469 {
470 //[snippet_objects_3
471
472 object obj;
473
474 obj.emplace( "key1", "value1" );
475 obj.emplace( "key2", 42 );
476 obj.emplace( "key3", false );
477
478 //]
479 }
480 try
481 {
482 //[snippet_objects_4
483
484 object obj;
485
486 obj["key1"] = "value1";
487 obj["key2"] = 42;
488 obj["key3"] = false;
489
490 // The following line throws std::out_of_range, since the key does not exist
491 obj.at( "key4" );
492
493 //]
494 }
495 catch (...)
496 {
497 }
498 }
499
500 //----------------------------------------------------------
501
502 //[snippet_conv_2
503
504 template< class T >
505 void identity_swap( T& a, T& b )
506 {
507 // introduces the declaration of
508 // std::swap into this scope
509 using std::swap;
510 if( &a == &b )
511 return;
512 // the overload set will contain std::swap,
513 // any declarations of swap within the enclosing
514 // namespace, and any declarations of swap within
515 // the namespaces associated with T
516 swap( a, b );
517 }
518
519 //]
520
521 //[snippet_conv_5
522
523 template< class T >
524 struct vec3
525 {
526 T x, y, z;
527 };
528
529 template< class T >
530 void tag_invoke( const value_from_tag&, value& jv, const vec3<T>& vec )
531 {
532 jv = {
533 { "x", vec.x },
534 { "y", vec.y },
535 { "z", vec.z }
536 };
537 }
538
539 //]
540
541 #ifdef BOOST_JSON_DOCS
542 //[snippet_conv_7
543
544 template< class T, typename std::enable_if<
545 std::is_floating_point< T >::value>::type* = nullptr >
546 void tag_invoke( const value_from_tag&, value& jv, T t )
547 {
548 jv = std::llround( t );
549 }
550
551 //]
552 #endif
553
554 //[snippet_conv_10
555
556 struct customer
557 {
558 std::uint64_t id;
559 std::string name;
560 bool late;
561
562 customer() = default;
563
564 customer( std::uint64_t i, const std::string& n, bool l )
565 : id( i ), name( n ), late( l ) { }
566
567 explicit customer( value const& );
568 };
569
570 void tag_invoke( const value_from_tag&, value& jv, customer const& c )
571 {
572 // Assign a JSON value
573 jv = {
574 { "id", c.id },
575 { "name", c.name },
576 { "late", c.late }
577 };
578 }
579
580 //]
581
582 //[snippet_conv_14
583
584 customer tag_invoke( const value_to_tag<customer>&, const value& jv )
585 {
586 // at() throws if `jv` is not an object, or if the key is not found.
587 // as_uint64() will throw if the value is not an unsigned 64-bit integer.
588 std::uint64_t id = jv.at( "id" ).as_uint64();
589
590 // We already know that jv is an object from
591 // the previous call to jv.as_object() succeeding,
592 // now we use jv.get_object() which skips the
593 // check. value_to will throw if jv.kind() != kind::string
594 std::string name = value_to< std::string >( jv.get_object().at( "name" ) );
595
596 // id and name are constructed from JSON in the member
597 // initializer list above, but we can also use regular
598 // assignments in the body of the function as shown below.
599 // as_bool() will throw if kv.kind() != kind::bool
600 bool late = jv.get_object().at( "late" ).as_bool();
601
602 return customer(id, name, late);
603 }
604
605 //]
606
607 void
608 usingExchange()
609 {
610 {
611 //[snippet_conv_1
612
613 std::vector< int > v1{ 1, 2, 3, 4 };
614
615 // Convert the vector to a JSON array
616 value jv = value_from( v1 );
617
618 assert( jv.is_array() );
619
620 array& ja = jv.as_array();
621
622 assert( ja.size() == 4 );
623
624 for ( std::size_t i = 0; i < v1.size(); ++i )
625 assert( v1[i] == ja[i].as_int64() );
626
627 // Convert back to vector< int >
628 std::vector< int > v2 = value_to< std::vector< int > >( jv );
629
630 assert( v1 == v2 );
631
632 //]
633
634 (void)ja;
635 }
636 {
637 //[snippet_conv_6
638
639 vec3< int > pos = { 4, 1, 4 };
640
641 value jv = value_from( pos );
642
643 assert( serialize( jv ) == "{\"x\":4,\"y\":1,\"z\":4}" );
644
645 //]
646 }
647 {
648 //[snippet_conv_8
649
650 value jv = value_from( 1.5 ); // error
651
652 //]
653 }
654 {
655 //[snippet_conv_9
656
657 std::map< std::string, vec3< int > > positions = {
658 { "Alex", { 42, -60, 18 } },
659 { "Blake", { 300, -60, -240} },
660 { "Carol", { -60, 30, 30 } }
661 };
662
663 // conversions are applied recursively;
664 // the key type and value type will be converted
665 // using value_from as well
666 value jv = value_from( positions );
667
668 assert( jv.is_object() );
669
670 object& jo = jv.as_object();
671
672 assert( jo.size() == 3 );
673
674 // The sum of the coordinates is 0
675 assert( std::accumulate( jo.begin(), jo.end(), std::int64_t(0),
676 []( std::int64_t total, const key_value_pair& jp )
677 {
678 assert ( jp.value().is_object() );
679
680 const object& pos = jp.value().as_object();
681
682 return total + pos.at( "x" ).as_int64() +
683 pos.at( "y" ).as_int64() +
684 pos.at( "z" ).as_int64();
685
686 } ) == 0 );
687
688 //]
689
690 (void)jo;
691 }
692 {
693 //[snippet_conv_11
694
695 std::vector< customer > customers = {
696 customer( 0, "Alison", false ),
697 customer( 1, "Bill", false ),
698 customer( 3, "Catherine", true ),
699 customer( 4, "Doug", false )
700 };
701
702 storage_ptr sp = make_shared_resource< monotonic_resource >();
703
704 value jv = value_from( customers, sp );
705
706 assert( jv.storage() == sp );
707
708 assert( jv.is_array() );
709
710 //]
711 }
712
713 {
714 //[snippet_conv_12
715
716 // Satisfies both FromMapLike and FromContainerLike
717 std::unordered_map< std::string, bool > available_tools = {
718 { "Crowbar", true },
719 { "Hammer", true },
720 { "Drill", true },
721 { "Saw", false },
722 };
723
724 value jv = value_from( available_tools );
725
726 assert( jv.is_object() );
727
728 //]
729 }
730 {
731 //[snippet_conv_13
732
733 std::complex< double > c1 = { 3.14159, 2.71828 };
734
735 // Convert a complex number to JSON
736 value jv = value_from( c1 );
737
738 assert ( jv.is_array() );
739
740 // Convert back to a complex number
741
742 std::complex< double > c2 = value_to< std::complex< double > >( jv );
743
744 //]
745
746 (void)c2;
747 }
748 {
749 //[snippet_conv_15
750
751 customer c1( 5, "Ed", false );
752
753 // Convert customer to value
754 value jv = value_from( c1 );
755
756 // Convert the result back to customer
757 customer c2 = value_to< customer >( jv );
758
759 // The resulting customer is unchanged
760 assert( c1.name == c2.name );
761
762 //]
763 }
764 {
765 //[snippet_conv_16
766
767 value available_tools = {
768 { "Crowbar", true },
769 { "Hammer", true },
770 { "Drill", true },
771 { "Saw", false }
772 };
773
774 assert( available_tools.is_object() );
775
776 auto as_map = value_to< std::map< std::string, bool > >( available_tools );
777
778 assert( available_tools.as_object().size() == as_map.size() );
779
780 //]
781 }
782 }
783
784 void
785 usingPointer()
786 {
787 //[snippet_pointer_1
788 value jv = { {"one", 1}, {"two", 2} };
789 assert( jv.at("one") == jv.at_pointer("/one") );
790
791 jv.at_pointer("/one") = {{"foo", "bar"}};
792 assert( jv.at("one").at("foo") == jv.at_pointer("/one/foo") );
793
794 jv.at_pointer("/one/foo") = {true, 4, "qwerty"};
795 assert( jv.at("one").at("foo").at(1) == jv.at_pointer("/one/foo/1") );
796 //]
797
798 value* elem1 = [&]() -> value*
799 {
800 //[snippet_pointer_2
801 object* obj = jv.if_object();
802 if( !obj )
803 return nullptr;
804
805 value* val = obj->if_contains("one");
806 if( !val )
807 return nullptr;
808
809 obj = val->if_object();
810 if( !obj )
811 return nullptr;
812
813 val = obj->if_contains("foo");
814 if( !val )
815 return nullptr;
816
817 array* arr = val->if_array();
818 if( !arr )
819 return nullptr;
820
821 return arr->if_contains(1);
822 //]
823 }();
824
825 value* elem2 = [&]() -> value*
826 {
827 //[snippet_pointer_3
828 error_code ec;
829 return jv.find_pointer("/one/foo/1", ec);
830 //]
831 }();
832
833 (void)elem1;
834 (void)elem2;
835 assert( elem1 == elem2 );
836 }
837
838 BOOST_STATIC_ASSERT(
839 has_value_from<customer>::value);
840
841 BOOST_STATIC_ASSERT(
842 has_value_from<std::complex<float>>::value);
843 BOOST_STATIC_ASSERT(
844 has_value_from<std::complex<double>>::value);
845
846 BOOST_STATIC_ASSERT(
847 has_value_to<std::complex<float>>::value);
848 BOOST_STATIC_ASSERT(
849 has_value_to<std::complex<double>>::value);
850
851 } // (anon)
852
853 } // json
854 } // boost
855
856 //----------------------------------------------------------
857
858
859 namespace {
860
861 class my_non_deallocating_resource { };
862
863 } // (anon)
864
865 //[snippet_allocators_14
866 namespace boost {
867 namespace json {
868
869 template<>
870 struct is_deallocate_trivial< my_non_deallocating_resource >
871 {
872 static constexpr bool value = true;
873 };
874
875 } // json
876 } // boost
877
878 //]
879
880 namespace boost {
881 namespace json {
882
883 class snippets_test
884 {
885 public:
886 void
887 run()
888 {
889 usingValues();
890 usingInitLists();
891 usingExchange();
892 usingArrays();
893 usingObjects();
894 usingStrings();
895 usingPointer();
896
897 BOOST_TEST_PASS();
898 }
899 };
900
901 TEST_SUITE(snippets_test, "boost.json.snippets");
902
903 BOOST_JSON_NS_END