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