1 /*
2 __ _____ _____ _____
3 __| | __| | | | JSON for Modern C++ (test suite)
4 | | |__ | | | | | | version 3.9.1
5 |_____|_____|_____|_|___| https://github.com/nlohmann/json
6
7 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
8 SPDX-License-Identifier: MIT
9 Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28 */
29
30 #include "doctest_compatibility.h"
31
32 #include <nlohmann/json.hpp>
33 using nlohmann::json;
34
35 #include <array>
36 #include <map>
37 #include <string>
38 #include <memory>
39
40 namespace udt
41 {
42 enum class country
43 {
44 china,
45 france,
46 russia
47 };
48
49 struct age
50 {
51 int m_val;
ageudt::age52 age(int rhs = 0) : m_val(rhs) {}
53 };
54
55 struct name
56 {
57 std::string m_val;
nameudt::name58 name(const std::string rhs = "") : m_val(rhs) {}
59 };
60
61 struct address
62 {
63 std::string m_val;
addressudt::address64 address(const std::string rhs = "") : m_val(rhs) {}
65 };
66
67 struct person
68 {
69 age m_age;
70 name m_name;
71 country m_country;
personudt::person72 person() : m_age(), m_name(), m_country() {}
personudt::person73 person(const age& a, const name& n, const country& c) : m_age(a), m_name(n), m_country(c) {}
74 };
75
76 struct contact
77 {
78 person m_person;
79 address m_address;
contactudt::contact80 contact() : m_person(), m_address() {}
contactudt::contact81 contact(const person& p, const address& a) : m_person(p), m_address(a) {}
82 };
83
84 struct contact_book
85 {
86 name m_book_name;
87 std::vector<contact> m_contacts;
contact_bookudt::contact_book88 contact_book() : m_book_name(), m_contacts() {}
contact_bookudt::contact_book89 contact_book(const name& n, const std::vector<contact>& c) : m_book_name(n), m_contacts(c) {}
90 };
91 }
92
93 // to_json methods
94 namespace udt
95 {
96 // templates because of the custom_json tests (see below)
97 template <typename BasicJsonType>
to_json(BasicJsonType & j,age a)98 static void to_json(BasicJsonType& j, age a)
99 {
100 j = a.m_val;
101 }
102
103 template <typename BasicJsonType>
to_json(BasicJsonType & j,const name & n)104 static void to_json(BasicJsonType& j, const name& n)
105 {
106 j = n.m_val;
107 }
108
109 template <typename BasicJsonType>
to_json(BasicJsonType & j,country c)110 static void to_json(BasicJsonType& j, country c)
111 {
112 switch (c)
113 {
114 case country::china:
115 j = u8"中华人民共和国";
116 return;
117 case country::france:
118 j = "France";
119 return;
120 case country::russia:
121 j = u8"Российская Федерация";
122 return;
123 default:
124 break;
125 }
126 }
127
128 template <typename BasicJsonType>
to_json(BasicJsonType & j,const person & p)129 static void to_json(BasicJsonType& j, const person& p)
130 {
131 j = BasicJsonType{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}};
132 }
133
to_json(nlohmann::json & j,const address & a)134 static void to_json(nlohmann::json& j, const address& a)
135 {
136 j = a.m_val;
137 }
138
to_json(nlohmann::json & j,const contact & c)139 static void to_json(nlohmann::json& j, const contact& c)
140 {
141 j = json{{"person", c.m_person}, {"address", c.m_address}};
142 }
143
to_json(nlohmann::json & j,const contact_book & cb)144 static void to_json(nlohmann::json& j, const contact_book& cb)
145 {
146 j = json{{"name", cb.m_book_name}, {"contacts", cb.m_contacts}};
147 }
148
149 // operators
operator ==(age lhs,age rhs)150 static bool operator==(age lhs, age rhs)
151 {
152 return lhs.m_val == rhs.m_val;
153 }
154
operator ==(const address & lhs,const address & rhs)155 static bool operator==(const address& lhs, const address& rhs)
156 {
157 return lhs.m_val == rhs.m_val;
158 }
159
operator ==(const name & lhs,const name & rhs)160 static bool operator==(const name& lhs, const name& rhs)
161 {
162 return lhs.m_val == rhs.m_val;
163 }
164
operator ==(const person & lhs,const person & rhs)165 static bool operator==(const person& lhs, const person& rhs)
166 {
167 return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age);
168 }
169
operator ==(const contact & lhs,const contact & rhs)170 static bool operator==(const contact& lhs, const contact& rhs)
171 {
172 return std::tie(lhs.m_person, lhs.m_address) ==
173 std::tie(rhs.m_person, rhs.m_address);
174 }
175
operator ==(const contact_book & lhs,const contact_book & rhs)176 static bool operator==(const contact_book& lhs, const contact_book& rhs)
177 {
178 return std::tie(lhs.m_book_name, lhs.m_contacts) ==
179 std::tie(rhs.m_book_name, rhs.m_contacts);
180 }
181 }
182
183 // from_json methods
184 namespace udt
185 {
186 template <typename BasicJsonType>
from_json(const BasicJsonType & j,age & a)187 static void from_json(const BasicJsonType& j, age& a)
188 {
189 a.m_val = j.template get<int>();
190 }
191
192 template <typename BasicJsonType>
from_json(const BasicJsonType & j,name & n)193 static void from_json(const BasicJsonType& j, name& n)
194 {
195 n.m_val = j.template get<std::string>();
196 }
197
198 template <typename BasicJsonType>
from_json(const BasicJsonType & j,country & c)199 static void from_json(const BasicJsonType& j, country& c)
200 {
201 const auto str = j.template get<std::string>();
202 static const std::map<std::string, country> m =
203 {
204 {u8"中华人民共和国", country::china},
205 {"France", country::france},
206 {u8"Российская Федерация", country::russia}
207 };
208
209 const auto it = m.find(str);
210 // TODO test exceptions
211 c = it->second;
212 }
213
214 template <typename BasicJsonType>
from_json(const BasicJsonType & j,person & p)215 static void from_json(const BasicJsonType& j, person& p)
216 {
217 p.m_age = j["age"].template get<age>();
218 p.m_name = j["name"].template get<name>();
219 p.m_country = j["country"].template get<country>();
220 }
221
from_json(const nlohmann::json & j,address & a)222 static void from_json(const nlohmann::json& j, address& a)
223 {
224 a.m_val = j.get<std::string>();
225 }
226
from_json(const nlohmann::json & j,contact & c)227 static void from_json(const nlohmann::json& j, contact& c)
228 {
229 c.m_person = j["person"].get<person>();
230 c.m_address = j["address"].get<address>();
231 }
232
from_json(const nlohmann::json & j,contact_book & cb)233 static void from_json(const nlohmann::json& j, contact_book& cb)
234 {
235 cb.m_book_name = j["name"].get<name>();
236 cb.m_contacts = j["contacts"].get<std::vector<contact>>();
237 }
238 }
239
240 TEST_CASE("basic usage" * doctest::test_suite("udt"))
241 {
242
243 // a bit narcissistic maybe :) ?
244 const udt::age a
245 {
246 23
247 };
248 const udt::name n{"theo"};
249 const udt::country c{udt::country::france};
250 const udt::person sfinae_addict{a, n, c};
251 const udt::person senior_programmer{{42}, {u8"王芳"}, udt::country::china};
252 const udt::address addr{"Paris"};
253 const udt::contact cpp_programmer{sfinae_addict, addr};
254 const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}};
255
256 SECTION("conversion to json via free-functions")
257 {
258 CHECK(json(a) == json(23));
259 CHECK(json(n) == json("theo"));
260 CHECK(json(c) == json("France"));
261 CHECK(json(sfinae_addict) == R"({"name":"theo", "age":23, "country":"France"})"_json);
262 CHECK(json("Paris") == json(addr));
263 CHECK(json(cpp_programmer) ==
264 R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json);
265
266 CHECK(
267 json(book) ==
268 u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json);
269
270 }
271
272 SECTION("conversion from json via free-functions")
273 {
274 const auto big_json =
275 u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json;
276 SECTION("via explicit calls to get")
277 {
278 const auto parsed_book = big_json.get<udt::contact_book>();
279 const auto book_name = big_json["name"].get<udt::name>();
280 const auto contacts =
281 big_json["contacts"].get<std::vector<udt::contact>>();
282 const auto contact_json = big_json["contacts"].at(0);
283 const auto contact = contact_json.get<udt::contact>();
284 const auto person = contact_json["person"].get<udt::person>();
285 const auto address = contact_json["address"].get<udt::address>();
286 const auto age = contact_json["person"]["age"].get<udt::age>();
287 const auto country =
288 contact_json["person"]["country"].get<udt::country>();
289 const auto name = contact_json["person"]["name"].get<udt::name>();
290
291 CHECK(age == a);
292 CHECK(name == n);
293 CHECK(country == c);
294 CHECK(address == addr);
295 CHECK(person == sfinae_addict);
296 CHECK(contact == cpp_programmer);
297 CHECK(contacts == book.m_contacts);
298 CHECK(book_name == udt::name{"C++"});
299 CHECK(book == parsed_book);
300 }
301
302 SECTION("via explicit calls to get_to")
303 {
304 udt::person person;
305 udt::name name;
306
307 json person_json = big_json["contacts"][0]["person"];
308 CHECK(person_json.get_to(person) == sfinae_addict);
309
310 // correct reference gets returned
311 person_json["name"].get_to(name).m_val = "new name";
312 CHECK(name.m_val == "new name");
313 }
314
315 #if JSON_USE_IMPLICIT_CONVERSIONS
316 SECTION("implicit conversions")
317 {
318 const udt::contact_book parsed_book = big_json;
319 const udt::name book_name = big_json["name"];
320 const std::vector<udt::contact> contacts = big_json["contacts"];
321 const auto contact_json = big_json["contacts"].at(0);
322 const udt::contact contact = contact_json;
323 const udt::person person = contact_json["person"];
324 const udt::address address = contact_json["address"];
325 const udt::age age = contact_json["person"]["age"];
326 const udt::country country = contact_json["person"]["country"];
327 const udt::name name = contact_json["person"]["name"];
328
329 CHECK(age == a);
330 CHECK(name == n);
331 CHECK(country == c);
332 CHECK(address == addr);
333 CHECK(person == sfinae_addict);
334 CHECK(contact == cpp_programmer);
335 CHECK(contacts == book.m_contacts);
336 CHECK(book_name == udt::name{"C++"});
337 CHECK(book == parsed_book);
338 }
339 #endif
340 }
341 }
342
343 namespace udt
344 {
345 struct legacy_type
346 {
347 std::string number;
legacy_typeudt::legacy_type348 legacy_type() : number() {}
legacy_typeudt::legacy_type349 legacy_type(const std::string& n) : number(n) {}
350 };
351 }
352
353 namespace nlohmann
354 {
355 template <typename T>
356 struct adl_serializer<std::shared_ptr<T>>
357 {
to_jsonnlohmann::adl_serializer358 static void to_json(json& j, const std::shared_ptr<T>& opt)
359 {
360 if (opt)
361 {
362 j = *opt;
363 }
364 else
365 {
366 j = nullptr;
367 }
368 }
369
from_jsonnlohmann::adl_serializer370 static void from_json(const json& j, std::shared_ptr<T>& opt)
371 {
372 if (j.is_null())
373 {
374 opt = nullptr;
375 }
376 else
377 {
378 opt.reset(new T(j.get<T>()));
379 }
380 }
381 };
382
383 template <>
384 struct adl_serializer<udt::legacy_type>
385 {
to_jsonnlohmann::adl_serializer386 static void to_json(json& j, const udt::legacy_type& l)
387 {
388 j = std::stoi(l.number);
389 }
390
from_jsonnlohmann::adl_serializer391 static void from_json(const json& j, udt::legacy_type& l)
392 {
393 l.number = std::to_string(j.get<int>());
394 }
395 };
396 }
397
398 TEST_CASE("adl_serializer specialization" * doctest::test_suite("udt"))
399 {
400 SECTION("partial specialization")
401 {
402 SECTION("to_json")
403 {
404 std::shared_ptr<udt::person> optPerson;
405
406 json j = optPerson;
407 CHECK(j.is_null());
408
409 optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia});
410 j = optPerson;
411 CHECK_FALSE(j.is_null());
412
413 CHECK(j.get<udt::person>() == *optPerson);
414 }
415
416 SECTION("from_json")
417 {
418 auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
419 json j = person;
420
421 auto optPerson = j.get<std::shared_ptr<udt::person>>();
422 REQUIRE(optPerson);
423 CHECK(*optPerson == person);
424
425 j = nullptr;
426 optPerson = j.get<std::shared_ptr<udt::person>>();
427 CHECK(!optPerson);
428 }
429 }
430
431 SECTION("total specialization")
432 {
433 SECTION("to_json")
434 {
435 udt::legacy_type lt{"4242"};
436
437 json j = lt;
438 CHECK(j.get<int>() == 4242);
439 }
440
441 SECTION("from_json")
442 {
443 json j = 4242;
444 auto lt = j.get<udt::legacy_type>();
445 CHECK(lt.number == "4242");
446 }
447 }
448 }
449
450 namespace nlohmann
451 {
452 template <>
453 struct adl_serializer<std::vector<float>>
454 {
455 using type = std::vector<float>;
to_jsonnlohmann::adl_serializer456 static void to_json(json& j, const type&)
457 {
458 j = "hijacked!";
459 }
460
from_jsonnlohmann::adl_serializer461 static void from_json(const json&, type& opt)
462 {
463 opt = {42.0, 42.0, 42.0};
464 }
465
466 // preferred version
from_jsonnlohmann::adl_serializer467 static type from_json(const json&)
468 {
469 return {4.0, 5.0, 6.0};
470 }
471 };
472 }
473
474 TEST_CASE("even supported types can be specialized" * doctest::test_suite("udt"))
475 {
476 json j = std::vector<float> {1.0, 2.0, 3.0};
477 CHECK(j.dump() == R"("hijacked!")");
478 auto f = j.get<std::vector<float>>();
479 // the single argument from_json method is preferred
480 CHECK((f == std::vector<float> {4.0, 5.0, 6.0}));
481 }
482
483 namespace nlohmann
484 {
485 template <typename T>
486 struct adl_serializer<std::unique_ptr<T>>
487 {
to_jsonnlohmann::adl_serializer488 static void to_json(json& j, const std::unique_ptr<T>& opt)
489 {
490 if (opt)
491 {
492 j = *opt;
493 }
494 else
495 {
496 j = nullptr;
497 }
498 }
499
500 // this is the overload needed for non-copyable types,
from_jsonnlohmann::adl_serializer501 static std::unique_ptr<T> from_json(const json& j)
502 {
503 if (j.is_null())
504 {
505 return nullptr;
506 }
507 else
508 {
509 return std::unique_ptr<T>(new T(j.get<T>()));
510 }
511 }
512 };
513 }
514
515 TEST_CASE("Non-copyable types" * doctest::test_suite("udt"))
516 {
517 SECTION("to_json")
518 {
519 std::unique_ptr<udt::person> optPerson;
520
521 json j = optPerson;
522 CHECK(j.is_null());
523
524 optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia});
525 j = optPerson;
526 CHECK_FALSE(j.is_null());
527
528 CHECK(j.get<udt::person>() == *optPerson);
529 }
530
531 SECTION("from_json")
532 {
533 auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
534 json j = person;
535
536 auto optPerson = j.get<std::unique_ptr<udt::person>>();
537 REQUIRE(optPerson);
538 CHECK(*optPerson == person);
539
540 j = nullptr;
541 optPerson = j.get<std::unique_ptr<udt::person>>();
542 CHECK(!optPerson);
543 }
544 }
545
546 // custom serializer - advanced usage
547 // pack structs that are pod-types (but not scalar types)
548 // relies on adl for any other type
549 template <typename T, typename = void>
550 struct pod_serializer
551 {
552 // use adl for non-pods, or scalar types
553 template <
554 typename BasicJsonType, typename U = T,
555 typename std::enable_if <
556 !(std::is_pod<U>::value && std::is_class<U>::value), int >::type = 0 >
from_jsonpod_serializer557 static void from_json(const BasicJsonType& j, U& t)
558 {
559 using nlohmann::from_json;
560 from_json(j, t);
561 }
562
563 // special behaviour for pods
564 template < typename BasicJsonType, typename U = T,
565 typename std::enable_if <
566 std::is_pod<U>::value && std::is_class<U>::value, int >::type = 0 >
from_jsonpod_serializer567 static void from_json(const BasicJsonType& j, U& t)
568 {
569 std::uint64_t value;
570 // TODO The following block is no longer relevant in this serializer, make another one that shows the issue
571 // the problem arises only when one from_json method is defined without any constraint
572 //
573 // Why cannot we simply use: j.get<std::uint64_t>() ?
574 // Well, with the current experiment, the get method looks for a from_json
575 // function, which we are currently defining!
576 // This would end up in a stack overflow. Calling nlohmann::from_json is a
577 // workaround (is it?).
578 // I shall find a good way to avoid this once all constructors are converted
579 // to free methods
580 //
581 // In short, constructing a json by constructor calls to_json
582 // calling get calls from_json, for now, we cannot do this in custom
583 // serializers
584 nlohmann::from_json(j, value);
585 auto bytes = static_cast<char*>(static_cast<void*>(&value));
586 std::memcpy(&t, bytes, sizeof(value));
587 }
588
589 template <
590 typename BasicJsonType, typename U = T,
591 typename std::enable_if <
592 !(std::is_pod<U>::value && std::is_class<U>::value), int >::type = 0 >
to_jsonpod_serializer593 static void to_json(BasicJsonType& j, const T& t)
594 {
595 using nlohmann::to_json;
596 to_json(j, t);
597 }
598
599 template < typename BasicJsonType, typename U = T,
600 typename std::enable_if <
601 std::is_pod<U>::value && std::is_class<U>::value, int >::type = 0 >
to_jsonpod_serializer602 static void to_json(BasicJsonType& j, const T& t) noexcept
603 {
604 auto bytes = static_cast< const unsigned char*>(static_cast<const void*>(&t));
605 std::uint64_t value;
606 std::memcpy(&value, bytes, sizeof(value));
607 nlohmann::to_json(j, value);
608 }
609 };
610
611 namespace udt
612 {
613 struct small_pod
614 {
615 int begin;
616 char middle;
617 short end;
618 };
619
620 struct non_pod
621 {
622 std::string s;
non_podudt::non_pod623 non_pod() : s() {}
non_podudt::non_pod624 non_pod(const std::string& S) : s(S) {}
625 };
626
627 template <typename BasicJsonType>
to_json(BasicJsonType & j,const non_pod & np)628 static void to_json(BasicJsonType& j, const non_pod& np)
629 {
630 j = np.s;
631 }
632
633 template <typename BasicJsonType>
from_json(const BasicJsonType & j,non_pod & np)634 static void from_json(const BasicJsonType& j, non_pod& np)
635 {
636 np.s = j.template get<std::string>();
637 }
638
operator ==(small_pod lhs,small_pod rhs)639 static bool operator==(small_pod lhs, small_pod rhs) noexcept
640 {
641 return std::tie(lhs.begin, lhs.middle, lhs.end) ==
642 std::tie(rhs.begin, rhs.middle, rhs.end);
643 }
644
operator ==(const non_pod & lhs,const non_pod & rhs)645 static bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept
646 {
647 return lhs.s == rhs.s;
648 }
649
operator <<(std::ostream & os,small_pod l)650 static std::ostream& operator<<(std::ostream& os, small_pod l)
651 {
652 return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end;
653 }
654 }
655
656 TEST_CASE("custom serializer for pods" * doctest::test_suite("udt"))
657 {
658 using custom_json =
659 nlohmann::basic_json<std::map, std::vector, std::string, bool,
660 std::int64_t, std::uint64_t, double, std::allocator, pod_serializer>;
661
662 auto p = udt::small_pod{42, '/', 42};
663 custom_json j = p;
664
665 auto p2 = j.get<udt::small_pod>();
666
667 CHECK(p == p2);
668
669 auto np = udt::non_pod{{"non-pod"}};
670 custom_json j2 = np;
671 auto np2 = j2.get<udt::non_pod>();
672 CHECK(np == np2);
673 }
674
675 template <typename T, typename>
676 struct another_adl_serializer;
677
678 using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, another_adl_serializer>;
679
680 template <typename T, typename>
681 struct another_adl_serializer
682 {
from_jsonanother_adl_serializer683 static void from_json(const custom_json& j, T& t)
684 {
685 using nlohmann::from_json;
686 from_json(j, t);
687 }
688
to_jsonanother_adl_serializer689 static void to_json(custom_json& j, const T& t)
690 {
691 using nlohmann::to_json;
692 to_json(j, t);
693 }
694 };
695
696 TEST_CASE("custom serializer that does adl by default" * doctest::test_suite("udt"))
697 {
698 auto me = udt::person{{23}, {"theo"}, udt::country::france};
699
700 json j = me;
701 custom_json cj = me;
702
703 CHECK(j.dump() == cj.dump());
704
705 CHECK(me == j.get<udt::person>());
706 CHECK(me == cj.get<udt::person>());
707 }
708
709 TEST_CASE("different basic_json types conversions")
710 {
711 SECTION("null")
712 {
713 json j;
714 custom_json cj = j;
715 CHECK(cj == nullptr);
716 }
717
718 SECTION("boolean")
719 {
720 json j = true;
721 custom_json cj = j;
722 CHECK(cj == true);
723 }
724
725 SECTION("discarded")
726 {
727 json j(json::value_t::discarded);
728 custom_json cj;
729 CHECK_NOTHROW(cj = j);
730 CHECK(cj.type() == custom_json::value_t::discarded);
731 }
732
733 SECTION("array")
734 {
735 json j = {1, 2, 3};
736 custom_json cj = j;
737 CHECK((cj == std::vector<int> {1, 2, 3}));
738 }
739
740 SECTION("integer")
741 {
742 json j = 42;
743 custom_json cj = j;
744 CHECK(cj == 42);
745 }
746
747 SECTION("float")
748 {
749 json j = 42.0;
750 custom_json cj = j;
751 CHECK(cj == 42.0);
752 }
753
754 SECTION("unsigned")
755 {
756 json j = 42u;
757 custom_json cj = j;
758 CHECK(cj == 42u);
759 }
760
761 SECTION("string")
762 {
763 json j = "forty-two";
764 custom_json cj = j;
765 CHECK(cj == "forty-two");
766 }
767
768 SECTION("binary")
769 {
770 json j = json::binary({1, 2, 3}, 42);
771 custom_json cj = j;
772 CHECK(cj.get_binary().subtype() == 42);
773 std::vector<std::uint8_t> cv = cj.get_binary();
774 std::vector<std::uint8_t> v = j.get_binary();
775 CHECK(cv == v);
776 }
777
778 SECTION("object")
779 {
780 json j = {{"forty", "two"}};
781 custom_json cj = j;
782 auto m = j.get<std::map<std::string, std::string>>();
783 CHECK(cj == m);
784 }
785
786 SECTION("get<custom_json>")
787 {
788 json j = 42;
789 custom_json cj = j.get<custom_json>();
790 CHECK(cj == 42);
791 }
792 }
793
794 namespace
795 {
796 struct incomplete;
797
798 // std::is_constructible is broken on macOS' libc++
799 // use the cppreference implementation
800
801 template <typename T, typename = void>
802 struct is_constructible_patched : std::false_type {};
803
804 template <typename T>
805 struct is_constructible_patched<T, decltype(void(json(std::declval<T>())))> : std::true_type {};
806 }
807
808 TEST_CASE("an incomplete type does not trigger a compiler error in non-evaluated context" * doctest::test_suite("udt"))
809 {
810 static_assert(!is_constructible_patched<json, incomplete>::value, "");
811 }
812
813 namespace
814 {
815 class Evil
816 {
817 public:
818 Evil() = default;
819 template <typename T>
Evil(T t)820 Evil(T t) : m_i(sizeof(t)) {}
821
822 int m_i = 0;
823 };
824
from_json(const json &,Evil &)825 void from_json(const json&, Evil&) {}
826 }
827
828 TEST_CASE("Issue #924")
829 {
830 // Prevent get<std::vector<Evil>>() to throw
831 auto j = json::array();
832
833 CHECK_NOTHROW(j.get<Evil>());
834 CHECK_NOTHROW(j.get<std::vector<Evil>>());
835
836 // silence Wunused-template warnings
837 Evil e(1);
838 CHECK(e.m_i >= 0);
839 }
840
841 TEST_CASE("Issue #1237")
842 {
843 struct non_convertible_type {};
844 static_assert(!std::is_convertible<json, non_convertible_type>::value, "");
845 }
846