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