1 /*
2 __ _____ _____ _____
3 __| | __| | | | JSON for Modern C++ (test suite)
4 | | |__ | | | | | | version 3.7.3
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 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 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 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 }
124 }
125
126 template <typename BasicJsonType>
to_json(BasicJsonType & j,const person & p)127 void to_json(BasicJsonType& j, const person& p)
128 {
129 j = BasicJsonType{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}};
130 }
131
to_json(nlohmann::json & j,const address & a)132 void to_json(nlohmann::json& j, const address& a)
133 {
134 j = a.m_val;
135 }
136
to_json(nlohmann::json & j,const contact & c)137 void to_json(nlohmann::json& j, const contact& c)
138 {
139 j = json{{"person", c.m_person}, {"address", c.m_address}};
140 }
141
to_json(nlohmann::json & j,const contact_book & cb)142 void to_json(nlohmann::json& j, const contact_book& cb)
143 {
144 j = json{{"name", cb.m_book_name}, {"contacts", cb.m_contacts}};
145 }
146
147 // operators
operator ==(age lhs,age rhs)148 bool operator==(age lhs, age rhs)
149 {
150 return lhs.m_val == rhs.m_val;
151 }
152
operator ==(const address & lhs,const address & rhs)153 bool operator==(const address& lhs, const address& rhs)
154 {
155 return lhs.m_val == rhs.m_val;
156 }
157
operator ==(const name & lhs,const name & rhs)158 bool operator==(const name& lhs, const name& rhs)
159 {
160 return lhs.m_val == rhs.m_val;
161 }
162
operator ==(const person & lhs,const person & rhs)163 bool operator==(const person& lhs, const person& rhs)
164 {
165 return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age);
166 }
167
operator ==(const contact & lhs,const contact & rhs)168 bool operator==(const contact& lhs, const contact& rhs)
169 {
170 return std::tie(lhs.m_person, lhs.m_address) ==
171 std::tie(rhs.m_person, rhs.m_address);
172 }
173
operator ==(const contact_book & lhs,const contact_book & rhs)174 bool operator==(const contact_book& lhs, const contact_book& rhs)
175 {
176 return std::tie(lhs.m_book_name, lhs.m_contacts) ==
177 std::tie(rhs.m_book_name, rhs.m_contacts);
178 }
179 }
180
181 // from_json methods
182 namespace udt
183 {
184 template <typename BasicJsonType>
from_json(const BasicJsonType & j,age & a)185 void from_json(const BasicJsonType& j, age& a)
186 {
187 a.m_val = j.template get<int>();
188 }
189
190 template <typename BasicJsonType>
from_json(const BasicJsonType & j,name & n)191 void from_json(const BasicJsonType& j, name& n)
192 {
193 n.m_val = j.template get<std::string>();
194 }
195
196 template <typename BasicJsonType>
from_json(const BasicJsonType & j,country & c)197 void from_json(const BasicJsonType& j, country& c)
198 {
199 const auto str = j.template get<std::string>();
200 static const std::map<std::string, country> m =
201 {
202 {u8"中华人民共和国", country::china},
203 {"France", country::france},
204 {u8"Российская Федерация", country::russia}
205 };
206
207 const auto it = m.find(str);
208 // TODO test exceptions
209 c = it->second;
210 }
211
212 template <typename BasicJsonType>
from_json(const BasicJsonType & j,person & p)213 void from_json(const BasicJsonType& j, person& p)
214 {
215 p.m_age = j["age"].template get<age>();
216 p.m_name = j["name"].template get<name>();
217 p.m_country = j["country"].template get<country>();
218 }
219
from_json(const nlohmann::json & j,address & a)220 void from_json(const nlohmann::json& j, address& a)
221 {
222 a.m_val = j.get<std::string>();
223 }
224
from_json(const nlohmann::json & j,contact & c)225 void from_json(const nlohmann::json& j, contact& c)
226 {
227 c.m_person = j["person"].get<person>();
228 c.m_address = j["address"].get<address>();
229 }
230
from_json(const nlohmann::json & j,contact_book & cb)231 void from_json(const nlohmann::json& j, contact_book& cb)
232 {
233 cb.m_book_name = j["name"].get<name>();
234 cb.m_contacts = j["contacts"].get<std::vector<contact>>();
235 }
236 }
237
238 TEST_CASE("basic usage" * doctest::test_suite("udt"))
239 {
240
241 // a bit narcissistic maybe :) ?
242 const udt::age a
243 {
244 23
245 };
246 const udt::name n{"theo"};
247 const udt::country c{udt::country::france};
248 const udt::person sfinae_addict{a, n, c};
249 const udt::person senior_programmer{{42}, {u8"王芳"}, udt::country::china};
250 const udt::address addr{"Paris"};
251 const udt::contact cpp_programmer{sfinae_addict, addr};
252 const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}};
253
254 SECTION("conversion to json via free-functions")
255 {
256 CHECK(json(a) == json(23));
257 CHECK(json(n) == json("theo"));
258 CHECK(json(c) == json("France"));
259 CHECK(json(sfinae_addict) == R"({"name":"theo", "age":23, "country":"France"})"_json);
260 CHECK(json("Paris") == json(addr));
261 CHECK(json(cpp_programmer) ==
262 R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json);
263
264 CHECK(
265 json(book) ==
266 u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json);
267
268 }
269
270 SECTION("conversion from json via free-functions")
271 {
272 const auto big_json =
273 u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json;
274 SECTION("via explicit calls to get")
275 {
276 const auto parsed_book = big_json.get<udt::contact_book>();
277 const auto book_name = big_json["name"].get<udt::name>();
278 const auto contacts =
279 big_json["contacts"].get<std::vector<udt::contact>>();
280 const auto contact_json = big_json["contacts"].at(0);
281 const auto contact = contact_json.get<udt::contact>();
282 const auto person = contact_json["person"].get<udt::person>();
283 const auto address = contact_json["address"].get<udt::address>();
284 const auto age = contact_json["person"]["age"].get<udt::age>();
285 const auto country =
286 contact_json["person"]["country"].get<udt::country>();
287 const auto name = contact_json["person"]["name"].get<udt::name>();
288
289 CHECK(age == a);
290 CHECK(name == n);
291 CHECK(country == c);
292 CHECK(address == addr);
293 CHECK(person == sfinae_addict);
294 CHECK(contact == cpp_programmer);
295 CHECK(contacts == book.m_contacts);
296 CHECK(book_name == udt::name{"C++"});
297 CHECK(book == parsed_book);
298 }
299
300 SECTION("via explicit calls to get_to")
301 {
302 udt::person person;
303 udt::name name;
304
305 json person_json = big_json["contacts"][0]["person"];
306 CHECK(person_json.get_to(person) == sfinae_addict);
307
308 // correct reference gets returned
309 person_json["name"].get_to(name).m_val = "new name";
310 CHECK(name.m_val == "new name");
311 }
312
313 SECTION("implicit conversions")
314 {
315 const udt::contact_book parsed_book = big_json;
316 const udt::name book_name = big_json["name"];
317 const std::vector<udt::contact> contacts = big_json["contacts"];
318 const auto contact_json = big_json["contacts"].at(0);
319 const udt::contact contact = contact_json;
320 const udt::person person = contact_json["person"];
321 const udt::address address = contact_json["address"];
322 const udt::age age = contact_json["person"]["age"];
323 const udt::country country = contact_json["person"]["country"];
324 const udt::name name = contact_json["person"]["name"];
325
326 CHECK(age == a);
327 CHECK(name == n);
328 CHECK(country == c);
329 CHECK(address == addr);
330 CHECK(person == sfinae_addict);
331 CHECK(contact == cpp_programmer);
332 CHECK(contacts == book.m_contacts);
333 CHECK(book_name == udt::name{"C++"});
334 CHECK(book == parsed_book);
335 }
336 }
337 }
338
339 namespace udt
340 {
341 struct legacy_type
342 {
343 std::string number;
legacy_typeudt::legacy_type344 legacy_type() : number() {}
legacy_typeudt::legacy_type345 legacy_type(const std::string& n) : number(n) {}
346 };
347 }
348
349 namespace nlohmann
350 {
351 template <typename T>
352 struct adl_serializer<std::shared_ptr<T>>
353 {
to_jsonnlohmann::adl_serializer354 static void to_json(json& j, const std::shared_ptr<T>& opt)
355 {
356 if (opt)
357 {
358 j = *opt;
359 }
360 else
361 {
362 j = nullptr;
363 }
364 }
365
from_jsonnlohmann::adl_serializer366 static void from_json(const json& j, std::shared_ptr<T>& opt)
367 {
368 if (j.is_null())
369 {
370 opt = nullptr;
371 }
372 else
373 {
374 opt.reset(new T(j.get<T>()));
375 }
376 }
377 };
378
379 template <>
380 struct adl_serializer<udt::legacy_type>
381 {
to_jsonnlohmann::adl_serializer382 static void to_json(json& j, const udt::legacy_type& l)
383 {
384 j = std::stoi(l.number);
385 }
386
from_jsonnlohmann::adl_serializer387 static void from_json(const json& j, udt::legacy_type& l)
388 {
389 l.number = std::to_string(j.get<int>());
390 }
391 };
392 }
393
394 TEST_CASE("adl_serializer specialization" * doctest::test_suite("udt"))
395 {
396 SECTION("partial specialization")
397 {
398 SECTION("to_json")
399 {
400 std::shared_ptr<udt::person> optPerson;
401
402 json j = optPerson;
403 CHECK(j.is_null());
404
405 optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia});
406 j = optPerson;
407 CHECK_FALSE(j.is_null());
408
409 CHECK(j.get<udt::person>() == *optPerson);
410 }
411
412 SECTION("from_json")
413 {
414 auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
415 json j = person;
416
417 auto optPerson = j.get<std::shared_ptr<udt::person>>();
418 REQUIRE(optPerson);
419 CHECK(*optPerson == person);
420
421 j = nullptr;
422 optPerson = j.get<std::shared_ptr<udt::person>>();
423 CHECK(!optPerson);
424 }
425 }
426
427 SECTION("total specialization")
428 {
429 SECTION("to_json")
430 {
431 udt::legacy_type lt{"4242"};
432
433 json j = lt;
434 CHECK(j.get<int>() == 4242);
435 }
436
437 SECTION("from_json")
438 {
439 json j = 4242;
440 auto lt = j.get<udt::legacy_type>();
441 CHECK(lt.number == "4242");
442 }
443 }
444 }
445
446 namespace nlohmann
447 {
448 template <>
449 struct adl_serializer<std::vector<float>>
450 {
451 using type = std::vector<float>;
to_jsonnlohmann::adl_serializer452 static void to_json(json& j, const type&)
453 {
454 j = "hijacked!";
455 }
456
from_jsonnlohmann::adl_serializer457 static void from_json(const json&, type& opt)
458 {
459 opt = {42.0, 42.0, 42.0};
460 }
461
462 // preferred version
from_jsonnlohmann::adl_serializer463 static type from_json(const json&)
464 {
465 return {4.0, 5.0, 6.0};
466 }
467 };
468 }
469
470 TEST_CASE("even supported types can be specialized" * doctest::test_suite("udt"))
471 {
472 json j = std::vector<float> {1.0, 2.0, 3.0};
473 CHECK(j.dump() == R"("hijacked!")");
474 auto f = j.get<std::vector<float>>();
475 // the single argument from_json method is preferred
476 CHECK((f == std::vector<float> {4.0, 5.0, 6.0}));
477 }
478
479 namespace nlohmann
480 {
481 template <typename T>
482 struct adl_serializer<std::unique_ptr<T>>
483 {
to_jsonnlohmann::adl_serializer484 static void to_json(json& j, const std::unique_ptr<T>& opt)
485 {
486 if (opt)
487 {
488 j = *opt;
489 }
490 else
491 {
492 j = nullptr;
493 }
494 }
495
496 // this is the overload needed for non-copyable types,
from_jsonnlohmann::adl_serializer497 static std::unique_ptr<T> from_json(const json& j)
498 {
499 if (j.is_null())
500 {
501 return nullptr;
502 }
503 else
504 {
505 return std::unique_ptr<T>(new T(j.get<T>()));
506 }
507 }
508 };
509 }
510
511 TEST_CASE("Non-copyable types" * doctest::test_suite("udt"))
512 {
513 SECTION("to_json")
514 {
515 std::unique_ptr<udt::person> optPerson;
516
517 json j = optPerson;
518 CHECK(j.is_null());
519
520 optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia});
521 j = optPerson;
522 CHECK_FALSE(j.is_null());
523
524 CHECK(j.get<udt::person>() == *optPerson);
525 }
526
527 SECTION("from_json")
528 {
529 auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
530 json j = person;
531
532 auto optPerson = j.get<std::unique_ptr<udt::person>>();
533 REQUIRE(optPerson);
534 CHECK(*optPerson == person);
535
536 j = nullptr;
537 optPerson = j.get<std::unique_ptr<udt::person>>();
538 CHECK(!optPerson);
539 }
540 }
541
542 // custom serializer - advanced usage
543 // pack structs that are pod-types (but not scalar types)
544 // relies on adl for any other type
545 template <typename T, typename = void>
546 struct pod_serializer
547 {
548 // use adl for non-pods, or scalar types
549 template <
550 typename BasicJsonType, typename U = T,
551 typename std::enable_if <
552 not(std::is_pod<U>::value and std::is_class<U>::value), int >::type = 0 >
from_jsonpod_serializer553 static void from_json(const BasicJsonType& j, U& t)
554 {
555 using nlohmann::from_json;
556 from_json(j, t);
557 }
558
559 // special behaviour for pods
560 template <typename BasicJsonType, typename U = T,
561 typename std::enable_if<
562 std::is_pod<U>::value and std::is_class<U>::value, int>::type = 0>
from_jsonpod_serializer563 static void from_json(const BasicJsonType& j, U& t)
564 {
565 std::uint64_t value;
566 // TODO The following block is no longer relevant in this serializer, make another one that shows the issue
567 // the problem arises only when one from_json method is defined without any constraint
568 //
569 // Why cannot we simply use: j.get<std::uint64_t>() ?
570 // Well, with the current experiment, the get method looks for a from_json
571 // function, which we are currently defining!
572 // This would end up in a stack overflow. Calling nlohmann::from_json is a
573 // workaround (is it?).
574 // I shall find a good way to avoid this once all constructors are converted
575 // to free methods
576 //
577 // In short, constructing a json by constructor calls to_json
578 // calling get calls from_json, for now, we cannot do this in custom
579 // serializers
580 nlohmann::from_json(j, value);
581 auto bytes = static_cast<char*>(static_cast<void*>(&value));
582 std::memcpy(&t, bytes, sizeof(value));
583 }
584
585 template <
586 typename BasicJsonType, typename U = T,
587 typename std::enable_if <
588 not(std::is_pod<U>::value and std::is_class<U>::value), int >::type = 0 >
to_jsonpod_serializer589 static void to_json(BasicJsonType& j, const T& t)
590 {
591 using nlohmann::to_json;
592 to_json(j, t);
593 }
594
595 template <typename BasicJsonType, typename U = T,
596 typename std::enable_if<
597 std::is_pod<U>::value and std::is_class<U>::value, int>::type = 0>
to_jsonpod_serializer598 static void to_json(BasicJsonType& j, const T& t) noexcept
599 {
600 auto bytes = static_cast< const unsigned char*>(static_cast<const void*>(&t));
601 std::uint64_t value;
602 std::memcpy(&value, bytes, sizeof(value));
603 nlohmann::to_json(j, value);
604 }
605 };
606
607 namespace udt
608 {
609 struct small_pod
610 {
611 int begin;
612 char middle;
613 short end;
614 };
615
616 struct non_pod
617 {
618 std::string s;
non_podudt::non_pod619 non_pod() : s() {}
non_podudt::non_pod620 non_pod(const std::string& S) : s(S) {}
621 };
622
623 template <typename BasicJsonType>
to_json(BasicJsonType & j,const non_pod & np)624 void to_json(BasicJsonType& j, const non_pod& np)
625 {
626 j = np.s;
627 }
628
629 template <typename BasicJsonType>
from_json(const BasicJsonType & j,non_pod & np)630 void from_json(const BasicJsonType& j, non_pod& np)
631 {
632 np.s = j.template get<std::string>();
633 }
634
operator ==(small_pod lhs,small_pod rhs)635 bool operator==(small_pod lhs, small_pod rhs) noexcept
636 {
637 return std::tie(lhs.begin, lhs.middle, lhs.end) ==
638 std::tie(rhs.begin, rhs.middle, rhs.end);
639 }
640
operator ==(const non_pod & lhs,const non_pod & rhs)641 bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept
642 {
643 return lhs.s == rhs.s;
644 }
645
operator <<(std::ostream & os,small_pod l)646 std::ostream& operator<<(std::ostream& os, small_pod l)
647 {
648 return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end;
649 }
650 }
651
652 TEST_CASE("custom serializer for pods" * doctest::test_suite("udt"))
653 {
654 using custom_json =
655 nlohmann::basic_json<std::map, std::vector, std::string, bool,
656 std::int64_t, std::uint64_t, double, std::allocator,
657 pod_serializer>;
658
659 auto p = udt::small_pod{42, '/', 42};
660 custom_json j = p;
661
662 auto p2 = j.get<udt::small_pod>();
663
664 CHECK(p == p2);
665
666 auto np = udt::non_pod{{"non-pod"}};
667 custom_json j2 = np;
668 auto np2 = j2.get<udt::non_pod>();
669 CHECK(np == np2);
670 }
671
672 template <typename T, typename>
673 struct another_adl_serializer;
674
675 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>;
676
677 template <typename T, typename>
678 struct another_adl_serializer
679 {
from_jsonanother_adl_serializer680 static void from_json(const custom_json& j, T& t)
681 {
682 using nlohmann::from_json;
683 from_json(j, t);
684 }
685
to_jsonanother_adl_serializer686 static void to_json(custom_json& j, const T& t)
687 {
688 using nlohmann::to_json;
689 to_json(j, t);
690 }
691 };
692
693 TEST_CASE("custom serializer that does adl by default" * doctest::test_suite("udt"))
694 {
695 using json = nlohmann::json;
696
697 auto me = udt::person{{23}, {"theo"}, udt::country::france};
698
699 json j = me;
700 custom_json cj = me;
701
702 CHECK(j.dump() == cj.dump());
703
704 CHECK(me == j.get<udt::person>());
705 CHECK(me == cj.get<udt::person>());
706 }
707
708 TEST_CASE("different basic_json types conversions")
709 {
710 using json = nlohmann::json;
711
712 SECTION("null")
713 {
714 json j;
715 custom_json cj = j;
716 CHECK(cj == nullptr);
717 }
718
719 SECTION("boolean")
720 {
721 json j = true;
722 custom_json cj = j;
723 CHECK(cj == true);
724 }
725
726 SECTION("discarded")
727 {
728 json j(json::value_t::discarded);
729 custom_json cj;
730 CHECK_NOTHROW(cj = j);
731 CHECK(cj.type() == custom_json::value_t::discarded);
732 }
733
734 SECTION("array")
735 {
736 json j = {1, 2, 3};
737 custom_json cj = j;
738 CHECK((cj == std::vector<int> {1, 2, 3}));
739 }
740
741 SECTION("integer")
742 {
743 json j = 42;
744 custom_json cj = j;
745 CHECK(cj == 42);
746 }
747
748 SECTION("float")
749 {
750 json j = 42.0;
751 custom_json cj = j;
752 CHECK(cj == 42.0);
753 }
754
755 SECTION("unsigned")
756 {
757 json j = 42u;
758 custom_json cj = j;
759 CHECK(cj == 42u);
760 }
761
762 SECTION("string")
763 {
764 json j = "forty-two";
765 custom_json cj = j;
766 CHECK(cj == "forty-two");
767 }
768
769 SECTION("object")
770 {
771 json j = {{"forty", "two"}};
772 custom_json cj = j;
773 auto m = j.get<std::map<std::string, std::string>>();
774 CHECK(cj == m);
775 }
776
777 SECTION("get<custom_json>")
778 {
779 json j = 42;
780 custom_json cj = j.get<custom_json>();
781 CHECK(cj == 42);
782 }
783 }
784
785 namespace
786 {
787 struct incomplete;
788
789 // std::is_constructible is broken on macOS' libc++
790 // use the cppreference implementation
791
792 template <typename T, typename = void>
793 struct is_constructible_patched : std::false_type {};
794
795 template <typename T>
796 struct is_constructible_patched<T, decltype(void(json(std::declval<T>())))> : std::true_type {};
797 }
798
799 TEST_CASE("an incomplete type does not trigger a compiler error in non-evaluated context" * doctest::test_suite("udt"))
800 {
801 static_assert(not is_constructible_patched<json, incomplete>::value, "");
802 }
803
804 namespace
805 {
806 class Evil
807 {
808 public:
809 Evil() = default;
810 template <typename T>
Evil(T)811 Evil(T) {}
812 };
813
from_json(const json &,Evil &)814 void from_json(const json&, Evil&) {}
815 }
816
817 TEST_CASE("Issue #924")
818 {
819 // Prevent get<std::vector<Evil>>() to throw
820 auto j = json::array();
821
822 CHECK_NOTHROW(j.get<Evil>());
823 CHECK_NOTHROW(j.get<std::vector<Evil>>());
824 }
825
826 TEST_CASE("Issue #1237")
827 {
828 struct non_convertible_type {};
829 static_assert(not std::is_convertible<json, non_convertible_type>::value, "");
830 }
831