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