• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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