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