From 0fe9f23254c98172c71a08ca5a1f75d4e6974811 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 20 Jun 2020 14:11:37 +0200
Subject: [PATCH 01/18] :sparkles: add macros from #2175

---
 include/nlohmann/detail/macro_scope.hpp |  41 ++++++++
 single_include/nlohmann/json.hpp        |  41 ++++++++
 test/CMakeLists.txt                     |   1 +
 test/src/unit-udt_macro.cpp             | 126 ++++++++++++++++++++++++
 4 files changed, 209 insertions(+)
 create mode 100644 test/src/unit-udt_macro.cpp

diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp
index 25e6b76e..92bc6370 100644
--- a/include/nlohmann/detail/macro_scope.hpp
+++ b/include/nlohmann/detail/macro_scope.hpp
@@ -120,3 +120,44 @@
     basic_json<ObjectType, ArrayType, StringType, BooleanType,             \
     NumberIntegerType, NumberUnsignedType, NumberFloatType,                \
     AllocatorType, JSONSerializer, BinaryType>
+
+// Macros to simplify conversion from/to types
+
+#define NLOHMANN_JSON_EXPAND( x ) x
+#define NLOHMANN_JSON_GET_MACRO(_1,_2,_3,_4,_5,_6, _7, _8, _9, _10, _11, NAME,...) NAME
+
+#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, NLOHMANN_JSON_PASTE11, \
+        NLOHMANN_JSON_PASTE10, NLOHMANN_JSON_PASTE9, NLOHMANN_JSON_PASTE8, NLOHMANN_JSON_PASTE7, \
+        NLOHMANN_JSON_PASTE6, NLOHMANN_JSON_PASTE5, NLOHMANN_JSON_PASTE4, NLOHMANN_JSON_PASTE3, \
+        NLOHMANN_JSON_PASTE2, NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
+#define NLOHMANN_JSON_PASTE2(func,  v1)                                      func(v1)
+#define NLOHMANN_JSON_PASTE3(func,  v1, v2)                                  NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
+#define NLOHMANN_JSON_PASTE4(func,  v1, v2, v3)                              NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
+#define NLOHMANN_JSON_PASTE5(func,  v1, v2, v3, v4)                          NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
+#define NLOHMANN_JSON_PASTE6(func,  v1, v2, v3, v4, v5)                      NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
+#define NLOHMANN_JSON_PASTE7(func,  v1, v2, v3, v4, v5, v6)                  NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
+#define NLOHMANN_JSON_PASTE8(func,  v1, v2, v3, v4, v5, v6, v7)              NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
+#define NLOHMANN_JSON_PASTE9(func,  v1, v2, v3, v4, v5, v6, v7, v8)          NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
+#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9)      NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8, v9)
+#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
+
+#define NLOHMANN_JSON_TO(v1) j[#v1] = t.v1;
+#define NLOHMANN_JSON_FROM(v1) j.at(#v1).get_to(t.v1);
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \
+    friend void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    friend void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \
+    void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index cc822a54..0c34d28e 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -2149,6 +2149,47 @@ JSON_HEDLEY_DIAGNOSTIC_POP
     NumberIntegerType, NumberUnsignedType, NumberFloatType,                \
     AllocatorType, JSONSerializer, BinaryType>
 
+// Macros to simplify conversion from/to types
+
+#define NLOHMANN_JSON_EXPAND( x ) x
+#define NLOHMANN_JSON_GET_MACRO(_1,_2,_3,_4,_5,_6, _7, _8, _9, _10, _11, NAME,...) NAME
+
+#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, NLOHMANN_JSON_PASTE11, \
+        NLOHMANN_JSON_PASTE10, NLOHMANN_JSON_PASTE9, NLOHMANN_JSON_PASTE8, NLOHMANN_JSON_PASTE7, \
+        NLOHMANN_JSON_PASTE6, NLOHMANN_JSON_PASTE5, NLOHMANN_JSON_PASTE4, NLOHMANN_JSON_PASTE3, \
+        NLOHMANN_JSON_PASTE2, NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
+#define NLOHMANN_JSON_PASTE2(func,  v1)                                      func(v1)
+#define NLOHMANN_JSON_PASTE3(func,  v1, v2)                                  NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
+#define NLOHMANN_JSON_PASTE4(func,  v1, v2, v3)                              NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
+#define NLOHMANN_JSON_PASTE5(func,  v1, v2, v3, v4)                          NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
+#define NLOHMANN_JSON_PASTE6(func,  v1, v2, v3, v4, v5)                      NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
+#define NLOHMANN_JSON_PASTE7(func,  v1, v2, v3, v4, v5, v6)                  NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
+#define NLOHMANN_JSON_PASTE8(func,  v1, v2, v3, v4, v5, v6, v7)              NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
+#define NLOHMANN_JSON_PASTE9(func,  v1, v2, v3, v4, v5, v6, v7, v8)          NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
+#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9)      NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8, v9)
+#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
+
+#define NLOHMANN_JSON_TO(v1) j[#v1] = t.v1;
+#define NLOHMANN_JSON_FROM(v1) j.at(#v1).get_to(t.v1);
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \
+    friend void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    friend void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \
+    void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
 
 namespace nlohmann
 {
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index fdf5ac5b..8434f78d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -132,6 +132,7 @@ set(files
     src/unit-to_chars.cpp
     src/unit-ubjson.cpp
     src/unit-udt.cpp
+    src/unit-udt_macro.cpp
     src/unit-unicode.cpp
     src/unit-user_defined_input.cpp
     src/unit-wstring.cpp)
diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp
new file mode 100644
index 00000000..ab2d3245
--- /dev/null
+++ b/test/src/unit-udt_macro.cpp
@@ -0,0 +1,126 @@
+/*
+    __ _____ _____ _____
+ __|  |   __|     |   | |  JSON for Modern C++ (test suite)
+|  |  |__   |  |  | | | |  version 3.8.0
+|_____|_____|_____|_|___|  https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+SPDX-License-Identifier: MIT
+Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby  granted, free of charge, to any  person obtaining a copy
+of this software and associated  documentation files (the "Software"), to deal
+in the Software  without restriction, including without  limitation the rights
+to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
+copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
+IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
+FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
+AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
+LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "doctest_compatibility.h"
+
+#include <nlohmann/json.hpp>
+using nlohmann::json;
+
+#include <utility>
+
+namespace persons
+{
+class person_with_private_data
+{
+  private:
+    std::string name;
+    int age = 0;
+
+  public:
+    bool operator==(const person_with_private_data& rhs) const
+    {
+        return std::tie(name, age) == std::tie(rhs.name, rhs.age);
+    }
+
+    person_with_private_data() = default;
+    person_with_private_data(std::string name, int age)
+        : name(std::move(name))
+        , age(age)
+    {}
+
+    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name);
+};
+
+class person_without_private_data_1
+{
+  public:
+    std::string name;
+    int age = 0;
+
+    bool operator==(const person_without_private_data_1& rhs) const
+    {
+        return std::tie(name, age) == std::tie(rhs.name, rhs.age);
+    }
+
+    person_without_private_data_1() = default;
+    person_without_private_data_1(std::string name, int age)
+        : name(std::move(name))
+        , age(age)
+    {}
+
+    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name);
+};
+
+class person_without_private_data_2
+{
+  public:
+    std::string name;
+    int age = 0;
+
+    bool operator==(const person_without_private_data_2& rhs) const
+    {
+        return std::tie(name, age) == std::tie(rhs.name, rhs.age);
+    }
+
+    person_without_private_data_2() = default;
+    person_without_private_data_2(std::string name, int age)
+        : name(std::move(name))
+        , age(age)
+    {}
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name);
+} // namespace persons
+
+TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE", T,
+                   persons::person_with_private_data,
+                   persons::person_without_private_data_1,
+                   persons::person_without_private_data_2)
+{
+    SECTION("person")
+    {
+        // serialization
+        T p1("Erik", 1);
+        CHECK(json(p1).dump() == "{\"age\":1,\"name\":\"Erik\"}");
+
+        // deserialization
+        T p2 = json(p1);
+        CHECK(p2 == p1);
+
+        // roundtrip
+        CHECK(T(json(p1)) == p1);
+        CHECK(json(T(json(p1))) == json(p1));
+
+        // check exception in case of missing field
+        json j = json(p1);
+        j.erase("age");
+        T p3;
+        CHECK_THROWS_WITH_AS(p3 = json(j), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range);
+    }
+}

From fa9f4040dffe11792d473552f7786e8bc60f290c Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 27 Jun 2020 13:07:02 +0200
Subject: [PATCH 02/18] :fire: remove unused typedefs #2221

---
 include/nlohmann/detail/json_pointer.hpp | 6 ------
 single_include/nlohmann/json.hpp         | 6 ------
 2 files changed, 12 deletions(-)

diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp
index 56280292..ea14861b 100644
--- a/include/nlohmann/detail/json_pointer.hpp
+++ b/include/nlohmann/detail/json_pointer.hpp
@@ -398,7 +398,6 @@ class json_pointer
     */
     BasicJsonType& get_and_create(BasicJsonType& j) const
     {
-        using size_type = typename BasicJsonType::size_type;
         auto result = &j;
 
         // in case no reference tokens exist, return a reference to the JSON value
@@ -471,7 +470,6 @@ class json_pointer
     */
     BasicJsonType& get_unchecked(BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             // convert null values to arrays or objects before continuing
@@ -531,7 +529,6 @@ class json_pointer
     */
     BasicJsonType& get_checked(BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             switch (ptr->type())
@@ -581,7 +578,6 @@ class json_pointer
     */
     const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             switch (ptr->type())
@@ -624,7 +620,6 @@ class json_pointer
     */
     const BasicJsonType& get_checked(const BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             switch (ptr->type())
@@ -665,7 +660,6 @@ class json_pointer
     */
     bool contains(const BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             switch (ptr->type())
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 770418f4..3a4fc022 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -11529,7 +11529,6 @@ class json_pointer
     */
     BasicJsonType& get_and_create(BasicJsonType& j) const
     {
-        using size_type = typename BasicJsonType::size_type;
         auto result = &j;
 
         // in case no reference tokens exist, return a reference to the JSON value
@@ -11602,7 +11601,6 @@ class json_pointer
     */
     BasicJsonType& get_unchecked(BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             // convert null values to arrays or objects before continuing
@@ -11662,7 +11660,6 @@ class json_pointer
     */
     BasicJsonType& get_checked(BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             switch (ptr->type())
@@ -11712,7 +11709,6 @@ class json_pointer
     */
     const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             switch (ptr->type())
@@ -11755,7 +11751,6 @@ class json_pointer
     */
     const BasicJsonType& get_checked(const BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             switch (ptr->type())
@@ -11796,7 +11791,6 @@ class json_pointer
     */
     bool contains(const BasicJsonType* ptr) const
     {
-        using size_type = typename BasicJsonType::size_type;
         for (const auto& reference_token : reference_tokens)
         {
             switch (ptr->type())

From 470f7c0c68a967e853781cde039486ab93866431 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 29 Jun 2020 13:43:06 +0200
Subject: [PATCH 03/18] :memo: add documentation

---
 README.md | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/README.md b/README.md
index d7492da3..1052cc0c 100644
--- a/README.md
+++ b/README.md
@@ -869,6 +869,42 @@ Some important things:
 * In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior.
 * You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these.
 
+#### Simplify your life with macros
+
+If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate.
+
+There are two macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object:
+
+- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the namespace of the class/struct to create code for.
+- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the class/struct to create code for. This macro can also access private members.
+
+In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members.
+
+##### Examples
+
+The `to_json`/`from_json` functions for the `person` struct above can be created with:
+
+```cpp
+namespace ns {
+    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age);
+}
+```
+
+Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed:
+
+```cpp
+namespace ns {
+    class address {
+      private:
+        std::string street;
+        int housenumber;
+        int postcode;
+        
+      public:
+        NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode);
+    };
+}
+```
 
 #### How do I convert third-party types?
 

From ad6eadeb70b6ba20cec985dea8da829fda2fbdd9 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Tue, 30 Jun 2020 13:59:43 +0200
Subject: [PATCH 04/18] :memo: refine documentation of error_handler parameter

---
 include/nlohmann/json.hpp        | 3 ++-
 single_include/nlohmann/json.hpp | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index ace649b6..998aa720 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -2238,7 +2238,8 @@ class basic_json
     @param[in] error_handler  how to react on decoding errors; there are three
     possible values: `strict` (throws and exception in case a decoding error
     occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),
-    and `ignore` (ignore invalid UTF-8 sequences during serialization).
+    and `ignore` (ignore invalid UTF-8 sequences during serialization; all
+    bytes are copied to the output unchanged).
 
     @return string containing the serialization of the JSON value
 
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 7ab24c84..426ee815 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -18168,7 +18168,8 @@ class basic_json
     @param[in] error_handler  how to react on decoding errors; there are three
     possible values: `strict` (throws and exception in case a decoding error
     occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),
-    and `ignore` (ignore invalid UTF-8 sequences during serialization).
+    and `ignore` (ignore invalid UTF-8 sequences during serialization; all
+    bytes are copied to the output unchanged).
 
     @return string containing the serialization of the JSON value
 

From c7e079cc98a6c5534e8d4a209dd0984e569d0625 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Tue, 30 Jun 2020 14:26:52 +0200
Subject: [PATCH 05/18] :ambulance: add specialization of get_to #2175

---
 include/nlohmann/json.hpp        | 12 ++++++++++++
 single_include/nlohmann/json.hpp | 12 ++++++++++++
 test/src/unit-udt_macro.cpp      | 28 +++++++++++++++++-----------
 3 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index ace649b6..00b7ce55 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -3019,6 +3019,18 @@ class basic_json
         return v;
     }
 
+    // specialization to allow to call get_to with a basic_json value
+    // see https://github.com/nlohmann/json/issues/2175
+    template<typename ValueType,
+             detail::enable_if_t <
+                 detail::is_basic_json<ValueType>::value,
+                 int> = 0>
+    ValueType & get_to(ValueType& v) const
+    {
+        v = *this;
+        return v;
+    }
+
     template <
         typename T, std::size_t N,
         typename Array = T (&)[N],
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 7ab24c84..9cba3069 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -18949,6 +18949,18 @@ class basic_json
         return v;
     }
 
+    // specialization to allow to call get_to with a basic_json value
+    // see https://github.com/nlohmann/json/issues/2175
+    template<typename ValueType,
+             detail::enable_if_t <
+                 detail::is_basic_json<ValueType>::value,
+                 int> = 0>
+    ValueType & get_to(ValueType& v) const
+    {
+        v = *this;
+        return v;
+    }
+
     template <
         typename T, std::size_t N,
         typename Array = T (&)[N],
diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp
index ab2d3245..a45b2c50 100644
--- a/test/src/unit-udt_macro.cpp
+++ b/test/src/unit-udt_macro.cpp
@@ -41,20 +41,22 @@ class person_with_private_data
   private:
     std::string name;
     int age = 0;
+    json metadata;
 
   public:
     bool operator==(const person_with_private_data& rhs) const
     {
-        return std::tie(name, age) == std::tie(rhs.name, rhs.age);
+        return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata);
     }
 
     person_with_private_data() = default;
-    person_with_private_data(std::string name, int age)
+    person_with_private_data(std::string name, int age, json metadata)
         : name(std::move(name))
         , age(age)
+        , metadata(std::move(metadata))
     {}
 
-    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name);
+    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata);
 };
 
 class person_without_private_data_1
@@ -62,19 +64,21 @@ class person_without_private_data_1
   public:
     std::string name;
     int age = 0;
+    json metadata;
 
     bool operator==(const person_without_private_data_1& rhs) const
     {
-        return std::tie(name, age) == std::tie(rhs.name, rhs.age);
+        return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata);
     }
 
     person_without_private_data_1() = default;
-    person_without_private_data_1(std::string name, int age)
+    person_without_private_data_1(std::string name, int age, json metadata)
         : name(std::move(name))
         , age(age)
+        , metadata(std::move(metadata))
     {}
 
-    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name);
+    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata);
 };
 
 class person_without_private_data_2
@@ -82,20 +86,22 @@ class person_without_private_data_2
   public:
     std::string name;
     int age = 0;
+    json metadata;
 
     bool operator==(const person_without_private_data_2& rhs) const
     {
-        return std::tie(name, age) == std::tie(rhs.name, rhs.age);
+        return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata);
     }
 
     person_without_private_data_2() = default;
-    person_without_private_data_2(std::string name, int age)
+    person_without_private_data_2(std::string name, int age, json metadata)
         : name(std::move(name))
         , age(age)
+        , metadata(std::move(metadata))
     {}
 };
 
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata);
 } // namespace persons
 
 TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE", T,
@@ -106,8 +112,8 @@ TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRU
     SECTION("person")
     {
         // serialization
-        T p1("Erik", 1);
-        CHECK(json(p1).dump() == "{\"age\":1,\"name\":\"Erik\"}");
+        T p1("Erik", 1, {{"haircuts", 2}});
+        CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
 
         // deserialization
         T p2 = json(p1);

From f59f4a2b61161f8aab0d7b499fbb9b7a94b66188 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Tue, 30 Jun 2020 19:55:40 +0200
Subject: [PATCH 06/18] :green_heart: fix build

---
 test/src/unit-udt_macro.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp
index a45b2c50..4535ae5b 100644
--- a/test/src/unit-udt_macro.cpp
+++ b/test/src/unit-udt_macro.cpp
@@ -32,8 +32,6 @@ SOFTWARE.
 #include <nlohmann/json.hpp>
 using nlohmann::json;
 
-#include <utility>
-
 namespace persons
 {
 class person_with_private_data
@@ -46,7 +44,7 @@ class person_with_private_data
   public:
     bool operator==(const person_with_private_data& rhs) const
     {
-        return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata);
+        return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
     }
 
     person_with_private_data() = default;
@@ -68,7 +66,7 @@ class person_without_private_data_1
 
     bool operator==(const person_without_private_data_1& rhs) const
     {
-        return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata);
+        return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
     }
 
     person_without_private_data_1() = default;
@@ -90,7 +88,7 @@ class person_without_private_data_2
 
     bool operator==(const person_without_private_data_2& rhs) const
     {
-        return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata);
+        return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
     }
 
     person_without_private_data_2() = default;

From 98b1c6d302d6b5eb4be0a3909e1dc09f920d8154 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 6 Jul 2020 12:22:31 +0200
Subject: [PATCH 07/18] :triangular_flag_on_post: use JSON_ASSERT(x) instead of
 assert(x)

---
 README.md                                     |   2 +-
 .../nlohmann/detail/conversions/to_chars.hpp  |  81 +++---
 .../nlohmann/detail/input/binary_reader.hpp   |   9 +-
 .../nlohmann/detail/input/input_adapters.hpp  |   9 +-
 include/nlohmann/detail/input/json_sax.hpp    |  29 +-
 include/nlohmann/detail/input/lexer.hpp       |  24 +-
 include/nlohmann/detail/input/parser.hpp      |   5 +-
 .../nlohmann/detail/iterators/iter_impl.hpp   |  34 +--
 .../detail/iterators/iteration_proxy.hpp      |   2 +-
 include/nlohmann/detail/json_pointer.hpp      |   5 +-
 include/nlohmann/detail/macro_scope.hpp       |   6 +
 include/nlohmann/detail/macro_unscope.hpp     |   1 +
 .../nlohmann/detail/output/binary_writer.hpp  |  12 +-
 include/nlohmann/detail/output/serializer.hpp |  33 +--
 include/nlohmann/json.hpp                     |  25 +-
 single_include/nlohmann/json.hpp              | 275 +++++++++---------
 16 files changed, 275 insertions(+), 277 deletions(-)

diff --git a/README.md b/README.md
index 6d21e2d8..7a7a46ad 100644
--- a/README.md
+++ b/README.md
@@ -1572,7 +1572,7 @@ Here is a related issue [#1924](https://github.com/nlohmann/json/issues/1924).
 
 ### Further notes
 
-- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a233b02b0839ef798942dd46157cc0fe6.html#a233b02b0839ef798942dd46157cc0fe6) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a73ae333487310e3302135189ce8ff5d8.html#a73ae333487310e3302135189ce8ff5d8).
+- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a233b02b0839ef798942dd46157cc0fe6.html#a233b02b0839ef798942dd46157cc0fe6) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a73ae333487310e3302135189ce8ff5d8.html#a73ae333487310e3302135189ce8ff5d8). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`.
 - As the exact type of a number is not defined in the [JSON specification](https://tools.ietf.org/html/rfc8259.html), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions.
 - The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag.
 - **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER´` (overriding `throw`), `JSON_TRY_USER` (overriding `try`), and `JSON_CATCH_USER` (overriding `catch`). Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior.
diff --git a/include/nlohmann/detail/conversions/to_chars.hpp b/include/nlohmann/detail/conversions/to_chars.hpp
index 5851fd0a..0907e765 100644
--- a/include/nlohmann/detail/conversions/to_chars.hpp
+++ b/include/nlohmann/detail/conversions/to_chars.hpp
@@ -1,7 +1,6 @@
 #pragma once
 
 #include <array> // array
-#include <cassert> // assert
 #include <cmath>   // signbit, isfinite
 #include <cstdint> // intN_t, uintN_t
 #include <cstring> // memcpy, memmove
@@ -63,8 +62,8 @@ struct diyfp // f * 2^e
     */
     static diyfp sub(const diyfp& x, const diyfp& y) noexcept
     {
-        assert(x.e == y.e);
-        assert(x.f >= y.f);
+        JSON_ASSERT(x.e == y.e);
+        JSON_ASSERT(x.f >= y.f);
 
         return {x.f - y.f, x.e};
     }
@@ -140,7 +139,7 @@ struct diyfp // f * 2^e
     */
     static diyfp normalize(diyfp x) noexcept
     {
-        assert(x.f != 0);
+        JSON_ASSERT(x.f != 0);
 
         while ((x.f >> 63u) == 0)
         {
@@ -159,8 +158,8 @@ struct diyfp // f * 2^e
     {
         const int delta = x.e - target_exponent;
 
-        assert(delta >= 0);
-        assert(((x.f << delta) >> delta) == x.f);
+        JSON_ASSERT(delta >= 0);
+        JSON_ASSERT(((x.f << delta) >> delta) == x.f);
 
         return {x.f << delta, target_exponent};
     }
@@ -182,8 +181,8 @@ boundaries.
 template <typename FloatType>
 boundaries compute_boundaries(FloatType value)
 {
-    assert(std::isfinite(value));
-    assert(value > 0);
+    JSON_ASSERT(std::isfinite(value));
+    JSON_ASSERT(value > 0);
 
     // Convert the IEEE representation into a diyfp.
     //
@@ -463,18 +462,18 @@ inline cached_power get_cached_power_for_binary_exponent(int e)
     //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)
     // for |e| <= 1500, but doesn't require floating-point operations.
     // NB: log_10(2) ~= 78913 / 2^18
-    assert(e >= -1500);
-    assert(e <=  1500);
+    JSON_ASSERT(e >= -1500);
+    JSON_ASSERT(e <=  1500);
     const int f = kAlpha - e - 1;
     const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
 
     const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
-    assert(index >= 0);
-    assert(static_cast<std::size_t>(index) < kCachedPowers.size());
+    JSON_ASSERT(index >= 0);
+    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());
 
     const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
-    assert(kAlpha <= cached.e + e + 64);
-    assert(kGamma >= cached.e + e + 64);
+    JSON_ASSERT(kAlpha <= cached.e + e + 64);
+    JSON_ASSERT(kGamma >= cached.e + e + 64);
 
     return cached;
 }
@@ -542,10 +541,10 @@ inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)
 inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,
                          std::uint64_t rest, std::uint64_t ten_k)
 {
-    assert(len >= 1);
-    assert(dist <= delta);
-    assert(rest <= delta);
-    assert(ten_k > 0);
+    JSON_ASSERT(len >= 1);
+    JSON_ASSERT(dist <= delta);
+    JSON_ASSERT(rest <= delta);
+    JSON_ASSERT(ten_k > 0);
 
     //               <--------------------------- delta ---->
     //                                  <---- dist --------->
@@ -570,7 +569,7 @@ inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t d
             and delta - rest >= ten_k
             and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))
     {
-        assert(buf[len - 1] != '0');
+        JSON_ASSERT(buf[len - 1] != '0');
         buf[len - 1]--;
         rest += ten_k;
     }
@@ -598,8 +597,8 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
     // Grisu2 generates the digits of M+ from left to right and stops as soon as
     // V is in [M-,M+].
 
-    assert(M_plus.e >= kAlpha);
-    assert(M_plus.e <= kGamma);
+    JSON_ASSERT(M_plus.e >= kAlpha);
+    JSON_ASSERT(M_plus.e <= kGamma);
 
     std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
     std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)
@@ -620,7 +619,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
     //
     // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
 
-    assert(p1 > 0);
+    JSON_ASSERT(p1 > 0);
 
     std::uint32_t pow10;
     const int k = find_largest_pow10(p1, pow10);
@@ -656,7 +655,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
         //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
         //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
         //
-        assert(d <= 9);
+        JSON_ASSERT(d <= 9);
         buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
         //
         //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
@@ -743,7 +742,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
     //
     // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
 
-    assert(p2 > delta);
+    JSON_ASSERT(p2 > delta);
 
     int m = 0;
     for (;;)
@@ -754,7 +753,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
         //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e
         //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
         //
-        assert(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
+        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
         p2 *= 10;
         const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e
         const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
@@ -763,7 +762,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
         //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
         //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
         //
-        assert(d <= 9);
+        JSON_ASSERT(d <= 9);
         buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
         //
         //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
@@ -824,8 +823,8 @@ JSON_HEDLEY_NON_NULL(1)
 inline void grisu2(char* buf, int& len, int& decimal_exponent,
                    diyfp m_minus, diyfp v, diyfp m_plus)
 {
-    assert(m_plus.e == m_minus.e);
-    assert(m_plus.e == v.e);
+    JSON_ASSERT(m_plus.e == m_minus.e);
+    JSON_ASSERT(m_plus.e == v.e);
 
     //  --------(-----------------------+-----------------------)--------    (A)
     //          m-                      v                       m+
@@ -886,8 +885,8 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
     static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
                   "internal error: not enough precision");
 
-    assert(std::isfinite(value));
-    assert(value > 0);
+    JSON_ASSERT(std::isfinite(value));
+    JSON_ASSERT(value > 0);
 
     // If the neighbors (and boundaries) of 'value' are always computed for double-precision
     // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
@@ -923,8 +922,8 @@ JSON_HEDLEY_NON_NULL(1)
 JSON_HEDLEY_RETURNS_NON_NULL
 inline char* append_exponent(char* buf, int e)
 {
-    assert(e > -1000);
-    assert(e <  1000);
+    JSON_ASSERT(e > -1000);
+    JSON_ASSERT(e <  1000);
 
     if (e < 0)
     {
@@ -976,8 +975,8 @@ JSON_HEDLEY_RETURNS_NON_NULL
 inline char* format_buffer(char* buf, int len, int decimal_exponent,
                            int min_exp, int max_exp)
 {
-    assert(min_exp < 0);
-    assert(max_exp > 0);
+    JSON_ASSERT(min_exp < 0);
+    JSON_ASSERT(max_exp > 0);
 
     const int k = len;
     const int n = len + decimal_exponent;
@@ -1003,7 +1002,7 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent,
         // dig.its
         // len <= max_digits10 + 1
 
-        assert(k > n);
+        JSON_ASSERT(k > n);
 
         std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
         buf[n] = '.';
@@ -1061,7 +1060,7 @@ JSON_HEDLEY_RETURNS_NON_NULL
 char* to_chars(char* first, const char* last, FloatType value)
 {
     static_cast<void>(last); // maybe unused - fix warning
-    assert(std::isfinite(value));
+    JSON_ASSERT(std::isfinite(value));
 
     // Use signbit(value) instead of (value < 0) since signbit works for -0.
     if (std::signbit(value))
@@ -1079,7 +1078,7 @@ char* to_chars(char* first, const char* last, FloatType value)
         return first;
     }
 
-    assert(last - first >= std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
 
     // Compute v = buffer * 10^decimal_exponent.
     // The decimal digits are stored in the buffer, which needs to be interpreted
@@ -1089,16 +1088,16 @@ char* to_chars(char* first, const char* last, FloatType value)
     int decimal_exponent = 0;
     dtoa_impl::grisu2(first, len, decimal_exponent, value);
 
-    assert(len <= std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
 
     // Format the buffer like printf("%.*g", prec, value)
     constexpr int kMinExp = -4;
     // Use digits10 here to increase compatibility with version 2.
     constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
 
-    assert(last - first >= kMaxExp + 2);
-    assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
-    assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+    JSON_ASSERT(last - first >= kMaxExp + 2);
+    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
 
     return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
 }
diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp
index a27a1538..e075d527 100644
--- a/include/nlohmann/detail/input/binary_reader.hpp
+++ b/include/nlohmann/detail/input/binary_reader.hpp
@@ -2,7 +2,6 @@
 
 #include <algorithm> // generate_n
 #include <array> // array
-#include <cassert> // assert
 #include <cmath> // ldexp
 #include <cstddef> // size_t
 #include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
@@ -109,7 +108,7 @@ class binary_reader
                 break;
 
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
 
         // strict mode: next byte must be EOF
@@ -717,8 +716,8 @@ class binary_reader
                 {
                     const int exp = (half >> 10u) & 0x1Fu;
                     const unsigned int mant = half & 0x3FFu;
-                    assert(0 <= exp and exp <= 32);
-                    assert(mant <= 1024);
+                    JSON_ASSERT(0 <= exp and exp <= 32);
+                    JSON_ASSERT(mant <= 1024);
                     switch (exp)
                     {
                         case 0:
@@ -2295,7 +2294,7 @@ class binary_reader
                 break;
 
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
 
         return error_msg + " " + context + ": " + detail;
diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp
index 47ea4a5b..2efccb1a 100644
--- a/include/nlohmann/detail/input/input_adapters.hpp
+++ b/include/nlohmann/detail/input/input_adapters.hpp
@@ -1,7 +1,6 @@
 #pragma once
 
 #include <array> // array
-#include <cassert> // assert
 #include <cstddef> // size_t
 #include <cstdio> //FILE *
 #include <cstring> // strlen
@@ -297,13 +296,13 @@ class wide_string_input_adapter
         {
             fill_buffer<sizeof(WideCharType)>();
 
-            assert(utf8_bytes_filled > 0);
-            assert(utf8_bytes_index == 0);
+            JSON_ASSERT(utf8_bytes_filled > 0);
+            JSON_ASSERT(utf8_bytes_index == 0);
         }
 
         // use buffer
-        assert(utf8_bytes_filled > 0);
-        assert(utf8_bytes_index < utf8_bytes_filled);
+        JSON_ASSERT(utf8_bytes_filled > 0);
+        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
         return utf8_bytes[utf8_bytes_index++];
     }
 
diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp
index c7c1db81..18d82822 100644
--- a/include/nlohmann/detail/input/json_sax.hpp
+++ b/include/nlohmann/detail/input/json_sax.hpp
@@ -1,6 +1,5 @@
 #pragma once
 
-#include <cassert> // assert
 #include <cstddef>
 #include <string> // string
 #include <utility> // move
@@ -280,7 +279,7 @@ class json_sax_dom_parser
                 case 5:
                     JSON_THROW(*dynamic_cast<const detail::other_error*>(&ex));
                 default:
-                    assert(false);
+                    JSON_ASSERT(false);
                     // LCOV_EXCL_STOP
             }
         }
@@ -309,7 +308,7 @@ class json_sax_dom_parser
             return &root;
         }
 
-        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
+        JSON_ASSERT(ref_stack.back()->is_array() or ref_stack.back()->is_object());
 
         if (ref_stack.back()->is_array())
         {
@@ -317,8 +316,8 @@ class json_sax_dom_parser
             return &(ref_stack.back()->m_value.array->back());
         }
 
-        assert(ref_stack.back()->is_object());
-        assert(object_element);
+        JSON_ASSERT(ref_stack.back()->is_object());
+        JSON_ASSERT(object_element);
         *object_element = BasicJsonType(std::forward<Value>(v));
         return object_element;
     }
@@ -447,8 +446,8 @@ class json_sax_dom_callback_parser
             *ref_stack.back() = discarded;
         }
 
-        assert(not ref_stack.empty());
-        assert(not keep_stack.empty());
+        JSON_ASSERT(not ref_stack.empty());
+        JSON_ASSERT(not keep_stack.empty());
         ref_stack.pop_back();
         keep_stack.pop_back();
 
@@ -499,8 +498,8 @@ class json_sax_dom_callback_parser
             }
         }
 
-        assert(not ref_stack.empty());
-        assert(not keep_stack.empty());
+        JSON_ASSERT(not ref_stack.empty());
+        JSON_ASSERT(not keep_stack.empty());
         ref_stack.pop_back();
         keep_stack.pop_back();
 
@@ -534,7 +533,7 @@ class json_sax_dom_callback_parser
                 case 5:
                     JSON_THROW(*dynamic_cast<const detail::other_error*>(&ex));
                 default:
-                    assert(false);
+                    JSON_ASSERT(false);
                     // LCOV_EXCL_STOP
             }
         }
@@ -565,7 +564,7 @@ class json_sax_dom_callback_parser
     template<typename Value>
     std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
     {
-        assert(not keep_stack.empty());
+        JSON_ASSERT(not keep_stack.empty());
 
         // do not handle this value if we know it would be added to a discarded
         // container
@@ -600,7 +599,7 @@ class json_sax_dom_callback_parser
         }
 
         // we now only expect arrays and objects
-        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
+        JSON_ASSERT(ref_stack.back()->is_array() or ref_stack.back()->is_object());
 
         // array
         if (ref_stack.back()->is_array())
@@ -610,9 +609,9 @@ class json_sax_dom_callback_parser
         }
 
         // object
-        assert(ref_stack.back()->is_object());
+        JSON_ASSERT(ref_stack.back()->is_object());
         // check if we should store an element for the current key
-        assert(not key_keep_stack.empty());
+        JSON_ASSERT(not key_keep_stack.empty());
         const bool store_element = key_keep_stack.back();
         key_keep_stack.pop_back();
 
@@ -621,7 +620,7 @@ class json_sax_dom_callback_parser
             return {false, nullptr};
         }
 
-        assert(object_element);
+        JSON_ASSERT(object_element);
         *object_element = std::move(value);
         return {true, object_element};
     }
diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp
index 8042f3c4..0a0c6c74 100644
--- a/include/nlohmann/detail/input/lexer.hpp
+++ b/include/nlohmann/detail/input/lexer.hpp
@@ -135,7 +135,7 @@ class lexer : public lexer_base<BasicJsonType>
     static char get_decimal_point() noexcept
     {
         const auto* loc = localeconv();
-        assert(loc != nullptr);
+        JSON_ASSERT(loc != nullptr);
         return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
     }
 
@@ -161,7 +161,7 @@ class lexer : public lexer_base<BasicJsonType>
     int get_codepoint()
     {
         // this function only makes sense after reading `\u`
-        assert(current == 'u');
+        JSON_ASSERT(current == 'u');
         int codepoint = 0;
 
         const auto factors = { 12u, 8u, 4u, 0u };
@@ -187,7 +187,7 @@ class lexer : public lexer_base<BasicJsonType>
             }
         }
 
-        assert(0x0000 <= codepoint and codepoint <= 0xFFFF);
+        JSON_ASSERT(0x0000 <= codepoint and codepoint <= 0xFFFF);
         return codepoint;
     }
 
@@ -208,7 +208,7 @@ class lexer : public lexer_base<BasicJsonType>
     */
     bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
     {
-        assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
+        JSON_ASSERT(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
         add(current);
 
         for (auto range = ranges.begin(); range != ranges.end(); ++range)
@@ -249,7 +249,7 @@ class lexer : public lexer_base<BasicJsonType>
         reset();
 
         // we entered the function by reading an open quote
-        assert(current == '\"');
+        JSON_ASSERT(current == '\"');
 
         while (true)
         {
@@ -369,7 +369,7 @@ class lexer : public lexer_base<BasicJsonType>
                             }
 
                             // result of the above calculation yields a proper codepoint
-                            assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
+                            JSON_ASSERT(0x00 <= codepoint and codepoint <= 0x10FFFF);
 
                             // translate codepoint into bytes
                             if (codepoint < 0x80)
@@ -998,7 +998,7 @@ class lexer : public lexer_base<BasicJsonType>
 
             // all other characters are rejected outside scan_number()
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
 
 scan_number_minus:
@@ -1245,7 +1245,7 @@ scan_number_done:
             const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
 
             // we checked the number format before
-            assert(endptr == token_buffer.data() + token_buffer.size());
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
 
             if (errno == 0)
             {
@@ -1261,7 +1261,7 @@ scan_number_done:
             const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
 
             // we checked the number format before
-            assert(endptr == token_buffer.data() + token_buffer.size());
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
 
             if (errno == 0)
             {
@@ -1278,7 +1278,7 @@ scan_number_done:
         strtof(value_float, token_buffer.data(), &endptr);
 
         // we checked the number format before
-        assert(endptr == token_buffer.data() + token_buffer.size());
+        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
 
         return token_type::value_float;
     }
@@ -1292,7 +1292,7 @@ scan_number_done:
     token_type scan_literal(const char_type* literal_text, const std::size_t length,
                             token_type return_type)
     {
-        assert(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
+        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
         for (std::size_t i = 1; i < length; ++i)
         {
             if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))
@@ -1384,7 +1384,7 @@ scan_number_done:
 
         if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
         {
-            assert(not token_string.empty());
+            JSON_ASSERT(not token_string.empty());
             token_string.pop_back();
         }
     }
diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp
index c79b492a..b9f7cd84 100644
--- a/include/nlohmann/detail/input/parser.hpp
+++ b/include/nlohmann/detail/input/parser.hpp
@@ -1,6 +1,5 @@
 #pragma once
 
-#include <cassert> // assert
 #include <cmath> // isfinite
 #include <cstdint> // uint8_t
 #include <functional> // function
@@ -383,7 +382,7 @@ class parser
                     // new value, we need to evaluate the new state first.
                     // By setting skip_to_state_evaluation to false, we
                     // are effectively jumping to the beginning of this if.
-                    assert(not states.empty());
+                    JSON_ASSERT(not states.empty());
                     states.pop_back();
                     skip_to_state_evaluation = true;
                     continue;
@@ -439,7 +438,7 @@ class parser
                     // new value, we need to evaluate the new state first.
                     // By setting skip_to_state_evaluation to false, we
                     // are effectively jumping to the beginning of this if.
-                    assert(not states.empty());
+                    JSON_ASSERT(not states.empty());
                     states.pop_back();
                     skip_to_state_evaluation = true;
                     continue;
diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp
index 0b79202a..8d7796e1 100644
--- a/include/nlohmann/detail/iterators/iter_impl.hpp
+++ b/include/nlohmann/detail/iterators/iter_impl.hpp
@@ -85,7 +85,7 @@ class iter_impl
     */
     explicit iter_impl(pointer object) noexcept : m_object(object)
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -171,7 +171,7 @@ class iter_impl
     */
     void set_begin() noexcept
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -208,7 +208,7 @@ class iter_impl
     */
     void set_end() noexcept
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -239,19 +239,19 @@ class iter_impl
     */
     reference operator*() const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
             case value_t::object:
             {
-                assert(m_it.object_iterator != m_object->m_value.object->end());
+                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
                 return m_it.object_iterator->second;
             }
 
             case value_t::array:
             {
-                assert(m_it.array_iterator != m_object->m_value.array->end());
+                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
                 return *m_it.array_iterator;
             }
 
@@ -276,19 +276,19 @@ class iter_impl
     */
     pointer operator->() const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
             case value_t::object:
             {
-                assert(m_it.object_iterator != m_object->m_value.object->end());
+                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
                 return &(m_it.object_iterator->second);
             }
 
             case value_t::array:
             {
-                assert(m_it.array_iterator != m_object->m_value.array->end());
+                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
                 return &*m_it.array_iterator;
             }
 
@@ -321,7 +321,7 @@ class iter_impl
     */
     iter_impl& operator++()
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -364,7 +364,7 @@ class iter_impl
     */
     iter_impl& operator--()
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -402,7 +402,7 @@ class iter_impl
             JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
         }
 
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -438,7 +438,7 @@ class iter_impl
             JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
         }
 
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -486,7 +486,7 @@ class iter_impl
     */
     iter_impl& operator+=(difference_type i)
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -557,7 +557,7 @@ class iter_impl
     */
     difference_type operator-(const iter_impl& other) const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -578,7 +578,7 @@ class iter_impl
     */
     reference operator[](difference_type n) const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -609,7 +609,7 @@ class iter_impl
     */
     const typename object_t::key_type& key() const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         if (JSON_HEDLEY_LIKELY(m_object->is_object()))
         {
diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp
index 7d0f1e59..b10d0587 100644
--- a/include/nlohmann/detail/iterators/iteration_proxy.hpp
+++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp
@@ -74,7 +74,7 @@ template <typename IteratorType> class iteration_proxy_value
     /// return key of the iterator
     const string_type& key() const
     {
-        assert(anchor.m_object != nullptr);
+        JSON_ASSERT(anchor.m_object != nullptr);
 
         switch (anchor.m_object->type())
         {
diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp
index ea14861b..1ac86591 100644
--- a/include/nlohmann/detail/json_pointer.hpp
+++ b/include/nlohmann/detail/json_pointer.hpp
@@ -1,7 +1,6 @@
 #pragma once
 
 #include <algorithm> // all_of
-#include <cassert> // assert
 #include <cctype> // isdigit
 #include <limits> // max
 #include <numeric> // accumulate
@@ -781,7 +780,7 @@ class json_pointer
                     pos != std::string::npos;
                     pos = reference_token.find_first_of('~', pos + 1))
             {
-                assert(reference_token[pos] == '~');
+                JSON_ASSERT(reference_token[pos] == '~');
 
                 // ~ must be followed by 0 or 1
                 if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or
@@ -816,7 +815,7 @@ class json_pointer
     static void replace_substring(std::string& s, const std::string& f,
                                   const std::string& t)
     {
-        assert(not f.empty());
+        JSON_ASSERT(not f.empty());
         for (auto pos = s.find(f);                // find first occurrence of f
                 pos != std::string::npos;         // make sure f was found
                 s.replace(pos, f.size(), t),      // replace with t, and
diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp
index 46fa8c10..3537768f 100644
--- a/include/nlohmann/detail/macro_scope.hpp
+++ b/include/nlohmann/detail/macro_scope.hpp
@@ -77,6 +77,12 @@
     #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
 #endif
 
+// allow to override assert
+#if !defined(JSON_ASSERT)
+    #include <cassert> // assert
+    #define JSON_ASSERT(x) assert(x)
+#endif
+
 /*!
 @brief macro to briefly define a mapping between an enum and JSON
 @def NLOHMANN_JSON_SERIALIZE_ENUM
diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp
index 80b293e7..04415061 100644
--- a/include/nlohmann/detail/macro_unscope.hpp
+++ b/include/nlohmann/detail/macro_unscope.hpp
@@ -9,6 +9,7 @@
 #endif
 
 // clean up
+#undef JSON_ASSERT
 #undef JSON_INTERNAL_CATCH
 #undef JSON_CATCH
 #undef JSON_THROW
diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp
index 3bac0227..844791a0 100644
--- a/include/nlohmann/detail/output/binary_writer.hpp
+++ b/include/nlohmann/detail/output/binary_writer.hpp
@@ -38,7 +38,7 @@ class binary_writer
     */
     explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
     {
-        assert(oa);
+        JSON_ASSERT(oa);
     }
 
     /*!
@@ -754,7 +754,7 @@ class binary_writer
                 bool prefix_required = true;
                 if (use_type and not j.m_value.array->empty())
                 {
-                    assert(use_count);
+                    JSON_ASSERT(use_count);
                     const CharType first_prefix = ubjson_prefix(j.front());
                     const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
                                                          [this, first_prefix](const BasicJsonType & v)
@@ -798,7 +798,7 @@ class binary_writer
 
                 if (use_type and not j.m_value.binary->empty())
                 {
-                    assert(use_count);
+                    JSON_ASSERT(use_count);
                     oa->write_character(to_char_type('$'));
                     oa->write_character('U');
                 }
@@ -842,7 +842,7 @@ class binary_writer
                 bool prefix_required = true;
                 if (use_type and not j.m_value.object->empty())
                 {
-                    assert(use_count);
+                    JSON_ASSERT(use_count);
                     const CharType first_prefix = ubjson_prefix(j.front());
                     const bool same_prefix = std::all_of(j.begin(), j.end(),
                                                          [this, first_prefix](const BasicJsonType & v)
@@ -1134,7 +1134,7 @@ class binary_writer
 
             // LCOV_EXCL_START
             default:
-                assert(false);
+                JSON_ASSERT(false);
                 return 0ul;
                 // LCOV_EXCL_STOP
         }
@@ -1181,7 +1181,7 @@ class binary_writer
 
             // LCOV_EXCL_START
             default:
-                assert(false);
+                JSON_ASSERT(false);
                 return;
                 // LCOV_EXCL_STOP
         }
diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp
index c089b94f..808d9368 100644
--- a/include/nlohmann/detail/output/serializer.hpp
+++ b/include/nlohmann/detail/output/serializer.hpp
@@ -2,7 +2,6 @@
 
 #include <algorithm> // reverse, remove, fill, find, none_of
 #include <array> // array
-#include <cassert> // assert
 #include <clocale> // localeconv, lconv
 #include <cmath> // labs, isfinite, isnan, signbit
 #include <cstddef> // size_t, ptrdiff_t
@@ -135,8 +134,8 @@ class serializer
                     }
 
                     // last element
-                    assert(i != val.m_value.object->cend());
-                    assert(std::next(i) == val.m_value.object->cend());
+                    JSON_ASSERT(i != val.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());
                     o->write_characters(indent_string.c_str(), new_indent);
                     o->write_character('\"');
                     dump_escaped(i->first, ensure_ascii);
@@ -163,8 +162,8 @@ class serializer
                     }
 
                     // last element
-                    assert(i != val.m_value.object->cend());
-                    assert(std::next(i) == val.m_value.object->cend());
+                    JSON_ASSERT(i != val.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());
                     o->write_character('\"');
                     dump_escaped(i->first, ensure_ascii);
                     o->write_characters("\":", 2);
@@ -205,7 +204,7 @@ class serializer
                     }
 
                     // last element
-                    assert(not val.m_value.array->empty());
+                    JSON_ASSERT(not val.m_value.array->empty());
                     o->write_characters(indent_string.c_str(), new_indent);
                     dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
 
@@ -226,7 +225,7 @@ class serializer
                     }
 
                     // last element
-                    assert(not val.m_value.array->empty());
+                    JSON_ASSERT(not val.m_value.array->empty());
                     dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
 
                     o->write_character(']');
@@ -360,7 +359,7 @@ class serializer
             }
 
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
     }
 
@@ -559,7 +558,7 @@ class serializer
                         }
 
                         default:            // LCOV_EXCL_LINE
-                            assert(false);  // LCOV_EXCL_LINE
+                            JSON_ASSERT(false);  // LCOV_EXCL_LINE
                     }
                     break;
                 }
@@ -622,7 +621,7 @@ class serializer
                 }
 
                 default:            // LCOV_EXCL_LINE
-                    assert(false);  // LCOV_EXCL_LINE
+                    JSON_ASSERT(false);  // LCOV_EXCL_LINE
             }
         }
     }
@@ -723,7 +722,7 @@ class serializer
         }
 
         // spare 1 byte for '\0'
-        assert(n_chars < number_buffer.size() - 1);
+        JSON_ASSERT(n_chars < number_buffer.size() - 1);
 
         // jump to the end to generate the string from backward
         // so we later avoid reversing the result
@@ -799,9 +798,9 @@ class serializer
         std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
 
         // negative value indicates an error
-        assert(len > 0);
+        JSON_ASSERT(len > 0);
         // check if buffer was large enough
-        assert(static_cast<std::size_t>(len) < number_buffer.size());
+        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
 
         // erase thousands separator
         if (thousands_sep != '\0')
@@ -809,7 +808,7 @@ class serializer
             const auto end = std::remove(number_buffer.begin(),
                                          number_buffer.begin() + len, thousands_sep);
             std::fill(end, number_buffer.end(), '\0');
-            assert((end - number_buffer.begin()) <= len);
+            JSON_ASSERT((end - number_buffer.begin()) <= len);
             len = (end - number_buffer.begin());
         }
 
@@ -889,7 +888,7 @@ class serializer
                 : (0xFFu >> type) & (byte);
 
         std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
-        assert(index < 400);
+        JSON_ASSERT(index < 400);
         state = utf8d[index];
         return state;
     }
@@ -901,7 +900,7 @@ class serializer
      */
     number_unsigned_t remove_sign(number_unsigned_t x)
     {
-        assert(false); // LCOV_EXCL_LINE
+        JSON_ASSERT(false); // LCOV_EXCL_LINE
         return x; // LCOV_EXCL_LINE
     }
 
@@ -916,7 +915,7 @@ class serializer
      */
     inline number_unsigned_t remove_sign(number_integer_t x) noexcept
     {
-        assert(x < 0 and x < (std::numeric_limits<number_integer_t>::max)());
+        JSON_ASSERT(x < 0 and x < (std::numeric_limits<number_integer_t>::max)());
         return static_cast<number_unsigned_t>(-(x + 1)) + 1;
     }
 
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 1dedb809..245fa391 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -35,7 +35,6 @@ SOFTWARE.
 #define NLOHMANN_JSON_VERSION_PATCH 0
 
 #include <algorithm> // all_of, find, for_each
-#include <cassert> // assert
 #include <cstddef> // nullptr_t, ptrdiff_t, size_t
 #include <functional> // hash, less
 #include <initializer_list> // initializer_list
@@ -924,7 +923,7 @@ class basic_json
         };
         std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);
         AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);
-        assert(object != nullptr);
+        JSON_ASSERT(object != nullptr);
         return object.release();
     }
 
@@ -1219,10 +1218,10 @@ class basic_json
     */
     void assert_invariant() const noexcept
     {
-        assert(m_type != value_t::object or m_value.object != nullptr);
-        assert(m_type != value_t::array or m_value.array != nullptr);
-        assert(m_type != value_t::string or m_value.string != nullptr);
-        assert(m_type != value_t::binary or m_value.binary != nullptr);
+        JSON_ASSERT(m_type != value_t::object or m_value.object != nullptr);
+        JSON_ASSERT(m_type != value_t::array or m_value.array != nullptr);
+        JSON_ASSERT(m_type != value_t::string or m_value.string != nullptr);
+        JSON_ASSERT(m_type != value_t::binary or m_value.binary != nullptr);
     }
 
   public:
@@ -1515,7 +1514,7 @@ class basic_json
                 m_type = value_t::discarded;
                 break;
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
         assert_invariant();
     }
@@ -1915,8 +1914,8 @@ class basic_json
                  std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>
     basic_json(InputIT first, InputIT last)
     {
-        assert(first.m_object != nullptr);
-        assert(last.m_object != nullptr);
+        JSON_ASSERT(first.m_object != nullptr);
+        JSON_ASSERT(last.m_object != nullptr);
 
         // make sure iterator fits the current value
         if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
@@ -3633,7 +3632,7 @@ class basic_json
         // const operator[] only works for objects
         if (JSON_HEDLEY_LIKELY(is_object()))
         {
-            assert(m_value.object->find(key) != m_value.object->end());
+            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
 
@@ -3725,7 +3724,7 @@ class basic_json
         // at only works for objects
         if (JSON_HEDLEY_LIKELY(is_object()))
         {
-            assert(m_value.object->find(key) != m_value.object->end());
+            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
 
@@ -5493,7 +5492,7 @@ class basic_json
     iterator insert_iterator(const_iterator pos, Args&& ... args)
     {
         iterator result(this);
-        assert(m_value.array != nullptr);
+        JSON_ASSERT(m_value.array != nullptr);
 
         auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);
         m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
@@ -8258,7 +8257,7 @@ class basic_json
 
                 // if there exists a parent it cannot be primitive
                 default:            // LCOV_EXCL_LINE
-                    assert(false);  // LCOV_EXCL_LINE
+                    JSON_ASSERT(false);  // LCOV_EXCL_LINE
             }
         };
 
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 87222900..a2940ed0 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -35,7 +35,6 @@ SOFTWARE.
 #define NLOHMANN_JSON_VERSION_PATCH 0
 
 #include <algorithm> // all_of, find, for_each
-#include <cassert> // assert
 #include <cstddef> // nullptr_t, ptrdiff_t, size_t
 #include <functional> // hash, less
 #include <initializer_list> // initializer_list
@@ -2105,6 +2104,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP
     #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
 #endif
 
+// allow to override assert
+#if !defined(JSON_ASSERT)
+    #include <cassert> // assert
+    #define JSON_ASSERT(x) assert(x)
+#endif
+
 /*!
 @brief macro to briefly define a mapping between an enum and JSON
 @def NLOHMANN_JSON_SERIALIZE_ENUM
@@ -3757,7 +3762,7 @@ template <typename IteratorType> class iteration_proxy_value
     /// return key of the iterator
     const string_type& key() const
     {
-        assert(anchor.m_object != nullptr);
+        JSON_ASSERT(anchor.m_object != nullptr);
 
         switch (anchor.m_object->type())
         {
@@ -4451,7 +4456,6 @@ class byte_container_with_subtype : public BinaryType
 
 #include <algorithm> // generate_n
 #include <array> // array
-#include <cassert> // assert
 #include <cmath> // ldexp
 #include <cstddef> // size_t
 #include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
@@ -4468,7 +4472,6 @@ class byte_container_with_subtype : public BinaryType
 
 
 #include <array> // array
-#include <cassert> // assert
 #include <cstddef> // size_t
 #include <cstdio> //FILE *
 #include <cstring> // strlen
@@ -4766,13 +4769,13 @@ class wide_string_input_adapter
         {
             fill_buffer<sizeof(WideCharType)>();
 
-            assert(utf8_bytes_filled > 0);
-            assert(utf8_bytes_index == 0);
+            JSON_ASSERT(utf8_bytes_filled > 0);
+            JSON_ASSERT(utf8_bytes_index == 0);
         }
 
         // use buffer
-        assert(utf8_bytes_filled > 0);
-        assert(utf8_bytes_index < utf8_bytes_filled);
+        JSON_ASSERT(utf8_bytes_filled > 0);
+        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
         return utf8_bytes[utf8_bytes_index++];
     }
 
@@ -4926,7 +4929,6 @@ class span_input_adapter
 // #include <nlohmann/detail/input/json_sax.hpp>
 
 
-#include <cassert> // assert
 #include <cstddef>
 #include <string> // string
 #include <utility> // move
@@ -5208,7 +5210,7 @@ class json_sax_dom_parser
                 case 5:
                     JSON_THROW(*dynamic_cast<const detail::other_error*>(&ex));
                 default:
-                    assert(false);
+                    JSON_ASSERT(false);
                     // LCOV_EXCL_STOP
             }
         }
@@ -5237,7 +5239,7 @@ class json_sax_dom_parser
             return &root;
         }
 
-        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
+        JSON_ASSERT(ref_stack.back()->is_array() or ref_stack.back()->is_object());
 
         if (ref_stack.back()->is_array())
         {
@@ -5245,8 +5247,8 @@ class json_sax_dom_parser
             return &(ref_stack.back()->m_value.array->back());
         }
 
-        assert(ref_stack.back()->is_object());
-        assert(object_element);
+        JSON_ASSERT(ref_stack.back()->is_object());
+        JSON_ASSERT(object_element);
         *object_element = BasicJsonType(std::forward<Value>(v));
         return object_element;
     }
@@ -5375,8 +5377,8 @@ class json_sax_dom_callback_parser
             *ref_stack.back() = discarded;
         }
 
-        assert(not ref_stack.empty());
-        assert(not keep_stack.empty());
+        JSON_ASSERT(not ref_stack.empty());
+        JSON_ASSERT(not keep_stack.empty());
         ref_stack.pop_back();
         keep_stack.pop_back();
 
@@ -5427,8 +5429,8 @@ class json_sax_dom_callback_parser
             }
         }
 
-        assert(not ref_stack.empty());
-        assert(not keep_stack.empty());
+        JSON_ASSERT(not ref_stack.empty());
+        JSON_ASSERT(not keep_stack.empty());
         ref_stack.pop_back();
         keep_stack.pop_back();
 
@@ -5462,7 +5464,7 @@ class json_sax_dom_callback_parser
                 case 5:
                     JSON_THROW(*dynamic_cast<const detail::other_error*>(&ex));
                 default:
-                    assert(false);
+                    JSON_ASSERT(false);
                     // LCOV_EXCL_STOP
             }
         }
@@ -5493,7 +5495,7 @@ class json_sax_dom_callback_parser
     template<typename Value>
     std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
     {
-        assert(not keep_stack.empty());
+        JSON_ASSERT(not keep_stack.empty());
 
         // do not handle this value if we know it would be added to a discarded
         // container
@@ -5528,7 +5530,7 @@ class json_sax_dom_callback_parser
         }
 
         // we now only expect arrays and objects
-        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
+        JSON_ASSERT(ref_stack.back()->is_array() or ref_stack.back()->is_object());
 
         // array
         if (ref_stack.back()->is_array())
@@ -5538,9 +5540,9 @@ class json_sax_dom_callback_parser
         }
 
         // object
-        assert(ref_stack.back()->is_object());
+        JSON_ASSERT(ref_stack.back()->is_object());
         // check if we should store an element for the current key
-        assert(not key_keep_stack.empty());
+        JSON_ASSERT(not key_keep_stack.empty());
         const bool store_element = key_keep_stack.back();
         key_keep_stack.pop_back();
 
@@ -5549,7 +5551,7 @@ class json_sax_dom_callback_parser
             return {false, nullptr};
         }
 
-        assert(object_element);
+        JSON_ASSERT(object_element);
         *object_element = std::move(value);
         return {true, object_element};
     }
@@ -5893,7 +5895,7 @@ class binary_reader
                 break;
 
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
 
         // strict mode: next byte must be EOF
@@ -6501,8 +6503,8 @@ class binary_reader
                 {
                     const int exp = (half >> 10u) & 0x1Fu;
                     const unsigned int mant = half & 0x3FFu;
-                    assert(0 <= exp and exp <= 32);
-                    assert(mant <= 1024);
+                    JSON_ASSERT(0 <= exp and exp <= 32);
+                    JSON_ASSERT(mant <= 1024);
                     switch (exp)
                     {
                         case 0:
@@ -8079,7 +8081,7 @@ class binary_reader
                 break;
 
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
 
         return error_msg + " " + context + ": " + detail;
@@ -8247,7 +8249,7 @@ class lexer : public lexer_base<BasicJsonType>
     static char get_decimal_point() noexcept
     {
         const auto* loc = localeconv();
-        assert(loc != nullptr);
+        JSON_ASSERT(loc != nullptr);
         return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
     }
 
@@ -8273,7 +8275,7 @@ class lexer : public lexer_base<BasicJsonType>
     int get_codepoint()
     {
         // this function only makes sense after reading `\u`
-        assert(current == 'u');
+        JSON_ASSERT(current == 'u');
         int codepoint = 0;
 
         const auto factors = { 12u, 8u, 4u, 0u };
@@ -8299,7 +8301,7 @@ class lexer : public lexer_base<BasicJsonType>
             }
         }
 
-        assert(0x0000 <= codepoint and codepoint <= 0xFFFF);
+        JSON_ASSERT(0x0000 <= codepoint and codepoint <= 0xFFFF);
         return codepoint;
     }
 
@@ -8320,7 +8322,7 @@ class lexer : public lexer_base<BasicJsonType>
     */
     bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
     {
-        assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
+        JSON_ASSERT(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
         add(current);
 
         for (auto range = ranges.begin(); range != ranges.end(); ++range)
@@ -8361,7 +8363,7 @@ class lexer : public lexer_base<BasicJsonType>
         reset();
 
         // we entered the function by reading an open quote
-        assert(current == '\"');
+        JSON_ASSERT(current == '\"');
 
         while (true)
         {
@@ -8481,7 +8483,7 @@ class lexer : public lexer_base<BasicJsonType>
                             }
 
                             // result of the above calculation yields a proper codepoint
-                            assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
+                            JSON_ASSERT(0x00 <= codepoint and codepoint <= 0x10FFFF);
 
                             // translate codepoint into bytes
                             if (codepoint < 0x80)
@@ -9110,7 +9112,7 @@ class lexer : public lexer_base<BasicJsonType>
 
             // all other characters are rejected outside scan_number()
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
 
 scan_number_minus:
@@ -9357,7 +9359,7 @@ scan_number_done:
             const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
 
             // we checked the number format before
-            assert(endptr == token_buffer.data() + token_buffer.size());
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
 
             if (errno == 0)
             {
@@ -9373,7 +9375,7 @@ scan_number_done:
             const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
 
             // we checked the number format before
-            assert(endptr == token_buffer.data() + token_buffer.size());
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
 
             if (errno == 0)
             {
@@ -9390,7 +9392,7 @@ scan_number_done:
         strtof(value_float, token_buffer.data(), &endptr);
 
         // we checked the number format before
-        assert(endptr == token_buffer.data() + token_buffer.size());
+        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
 
         return token_type::value_float;
     }
@@ -9404,7 +9406,7 @@ scan_number_done:
     token_type scan_literal(const char_type* literal_text, const std::size_t length,
                             token_type return_type)
     {
-        assert(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
+        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
         for (std::size_t i = 1; i < length; ++i)
         {
             if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))
@@ -9496,7 +9498,7 @@ scan_number_done:
 
         if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
         {
-            assert(not token_string.empty());
+            JSON_ASSERT(not token_string.empty());
             token_string.pop_back();
         }
     }
@@ -9737,7 +9739,6 @@ scan_number_done:
 // #include <nlohmann/detail/input/parser.hpp>
 
 
-#include <cassert> // assert
 #include <cmath> // isfinite
 #include <cstdint> // uint8_t
 #include <functional> // function
@@ -10127,7 +10128,7 @@ class parser
                     // new value, we need to evaluate the new state first.
                     // By setting skip_to_state_evaluation to false, we
                     // are effectively jumping to the beginning of this if.
-                    assert(not states.empty());
+                    JSON_ASSERT(not states.empty());
                     states.pop_back();
                     skip_to_state_evaluation = true;
                     continue;
@@ -10183,7 +10184,7 @@ class parser
                     // new value, we need to evaluate the new state first.
                     // By setting skip_to_state_evaluation to false, we
                     // are effectively jumping to the beginning of this if.
-                    assert(not states.empty());
+                    JSON_ASSERT(not states.empty());
                     states.pop_back();
                     skip_to_state_evaluation = true;
                     continue;
@@ -10489,7 +10490,7 @@ class iter_impl
     */
     explicit iter_impl(pointer object) noexcept : m_object(object)
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10575,7 +10576,7 @@ class iter_impl
     */
     void set_begin() noexcept
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10612,7 +10613,7 @@ class iter_impl
     */
     void set_end() noexcept
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10643,19 +10644,19 @@ class iter_impl
     */
     reference operator*() const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
             case value_t::object:
             {
-                assert(m_it.object_iterator != m_object->m_value.object->end());
+                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
                 return m_it.object_iterator->second;
             }
 
             case value_t::array:
             {
-                assert(m_it.array_iterator != m_object->m_value.array->end());
+                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
                 return *m_it.array_iterator;
             }
 
@@ -10680,19 +10681,19 @@ class iter_impl
     */
     pointer operator->() const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
             case value_t::object:
             {
-                assert(m_it.object_iterator != m_object->m_value.object->end());
+                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
                 return &(m_it.object_iterator->second);
             }
 
             case value_t::array:
             {
-                assert(m_it.array_iterator != m_object->m_value.array->end());
+                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
                 return &*m_it.array_iterator;
             }
 
@@ -10725,7 +10726,7 @@ class iter_impl
     */
     iter_impl& operator++()
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10768,7 +10769,7 @@ class iter_impl
     */
     iter_impl& operator--()
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10806,7 +10807,7 @@ class iter_impl
             JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
         }
 
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10842,7 +10843,7 @@ class iter_impl
             JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
         }
 
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10890,7 +10891,7 @@ class iter_impl
     */
     iter_impl& operator+=(difference_type i)
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10961,7 +10962,7 @@ class iter_impl
     */
     difference_type operator-(const iter_impl& other) const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -10982,7 +10983,7 @@ class iter_impl
     */
     reference operator[](difference_type n) const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         switch (m_object->m_type)
         {
@@ -11013,7 +11014,7 @@ class iter_impl
     */
     const typename object_t::key_type& key() const
     {
-        assert(m_object != nullptr);
+        JSON_ASSERT(m_object != nullptr);
 
         if (JSON_HEDLEY_LIKELY(m_object->is_object()))
         {
@@ -11170,7 +11171,6 @@ class json_reverse_iterator : public std::reverse_iterator<Base>
 
 
 #include <algorithm> // all_of
-#include <cassert> // assert
 #include <cctype> // isdigit
 #include <limits> // max
 #include <numeric> // accumulate
@@ -11953,7 +11953,7 @@ class json_pointer
                     pos != std::string::npos;
                     pos = reference_token.find_first_of('~', pos + 1))
             {
-                assert(reference_token[pos] == '~');
+                JSON_ASSERT(reference_token[pos] == '~');
 
                 // ~ must be followed by 0 or 1
                 if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or
@@ -11988,7 +11988,7 @@ class json_pointer
     static void replace_substring(std::string& s, const std::string& f,
                                   const std::string& t)
     {
-        assert(not f.empty());
+        JSON_ASSERT(not f.empty());
         for (auto pos = s.find(f);                // find first occurrence of f
                 pos != std::string::npos;         // make sure f was found
                 s.replace(pos, f.size(), t),      // replace with t, and
@@ -12400,7 +12400,7 @@ class binary_writer
     */
     explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
     {
-        assert(oa);
+        JSON_ASSERT(oa);
     }
 
     /*!
@@ -13116,7 +13116,7 @@ class binary_writer
                 bool prefix_required = true;
                 if (use_type and not j.m_value.array->empty())
                 {
-                    assert(use_count);
+                    JSON_ASSERT(use_count);
                     const CharType first_prefix = ubjson_prefix(j.front());
                     const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
                                                          [this, first_prefix](const BasicJsonType & v)
@@ -13160,7 +13160,7 @@ class binary_writer
 
                 if (use_type and not j.m_value.binary->empty())
                 {
-                    assert(use_count);
+                    JSON_ASSERT(use_count);
                     oa->write_character(to_char_type('$'));
                     oa->write_character('U');
                 }
@@ -13204,7 +13204,7 @@ class binary_writer
                 bool prefix_required = true;
                 if (use_type and not j.m_value.object->empty())
                 {
-                    assert(use_count);
+                    JSON_ASSERT(use_count);
                     const CharType first_prefix = ubjson_prefix(j.front());
                     const bool same_prefix = std::all_of(j.begin(), j.end(),
                                                          [this, first_prefix](const BasicJsonType & v)
@@ -13496,7 +13496,7 @@ class binary_writer
 
             // LCOV_EXCL_START
             default:
-                assert(false);
+                JSON_ASSERT(false);
                 return 0ul;
                 // LCOV_EXCL_STOP
         }
@@ -13543,7 +13543,7 @@ class binary_writer
 
             // LCOV_EXCL_START
             default:
-                assert(false);
+                JSON_ASSERT(false);
                 return;
                 // LCOV_EXCL_STOP
         }
@@ -13935,7 +13935,6 @@ class binary_writer
 
 #include <algorithm> // reverse, remove, fill, find, none_of
 #include <array> // array
-#include <cassert> // assert
 #include <clocale> // localeconv, lconv
 #include <cmath> // labs, isfinite, isnan, signbit
 #include <cstddef> // size_t, ptrdiff_t
@@ -13952,7 +13951,6 @@ class binary_writer
 
 
 #include <array> // array
-#include <cassert> // assert
 #include <cmath>   // signbit, isfinite
 #include <cstdint> // intN_t, uintN_t
 #include <cstring> // memcpy, memmove
@@ -14016,8 +14014,8 @@ struct diyfp // f * 2^e
     */
     static diyfp sub(const diyfp& x, const diyfp& y) noexcept
     {
-        assert(x.e == y.e);
-        assert(x.f >= y.f);
+        JSON_ASSERT(x.e == y.e);
+        JSON_ASSERT(x.f >= y.f);
 
         return {x.f - y.f, x.e};
     }
@@ -14093,7 +14091,7 @@ struct diyfp // f * 2^e
     */
     static diyfp normalize(diyfp x) noexcept
     {
-        assert(x.f != 0);
+        JSON_ASSERT(x.f != 0);
 
         while ((x.f >> 63u) == 0)
         {
@@ -14112,8 +14110,8 @@ struct diyfp // f * 2^e
     {
         const int delta = x.e - target_exponent;
 
-        assert(delta >= 0);
-        assert(((x.f << delta) >> delta) == x.f);
+        JSON_ASSERT(delta >= 0);
+        JSON_ASSERT(((x.f << delta) >> delta) == x.f);
 
         return {x.f << delta, target_exponent};
     }
@@ -14135,8 +14133,8 @@ boundaries.
 template <typename FloatType>
 boundaries compute_boundaries(FloatType value)
 {
-    assert(std::isfinite(value));
-    assert(value > 0);
+    JSON_ASSERT(std::isfinite(value));
+    JSON_ASSERT(value > 0);
 
     // Convert the IEEE representation into a diyfp.
     //
@@ -14416,18 +14414,18 @@ inline cached_power get_cached_power_for_binary_exponent(int e)
     //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)
     // for |e| <= 1500, but doesn't require floating-point operations.
     // NB: log_10(2) ~= 78913 / 2^18
-    assert(e >= -1500);
-    assert(e <=  1500);
+    JSON_ASSERT(e >= -1500);
+    JSON_ASSERT(e <=  1500);
     const int f = kAlpha - e - 1;
     const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
 
     const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
-    assert(index >= 0);
-    assert(static_cast<std::size_t>(index) < kCachedPowers.size());
+    JSON_ASSERT(index >= 0);
+    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());
 
     const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
-    assert(kAlpha <= cached.e + e + 64);
-    assert(kGamma >= cached.e + e + 64);
+    JSON_ASSERT(kAlpha <= cached.e + e + 64);
+    JSON_ASSERT(kGamma >= cached.e + e + 64);
 
     return cached;
 }
@@ -14495,10 +14493,10 @@ inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)
 inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,
                          std::uint64_t rest, std::uint64_t ten_k)
 {
-    assert(len >= 1);
-    assert(dist <= delta);
-    assert(rest <= delta);
-    assert(ten_k > 0);
+    JSON_ASSERT(len >= 1);
+    JSON_ASSERT(dist <= delta);
+    JSON_ASSERT(rest <= delta);
+    JSON_ASSERT(ten_k > 0);
 
     //               <--------------------------- delta ---->
     //                                  <---- dist --------->
@@ -14523,7 +14521,7 @@ inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t d
             and delta - rest >= ten_k
             and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))
     {
-        assert(buf[len - 1] != '0');
+        JSON_ASSERT(buf[len - 1] != '0');
         buf[len - 1]--;
         rest += ten_k;
     }
@@ -14551,8 +14549,8 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
     // Grisu2 generates the digits of M+ from left to right and stops as soon as
     // V is in [M-,M+].
 
-    assert(M_plus.e >= kAlpha);
-    assert(M_plus.e <= kGamma);
+    JSON_ASSERT(M_plus.e >= kAlpha);
+    JSON_ASSERT(M_plus.e <= kGamma);
 
     std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
     std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)
@@ -14573,7 +14571,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
     //
     // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
 
-    assert(p1 > 0);
+    JSON_ASSERT(p1 > 0);
 
     std::uint32_t pow10;
     const int k = find_largest_pow10(p1, pow10);
@@ -14609,7 +14607,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
         //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
         //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
         //
-        assert(d <= 9);
+        JSON_ASSERT(d <= 9);
         buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
         //
         //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
@@ -14696,7 +14694,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
     //
     // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
 
-    assert(p2 > delta);
+    JSON_ASSERT(p2 > delta);
 
     int m = 0;
     for (;;)
@@ -14707,7 +14705,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
         //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e
         //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
         //
-        assert(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
+        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
         p2 *= 10;
         const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e
         const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
@@ -14716,7 +14714,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
         //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
         //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
         //
-        assert(d <= 9);
+        JSON_ASSERT(d <= 9);
         buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
         //
         //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
@@ -14777,8 +14775,8 @@ JSON_HEDLEY_NON_NULL(1)
 inline void grisu2(char* buf, int& len, int& decimal_exponent,
                    diyfp m_minus, diyfp v, diyfp m_plus)
 {
-    assert(m_plus.e == m_minus.e);
-    assert(m_plus.e == v.e);
+    JSON_ASSERT(m_plus.e == m_minus.e);
+    JSON_ASSERT(m_plus.e == v.e);
 
     //  --------(-----------------------+-----------------------)--------    (A)
     //          m-                      v                       m+
@@ -14839,8 +14837,8 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
     static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
                   "internal error: not enough precision");
 
-    assert(std::isfinite(value));
-    assert(value > 0);
+    JSON_ASSERT(std::isfinite(value));
+    JSON_ASSERT(value > 0);
 
     // If the neighbors (and boundaries) of 'value' are always computed for double-precision
     // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
@@ -14876,8 +14874,8 @@ JSON_HEDLEY_NON_NULL(1)
 JSON_HEDLEY_RETURNS_NON_NULL
 inline char* append_exponent(char* buf, int e)
 {
-    assert(e > -1000);
-    assert(e <  1000);
+    JSON_ASSERT(e > -1000);
+    JSON_ASSERT(e <  1000);
 
     if (e < 0)
     {
@@ -14929,8 +14927,8 @@ JSON_HEDLEY_RETURNS_NON_NULL
 inline char* format_buffer(char* buf, int len, int decimal_exponent,
                            int min_exp, int max_exp)
 {
-    assert(min_exp < 0);
-    assert(max_exp > 0);
+    JSON_ASSERT(min_exp < 0);
+    JSON_ASSERT(max_exp > 0);
 
     const int k = len;
     const int n = len + decimal_exponent;
@@ -14956,7 +14954,7 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent,
         // dig.its
         // len <= max_digits10 + 1
 
-        assert(k > n);
+        JSON_ASSERT(k > n);
 
         std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
         buf[n] = '.';
@@ -15014,7 +15012,7 @@ JSON_HEDLEY_RETURNS_NON_NULL
 char* to_chars(char* first, const char* last, FloatType value)
 {
     static_cast<void>(last); // maybe unused - fix warning
-    assert(std::isfinite(value));
+    JSON_ASSERT(std::isfinite(value));
 
     // Use signbit(value) instead of (value < 0) since signbit works for -0.
     if (std::signbit(value))
@@ -15032,7 +15030,7 @@ char* to_chars(char* first, const char* last, FloatType value)
         return first;
     }
 
-    assert(last - first >= std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
 
     // Compute v = buffer * 10^decimal_exponent.
     // The decimal digits are stored in the buffer, which needs to be interpreted
@@ -15042,16 +15040,16 @@ char* to_chars(char* first, const char* last, FloatType value)
     int decimal_exponent = 0;
     dtoa_impl::grisu2(first, len, decimal_exponent, value);
 
-    assert(len <= std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
 
     // Format the buffer like printf("%.*g", prec, value)
     constexpr int kMinExp = -4;
     // Use digits10 here to increase compatibility with version 2.
     constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
 
-    assert(last - first >= kMaxExp + 2);
-    assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
-    assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+    JSON_ASSERT(last - first >= kMaxExp + 2);
+    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
 
     return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
 }
@@ -15185,8 +15183,8 @@ class serializer
                     }
 
                     // last element
-                    assert(i != val.m_value.object->cend());
-                    assert(std::next(i) == val.m_value.object->cend());
+                    JSON_ASSERT(i != val.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());
                     o->write_characters(indent_string.c_str(), new_indent);
                     o->write_character('\"');
                     dump_escaped(i->first, ensure_ascii);
@@ -15213,8 +15211,8 @@ class serializer
                     }
 
                     // last element
-                    assert(i != val.m_value.object->cend());
-                    assert(std::next(i) == val.m_value.object->cend());
+                    JSON_ASSERT(i != val.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());
                     o->write_character('\"');
                     dump_escaped(i->first, ensure_ascii);
                     o->write_characters("\":", 2);
@@ -15255,7 +15253,7 @@ class serializer
                     }
 
                     // last element
-                    assert(not val.m_value.array->empty());
+                    JSON_ASSERT(not val.m_value.array->empty());
                     o->write_characters(indent_string.c_str(), new_indent);
                     dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
 
@@ -15276,7 +15274,7 @@ class serializer
                     }
 
                     // last element
-                    assert(not val.m_value.array->empty());
+                    JSON_ASSERT(not val.m_value.array->empty());
                     dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
 
                     o->write_character(']');
@@ -15410,7 +15408,7 @@ class serializer
             }
 
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
     }
 
@@ -15609,7 +15607,7 @@ class serializer
                         }
 
                         default:            // LCOV_EXCL_LINE
-                            assert(false);  // LCOV_EXCL_LINE
+                            JSON_ASSERT(false);  // LCOV_EXCL_LINE
                     }
                     break;
                 }
@@ -15672,7 +15670,7 @@ class serializer
                 }
 
                 default:            // LCOV_EXCL_LINE
-                    assert(false);  // LCOV_EXCL_LINE
+                    JSON_ASSERT(false);  // LCOV_EXCL_LINE
             }
         }
     }
@@ -15773,7 +15771,7 @@ class serializer
         }
 
         // spare 1 byte for '\0'
-        assert(n_chars < number_buffer.size() - 1);
+        JSON_ASSERT(n_chars < number_buffer.size() - 1);
 
         // jump to the end to generate the string from backward
         // so we later avoid reversing the result
@@ -15849,9 +15847,9 @@ class serializer
         std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
 
         // negative value indicates an error
-        assert(len > 0);
+        JSON_ASSERT(len > 0);
         // check if buffer was large enough
-        assert(static_cast<std::size_t>(len) < number_buffer.size());
+        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
 
         // erase thousands separator
         if (thousands_sep != '\0')
@@ -15859,7 +15857,7 @@ class serializer
             const auto end = std::remove(number_buffer.begin(),
                                          number_buffer.begin() + len, thousands_sep);
             std::fill(end, number_buffer.end(), '\0');
-            assert((end - number_buffer.begin()) <= len);
+            JSON_ASSERT((end - number_buffer.begin()) <= len);
             len = (end - number_buffer.begin());
         }
 
@@ -15939,7 +15937,7 @@ class serializer
                 : (0xFFu >> type) & (byte);
 
         std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
-        assert(index < 400);
+        JSON_ASSERT(index < 400);
         state = utf8d[index];
         return state;
     }
@@ -15951,7 +15949,7 @@ class serializer
      */
     number_unsigned_t remove_sign(number_unsigned_t x)
     {
-        assert(false); // LCOV_EXCL_LINE
+        JSON_ASSERT(false); // LCOV_EXCL_LINE
         return x; // LCOV_EXCL_LINE
     }
 
@@ -15966,7 +15964,7 @@ class serializer
      */
     inline number_unsigned_t remove_sign(number_integer_t x) noexcept
     {
-        assert(x < 0 and x < (std::numeric_limits<number_integer_t>::max)());
+        JSON_ASSERT(x < 0 and x < (std::numeric_limits<number_integer_t>::max)());
         return static_cast<number_unsigned_t>(-(x + 1)) + 1;
     }
 
@@ -16854,7 +16852,7 @@ class basic_json
         };
         std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);
         AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);
-        assert(object != nullptr);
+        JSON_ASSERT(object != nullptr);
         return object.release();
     }
 
@@ -17149,10 +17147,10 @@ class basic_json
     */
     void assert_invariant() const noexcept
     {
-        assert(m_type != value_t::object or m_value.object != nullptr);
-        assert(m_type != value_t::array or m_value.array != nullptr);
-        assert(m_type != value_t::string or m_value.string != nullptr);
-        assert(m_type != value_t::binary or m_value.binary != nullptr);
+        JSON_ASSERT(m_type != value_t::object or m_value.object != nullptr);
+        JSON_ASSERT(m_type != value_t::array or m_value.array != nullptr);
+        JSON_ASSERT(m_type != value_t::string or m_value.string != nullptr);
+        JSON_ASSERT(m_type != value_t::binary or m_value.binary != nullptr);
     }
 
   public:
@@ -17445,7 +17443,7 @@ class basic_json
                 m_type = value_t::discarded;
                 break;
             default:            // LCOV_EXCL_LINE
-                assert(false);  // LCOV_EXCL_LINE
+                JSON_ASSERT(false);  // LCOV_EXCL_LINE
         }
         assert_invariant();
     }
@@ -17845,8 +17843,8 @@ class basic_json
                  std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>
     basic_json(InputIT first, InputIT last)
     {
-        assert(first.m_object != nullptr);
-        assert(last.m_object != nullptr);
+        JSON_ASSERT(first.m_object != nullptr);
+        JSON_ASSERT(last.m_object != nullptr);
 
         // make sure iterator fits the current value
         if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
@@ -19563,7 +19561,7 @@ class basic_json
         // const operator[] only works for objects
         if (JSON_HEDLEY_LIKELY(is_object()))
         {
-            assert(m_value.object->find(key) != m_value.object->end());
+            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
 
@@ -19655,7 +19653,7 @@ class basic_json
         // at only works for objects
         if (JSON_HEDLEY_LIKELY(is_object()))
         {
-            assert(m_value.object->find(key) != m_value.object->end());
+            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
 
@@ -21423,7 +21421,7 @@ class basic_json
     iterator insert_iterator(const_iterator pos, Args&& ... args)
     {
         iterator result(this);
-        assert(m_value.array != nullptr);
+        JSON_ASSERT(m_value.array != nullptr);
 
         auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);
         m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
@@ -24188,7 +24186,7 @@ class basic_json
 
                 // if there exists a parent it cannot be primitive
                 default:            // LCOV_EXCL_LINE
-                    assert(false);  // LCOV_EXCL_LINE
+                    JSON_ASSERT(false);  // LCOV_EXCL_LINE
             }
         };
 
@@ -24723,6 +24721,7 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
 #endif
 
 // clean up
+#undef JSON_ASSERT
 #undef JSON_INTERNAL_CATCH
 #undef JSON_CATCH
 #undef JSON_THROW

From efcc826ecb9b55893397f749e5514316ba8629bb Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 6 Jul 2020 12:37:39 +0200
Subject: [PATCH 08/18] :rotating_light: fix warning

---
 Makefile                    |  2 +-
 README.md                   |  4 ++--
 test/src/unit-udt_macro.cpp | 46 ++++++++++++++++++-------------------
 3 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/Makefile b/Makefile
index 8579fb5d..ff62d6e7 100644
--- a/Makefile
+++ b/Makefile
@@ -253,7 +253,7 @@ pedantic_gcc:
 		-Wmismatched-tags                                 \
 		-Wmissing-attributes                              \
 		-Wmissing-braces                                  \
-		-Wmissing-declarations                            \
+		-Wno-missing-declarations                            \
 		-Wmissing-field-initializers                      \
 		-Wmissing-include-dirs                            \
 		-Wmissing-profile                                 \
diff --git a/README.md b/README.md
index 6d21e2d8..3bb92d38 100644
--- a/README.md
+++ b/README.md
@@ -886,7 +886,7 @@ The `to_json`/`from_json` functions for the `person` struct above can be created
 
 ```cpp
 namespace ns {
-    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age);
+    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age)
 }
 ```
 
@@ -901,7 +901,7 @@ namespace ns {
         int postcode;
         
       public:
-        NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode);
+        NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode)
     };
 }
 ```
diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp
index 4535ae5b..a6b66e95 100644
--- a/test/src/unit-udt_macro.cpp
+++ b/test/src/unit-udt_macro.cpp
@@ -37,9 +37,9 @@ namespace persons
 class person_with_private_data
 {
   private:
-    std::string name;
+    std::string name = "";
     int age = 0;
-    json metadata;
+    json metadata = nullptr;
 
   public:
     bool operator==(const person_with_private_data& rhs) const
@@ -48,21 +48,21 @@ class person_with_private_data
     }
 
     person_with_private_data() = default;
-    person_with_private_data(std::string name, int age, json metadata)
-        : name(std::move(name))
-        , age(age)
-        , metadata(std::move(metadata))
+    person_with_private_data(std::string name_, int age_, json metadata_)
+        : name(std::move(name_))
+        , age(age_)
+        , metadata(std::move(metadata_))
     {}
 
-    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata);
+    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata)
 };
 
 class person_without_private_data_1
 {
   public:
-    std::string name;
-    int age = 0;
-    json metadata;
+  std::string name = "";
+  int age = 0;
+  json metadata = nullptr;
 
     bool operator==(const person_without_private_data_1& rhs) const
     {
@@ -70,21 +70,21 @@ class person_without_private_data_1
     }
 
     person_without_private_data_1() = default;
-    person_without_private_data_1(std::string name, int age, json metadata)
-        : name(std::move(name))
-        , age(age)
-        , metadata(std::move(metadata))
+    person_without_private_data_1(std::string name_, int age_, json metadata_)
+        : name(std::move(name_))
+        , age(age_)
+        , metadata(std::move(metadata_))
     {}
 
-    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata);
+    NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata)
 };
 
 class person_without_private_data_2
 {
   public:
-    std::string name;
-    int age = 0;
-    json metadata;
+  std::string name = "";
+  int age = 0;
+  json metadata = nullptr;
 
     bool operator==(const person_without_private_data_2& rhs) const
     {
@@ -92,14 +92,14 @@ class person_without_private_data_2
     }
 
     person_without_private_data_2() = default;
-    person_without_private_data_2(std::string name, int age, json metadata)
-        : name(std::move(name))
-        , age(age)
-        , metadata(std::move(metadata))
+    person_without_private_data_2(std::string name_, int age_, json metadata_)
+        : name(std::move(name_))
+        , age(age_)
+        , metadata(std::move(metadata_))
     {}
 };
 
-NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata)
 } // namespace persons
 
 TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE", T,

From ba8174041e66038c4c02814ecbf2f8e062f2291c Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 6 Jul 2020 12:52:48 +0200
Subject: [PATCH 09/18] :white_check_mark: add test case for JSON_ASSERT

---
 test/CMakeLists.txt            |  1 +
 test/src/unit-assert_macro.cpp | 50 ++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)
 create mode 100644 test/src/unit-assert_macro.cpp

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 8434f78d..43af37d5 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -95,6 +95,7 @@ set(files
     src/unit-algorithms.cpp
     src/unit-allocator.cpp
     src/unit-alt-string.cpp
+    src/unit-assert_macro.cpp
     src/unit-bson.cpp
     src/unit-capacity.cpp
     src/unit-cbor.cpp
diff --git a/test/src/unit-assert_macro.cpp b/test/src/unit-assert_macro.cpp
new file mode 100644
index 00000000..009a2b0b
--- /dev/null
+++ b/test/src/unit-assert_macro.cpp
@@ -0,0 +1,50 @@
+/*
+    __ _____ _____ _____
+ __|  |   __|     |   | |  JSON for Modern C++ (test suite)
+|  |  |__   |  |  | | | |  version 3.8.0
+|_____|_____|_____|_|___|  https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+SPDX-License-Identifier: MIT
+Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
+
+Permission is hereby  granted, free of charge, to any  person obtaining a copy
+of this software and associated  documentation files (the "Software"), to deal
+in the Software  without restriction, including without  limitation the rights
+to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
+copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
+IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
+FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
+AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
+LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "doctest_compatibility.h"
+
+/// global variable to record side effect of assert calls
+int assert_counter = 0;
+
+/// set failure variable to true instead of calling assert(x)
+#define JSON_ASSERT(x) if (!(x)) ++assert_counter;
+
+#include <nlohmann/json.hpp>
+using nlohmann::json;
+
+TEST_CASE("JSON_ASSERT(x)")
+{
+  const json j = {{"bar", 1}};
+  CHECK(assert_counter == 0);
+
+  // accessing non-exising key in const value would assert
+  j["foo"] == 1;
+
+  CHECK(assert_counter == 1);
+}
\ No newline at end of file

From 28ef87370be43ee4ea0d35c33097202c2d71fd2e Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 6 Jul 2020 13:19:06 +0200
Subject: [PATCH 10/18] :rotating_light: fix warning

---
 Makefile                       |  2 ++
 test/src/unit-assert_macro.cpp | 25 +++++++++++++++++--------
 2 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/Makefile b/Makefile
index ff62d6e7..3d296baf 100644
--- a/Makefile
+++ b/Makefile
@@ -97,6 +97,7 @@ doctest:
 # -Wno-exit-time-destructors: warning in json code triggered by NLOHMANN_JSON_SERIALIZE_ENUM
 # -Wno-float-equal: not all comparisons in the tests can be replaced by Approx
 # -Wno-keyword-macro: unit-tests use "#define private public"
+# -Wno-missing-prototypes: for NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
 # -Wno-padded: padding is nothing to warn about
 # -Wno-range-loop-analysis: items tests "for(const auto i...)"
 # -Wno-switch-enum -Wno-covered-switch-default: pedantic/contradicting warnings about switches
@@ -113,6 +114,7 @@ pedantic_clang:
 		-Wno-exit-time-destructors \
 		-Wno-float-equal \
 		-Wno-keyword-macro \
+		-Wno-missing-prototypes \
 		-Wno-padded \
 		-Wno-range-loop-analysis \
 		-Wno-switch-enum -Wno-covered-switch-default \
diff --git a/test/src/unit-assert_macro.cpp b/test/src/unit-assert_macro.cpp
index 009a2b0b..c5cd3a01 100644
--- a/test/src/unit-assert_macro.cpp
+++ b/test/src/unit-assert_macro.cpp
@@ -27,24 +27,33 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+// avoid warning when assert does not abort
+#if defined(__GNUC__)
+    #pragma GCC diagnostic ignored "-Wstrict-overflow"
+#endif
+
 #include "doctest_compatibility.h"
 
 /// global variable to record side effect of assert calls
-int assert_counter = 0;
+static int assert_counter;
 
 /// set failure variable to true instead of calling assert(x)
-#define JSON_ASSERT(x) if (!(x)) ++assert_counter;
+#define JSON_ASSERT(x) {if (!(x)) ++assert_counter; }
 
 #include <nlohmann/json.hpp>
 using nlohmann::json;
 
 TEST_CASE("JSON_ASSERT(x)")
 {
-  const json j = {{"bar", 1}};
-  CHECK(assert_counter == 0);
+    assert_counter = 0;
+    const json j = {{"bar", 1}};
+    CHECK(assert_counter == 0);
 
-  // accessing non-exising key in const value would assert
-  j["foo"] == 1;
+    // accessing non-existing key in const value would assert
+    if (j["foo"] == 1)
+    {
+        CHECK(true);
+    }
 
-  CHECK(assert_counter == 1);
-}
\ No newline at end of file
+    CHECK(assert_counter == 1);
+}

From d019ddfcdb26ee08cd30bf85a10e2a37703f1c56 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <niels.lohmann@gmail.com>
Date: Wed, 8 Jul 2020 12:25:53 +0200
Subject: [PATCH 11/18] :construction_worker: add code scanning

---
 .github/workflows/codeql-analysis.yml | 54 +++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 .github/workflows/codeql-analysis.yml

diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 00000000..5ee44a04
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,54 @@
+name: "Code scanning - action"
+
+on:
+  push:
+    branches: [develop, ]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [develop]
+  schedule:
+    - cron: '0 19 * * 1'
+
+jobs:
+  CodeQL-Build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+      with:
+        # We must fetch at least the immediate parents so that if this is
+        # a pull request then we can checkout the head.
+        fetch-depth: 2
+
+    # If this run was triggered by a pull request event, then checkout
+    # the head of the pull request instead of the merge commit.
+    - run: git checkout HEAD^2
+      if: ${{ github.event_name == 'pull_request' }}
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v1
+      # Override language selection by uncommenting this and choosing your languages
+      # with:
+      #   languages: go, javascript, csharp, python, cpp, java
+
+    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
+    # If this step fails, then you should remove it and run the build manually (see below)
+    - name: Autobuild
+      uses: github/codeql-action/autobuild@v1
+
+    # ℹ️ Command-line programs to run using the OS shell.
+    # 📚 https://git.io/JvXDl
+
+    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+    #    and modify them (or add more) to build your code if your project
+    #    uses a compiled language
+
+    #- run: |
+    #   make bootstrap
+    #   make release
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v1

From 1b04092c5c724941e75ed7b993440cec80e318b8 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Wed, 8 Jul 2020 12:38:46 +0200
Subject: [PATCH 12/18] :white_check_mark: fix test

---
 test/src/unit-assert_macro.cpp | 19 ++++++++++---------
 test/src/unit-udt_macro.cpp    | 12 ++++++------
 2 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/test/src/unit-assert_macro.cpp b/test/src/unit-assert_macro.cpp
index c5cd3a01..416cda23 100644
--- a/test/src/unit-assert_macro.cpp
+++ b/test/src/unit-assert_macro.cpp
@@ -45,15 +45,16 @@ using nlohmann::json;
 
 TEST_CASE("JSON_ASSERT(x)")
 {
-    assert_counter = 0;
-    const json j = {{"bar", 1}};
-    CHECK(assert_counter == 0);
-
-    // accessing non-existing key in const value would assert
-    if (j["foo"] == 1)
+    SECTION("basic_json(first, second)")
     {
-        CHECK(true);
-    }
+        assert_counter = 0;
+        CHECK(assert_counter == 0);
 
-    CHECK(assert_counter == 1);
+        json::iterator it;
+        json j;
+
+        CHECK_THROWS_WITH_AS(json(it, j.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator);
+
+        CHECK(assert_counter == 1);
+    }
 }
diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp
index a6b66e95..90d5e1cc 100644
--- a/test/src/unit-udt_macro.cpp
+++ b/test/src/unit-udt_macro.cpp
@@ -60,9 +60,9 @@ class person_with_private_data
 class person_without_private_data_1
 {
   public:
-  std::string name = "";
-  int age = 0;
-  json metadata = nullptr;
+    std::string name = "";
+    int age = 0;
+    json metadata = nullptr;
 
     bool operator==(const person_without_private_data_1& rhs) const
     {
@@ -82,9 +82,9 @@ class person_without_private_data_1
 class person_without_private_data_2
 {
   public:
-  std::string name = "";
-  int age = 0;
-  json metadata = nullptr;
+    std::string name = "";
+    int age = 0;
+    json metadata = nullptr;
 
     bool operator==(const person_without_private_data_2& rhs) const
     {

From e3e9ccfc027c26b75234ce1b681bb93929035563 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Wed, 8 Jul 2020 14:02:28 +0200
Subject: [PATCH 13/18] :ambulance: fix regression from #2181

---
 include/nlohmann/json.hpp        | 12 ++++++------
 single_include/nlohmann/json.hpp | 12 ++++++------
 test/src/unit-regression.cpp     |  9 +++++++++
 test/src/unit-udt_macro.cpp      | 12 ++++++------
 4 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 1dedb809..8a3957a6 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -3785,7 +3785,7 @@ class basic_json
     template<class ValueType, typename std::enable_if<
                  std::is_convertible<basic_json_t, ValueType>::value
                  and not std::is_same<value_t, ValueType>::value, int>::type = 0>
-    ValueType value(const typename object_t::key_type& key, ValueType && default_value) const
+    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
     {
         // at only works for objects
         if (JSON_HEDLEY_LIKELY(is_object()))
@@ -3797,7 +3797,7 @@ class basic_json
                 return *it;
             }
 
-            return std::move(default_value);
+            return default_value;
         }
 
         JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
@@ -3809,7 +3809,7 @@ class basic_json
     */
     string_t value(const typename object_t::key_type& key, const char* default_value) const
     {
-        return value(key, std::move(string_t(default_value)));
+        return value(key, string_t(default_value));
     }
 
     /*!
@@ -3857,7 +3857,7 @@ class basic_json
     */
     template<class ValueType, typename std::enable_if<
                  std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
-    ValueType value(const json_pointer& ptr, ValueType && default_value) const
+    ValueType value(const json_pointer& ptr, const ValueType& default_value) const
     {
         // at only works for objects
         if (JSON_HEDLEY_LIKELY(is_object()))
@@ -3869,7 +3869,7 @@ class basic_json
             }
             JSON_INTERNAL_CATCH (out_of_range&)
             {
-                return std::move(default_value);
+                return default_value;
             }
         }
 
@@ -3883,7 +3883,7 @@ class basic_json
     JSON_HEDLEY_NON_NULL(3)
     string_t value(const json_pointer& ptr, const char* default_value) const
     {
-        return value(ptr, std::move(string_t(default_value)));
+        return value(ptr, string_t(default_value));
     }
 
     /*!
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 87222900..05c3c925 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -19715,7 +19715,7 @@ class basic_json
     template<class ValueType, typename std::enable_if<
                  std::is_convertible<basic_json_t, ValueType>::value
                  and not std::is_same<value_t, ValueType>::value, int>::type = 0>
-    ValueType value(const typename object_t::key_type& key, ValueType && default_value) const
+    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
     {
         // at only works for objects
         if (JSON_HEDLEY_LIKELY(is_object()))
@@ -19727,7 +19727,7 @@ class basic_json
                 return *it;
             }
 
-            return std::move(default_value);
+            return default_value;
         }
 
         JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
@@ -19739,7 +19739,7 @@ class basic_json
     */
     string_t value(const typename object_t::key_type& key, const char* default_value) const
     {
-        return value(key, std::move(string_t(default_value)));
+        return value(key, string_t(default_value));
     }
 
     /*!
@@ -19787,7 +19787,7 @@ class basic_json
     */
     template<class ValueType, typename std::enable_if<
                  std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
-    ValueType value(const json_pointer& ptr, ValueType && default_value) const
+    ValueType value(const json_pointer& ptr, const ValueType& default_value) const
     {
         // at only works for objects
         if (JSON_HEDLEY_LIKELY(is_object()))
@@ -19799,7 +19799,7 @@ class basic_json
             }
             JSON_INTERNAL_CATCH (out_of_range&)
             {
-                return std::move(default_value);
+                return default_value;
             }
         }
 
@@ -19813,7 +19813,7 @@ class basic_json
     JSON_HEDLEY_NON_NULL(3)
     string_t value(const json_pointer& ptr, const char* default_value) const
     {
-        return value(ptr, std::move(string_t(default_value)));
+        return value(ptr, string_t(default_value));
     }
 
     /*!
diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp
index e5cd0337..3f9d9c26 100644
--- a/test/src/unit-regression.cpp
+++ b/test/src/unit-regression.cpp
@@ -1945,6 +1945,15 @@ TEST_CASE("regression tests")
                   )
         );
     }
+
+    SECTION("PR #2181 - regression bug with lvalue")
+    {
+        // see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060
+        json j{{"x", "test"}};
+        std::string defval = "default value";
+        auto val = j.value("x", defval);
+        auto val2 = j.value("y", defval);
+    }
 }
 
 #if not defined(JSON_NOEXCEPTION)
diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp
index a6b66e95..90d5e1cc 100644
--- a/test/src/unit-udt_macro.cpp
+++ b/test/src/unit-udt_macro.cpp
@@ -60,9 +60,9 @@ class person_with_private_data
 class person_without_private_data_1
 {
   public:
-  std::string name = "";
-  int age = 0;
-  json metadata = nullptr;
+    std::string name = "";
+    int age = 0;
+    json metadata = nullptr;
 
     bool operator==(const person_without_private_data_1& rhs) const
     {
@@ -82,9 +82,9 @@ class person_without_private_data_1
 class person_without_private_data_2
 {
   public:
-  std::string name = "";
-  int age = 0;
-  json metadata = nullptr;
+    std::string name = "";
+    int age = 0;
+    json metadata = nullptr;
 
     bool operator==(const person_without_private_data_2& rhs) const
     {

From f774a32d2baccc06d744dd8f794fca53babb7fd8 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Wed, 8 Jul 2020 14:47:22 +0200
Subject: [PATCH 14/18] :white_check_mark: fix test

---
 test/src/unit-assert_macro.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/test/src/unit-assert_macro.cpp b/test/src/unit-assert_macro.cpp
index 416cda23..06aa0770 100644
--- a/test/src/unit-assert_macro.cpp
+++ b/test/src/unit-assert_macro.cpp
@@ -43,6 +43,8 @@ static int assert_counter;
 #include <nlohmann/json.hpp>
 using nlohmann::json;
 
+// the test assumes exceptions to work
+#if not defined(JSON_NOEXCEPTION)
 TEST_CASE("JSON_ASSERT(x)")
 {
     SECTION("basic_json(first, second)")
@@ -53,8 +55,11 @@ TEST_CASE("JSON_ASSERT(x)")
         json::iterator it;
         json j;
 
+        // in case assertions do not abort execution, an exception is thrown
         CHECK_THROWS_WITH_AS(json(it, j.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator);
 
+        // check that assertion actually happened
         CHECK(assert_counter == 1);
     }
 }
+#endif

From c7d18c16259048878ab566c30aea2078a94b58fe Mon Sep 17 00:00:00 2001
From: Palmer Dabbelt <palmer@dabbelt.com>
Date: Thu, 8 Aug 2019 14:36:27 -0700
Subject: [PATCH 15/18] cmake: Generate and install a pkg-config file.

The meson builds install a pkg-config file, but the cmake builds don't.
This adds a pkg-config file to the cmake builds that is functionally
equivalent to the one generated from meson.
---
 CMakeLists.txt         | 12 +++++++++++-
 cmake/pkg-config.pc.in |  4 ++++
 2 files changed, 15 insertions(+), 1 deletion(-)
 create mode 100644 cmake/pkg-config.pc.in

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f7ba4fa3..372a7a03 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1)
 ## PROJECT
 ## name and version
 ##
-project(nlohmann_json VERSION 3.8.0 LANGUAGES CXX)
+project(nlohmann_json VERSION 3.8.0 DESCRIPTION "JSON for Modern C++" LANGUAGES CXX)
 
 ##
 ## INCLUDE
@@ -79,6 +79,12 @@ if (MSVC)
     )
 endif()
 
+# Install a pkg-config file, so other tools can find this.
+CONFIGURE_FILE(
+  "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
+)
+
 ##
 ## TESTS
 ## create and configure the unit test target
@@ -139,4 +145,8 @@ endif()
         NAMESPACE ${PROJECT_NAME}::
         DESTINATION ${NLOHMANN_JSON_CONFIG_INSTALL_DIR}
     )
+    install(
+        FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc"
+        DESTINATION lib/pkgconfig
+    )
 endif()
diff --git a/cmake/pkg-config.pc.in b/cmake/pkg-config.pc.in
new file mode 100644
index 00000000..680f10c2
--- /dev/null
+++ b/cmake/pkg-config.pc.in
@@ -0,0 +1,4 @@
+Name: ${PROJECT_NAME}
+Description: ${PROJECT_DESCRIPTION}
+Version: ${PROJECT_VERSION}
+Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}

From 251fce819c64b356d039f1777740ab9c0489d495 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=89rico=20Rolim?= <erico.erc@gmail.com>
Date: Wed, 8 Jul 2020 17:10:51 -0300
Subject: [PATCH 16/18] README: add explanation on how to use pkg-config.

---
 README.md | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/README.md b/README.md
index 3bb92d38..41d3145c 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@
 - [Integration](#integration)
   - [CMake](#cmake)
   - [Package Managers](#package-managers)
+  - [Pkg-config](#pkg-config)
 - [Examples](#examples)
   - [JSON as first-class data type](#json-as-first-class-data-type)
   - [Serialization / Deserialization](#serialization--deserialization)
@@ -230,6 +231,20 @@ Please file issues [here](https://github.com/build2-packaging/nlohmann-json) if
 
 If you are using [`wsjcpp`](https://wsjcpp.org), you can use the command `wsjcpp install "https://github.com/nlohmann/json:develop"` to get the latest version. Note you can change the branch ":develop" to an existing tag or another branch.
 
+### Pkg-config
+
+If you are using bare Makefiles, you can use `pkg-config` to generate the include flags that point to where the library is installed:
+
+```sh
+pkg-config nlohmann_json --cflags
+```
+
+Users of the Meson build system will also be able to use a system wide library, which will be found by `pkg-config`:
+
+```meson
+json = dependency('nlohmann_json', required: true)
+```
+
 ## Examples
 
 Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a5338e282d1d02bed389d852dd670d98d.html#a5338e282d1d02bed389d852dd670d98d)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)).

From d740622f961a07d73fa2f6e1f61841812c998b1f Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Thu, 9 Jul 2020 13:23:33 +0200
Subject: [PATCH 17/18] :pencil: add documentation for macros

---
 doc/mkdocs/docs/features/arbitrary_types.md | 37 ++++++++++++++
 doc/mkdocs/docs/features/macros.md          | 55 +++++++++++++++++++++
 doc/mkdocs/docs/home/exceptions.md          | 18 +++++++
 doc/mkdocs/mkdocs.yml                       |  1 +
 4 files changed, 111 insertions(+)
 create mode 100644 doc/mkdocs/docs/features/macros.md

diff --git a/doc/mkdocs/docs/features/arbitrary_types.md b/doc/mkdocs/docs/features/arbitrary_types.md
index 3d238317..7bd7adf7 100644
--- a/doc/mkdocs/docs/features/arbitrary_types.md
+++ b/doc/mkdocs/docs/features/arbitrary_types.md
@@ -81,6 +81,43 @@ Some important things:
 * You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these.
 
 
+## Simplify your life with macros
+
+If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate.
+
+There are two macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object:
+
+- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the namespace of the class/struct to create code for.
+- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the class/struct to create code for. This macro can also access private members.
+
+In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members.
+
+??? example
+
+    The `to_json`/`from_json` functions for the `person` struct above can be created with:
+    
+    ```cpp
+    namespace ns {
+        NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age)
+    }
+    ```
+    
+    Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed:
+    
+    ```cpp
+    namespace ns {
+        class address {
+          private:
+            std::string street;
+            int housenumber;
+            int postcode;
+            
+          public:
+            NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode)
+        };
+    }
+    ```
+
 ## How do I convert third-party types?
 
 This requires a bit more advanced technique. But first, let's see how this conversion mechanism works:
diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md
new file mode 100644
index 00000000..7147be7e
--- /dev/null
+++ b/doc/mkdocs/docs/features/macros.md
@@ -0,0 +1,55 @@
+# Supported Macros
+
+Some aspects of the library can be configured by defining preprocessor macros before including the `json.hpp` header.
+
+## `JSON_CATCH_USER(exception)`
+
+This macro overrides `#!cpp catch` calls inside the library. The argument is the type of the exception to catch. As of version 3.8.0, the library only catches `std::out_of_range` exceptions internally to rethrow them as [`json::out_of_range`](../home/exceptions.md#out-of-range) exceptions. The macro is always followed by a scope.
+
+See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example.
+
+## `JSON_NOEXCEPTION`
+
+Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`.
+When defining `JSON_NOEXCEPTION`, `#!cpp try` is replaced by `#!cpp if (true)`, 
+`#!cpp catch` is replaced by `#!cpp if (false)`, and `#!cpp throw` is replaced by `#!cpp std::abort()`.
+
+The same effect is achieved by setting the compiler flag `-fno-exceptions`.
+
+## `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`
+
+When defined, the library will not create a compile error when a known unsupported compiler is detected. This allows to use the library with compilers that do not fully support C++11 and may only work if unsupported features are not used.
+
+## `JSON_THROW_USER(exception)`
+
+This macro overrides `#!cpp throw` calls inside the library. The argument is the exception to be thrown. Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior.
+
+See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example.
+
+## `JSON_TRY_USER`
+
+This macro overrides `#!cpp try` calls inside the library. It has no arguments and is always followed by a scope.
+
+See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example.
+
+## `NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)`
+
+This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object.
+
+The macro is to be defined inside of the class/struct to create code for. Unlike [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](#nlohmann_define_type_non_intrusivetype-member), it can access private members.
+The first parameter is the name of the class/struct, and all remaining parameters name the members.
+
+See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example.
+
+## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)`
+
+This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object.
+
+The macro is to be defined inside of the namespace of the class/struct to create code for. Private members cannot be accessed. Use [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](#nlohmann_define_type_intrusivetype-member) in these scenarios.
+The first parameter is the name of the class/struct, and all remaining parameters name the members.
+
+See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example.
+
+## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)`
+
+This macro simplifies the serialization/deserialization of enum types. See [Specializing enum conversion](enum_conversion.md) for more information.
diff --git a/doc/mkdocs/docs/home/exceptions.md b/doc/mkdocs/docs/home/exceptions.md
index 92dfb43b..d7430ccc 100644
--- a/doc/mkdocs/docs/home/exceptions.md
+++ b/doc/mkdocs/docs/home/exceptions.md
@@ -32,6 +32,24 @@ Exceptions are used widely within the library. They can, however, be switched of
 
 Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior.
 
+??? example
+
+    The code below switches off exceptions and creates a log entry with a detailed error message in case of errors.
+
+    ```cpp
+    #include <iostream>
+    
+    #define JSON_TRY_USER if(true)
+    #define JSON_CATCH_USER(exception) if(false)
+    #define JSON_THROW_USER(exception)                           \
+        {std::clog << "Error in " << __FILE__ << ":" << __LINE__ \
+                   << " (function " << __FUNCTION__ << ") - "    \
+                   << (exception).what() << std::endl;           \
+         std::abort();}
+    
+    #include <nlohmann/json.hpp>
+    ```
+
 ## Parse errors
 
 This exception is thrown by the library when a parse error occurs. Parse errors
diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml
index 5d6e3b4e..9169e4f1 100644
--- a/doc/mkdocs/mkdocs.yml
+++ b/doc/mkdocs/mkdocs.yml
@@ -49,6 +49,7 @@ nav:
     - features/json_patch.md
     - features/merge_patch.md
     - features/enum_conversion.md
+    - features/macros.md
     - Parsing:
       - features/parsing/index.md
       - features/parsing/parse_exceptions.md

From cbafed349493dd8b072b5c1c5ac7f7c0ef0ed7df Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Fri, 10 Jul 2020 12:53:48 +0200
Subject: [PATCH 18/18] :arrow_down: require CMake 3.1

---
 CMakeLists.txt         | 2 +-
 cmake/pkg-config.pc.in | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 372a7a03..5df3f99f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1)
 ## PROJECT
 ## name and version
 ##
-project(nlohmann_json VERSION 3.8.0 DESCRIPTION "JSON for Modern C++" LANGUAGES CXX)
+project(nlohmann_json VERSION 3.8.0 LANGUAGES CXX)
 
 ##
 ## INCLUDE
diff --git a/cmake/pkg-config.pc.in b/cmake/pkg-config.pc.in
index 680f10c2..3541abf0 100644
--- a/cmake/pkg-config.pc.in
+++ b/cmake/pkg-config.pc.in
@@ -1,4 +1,4 @@
 Name: ${PROJECT_NAME}
-Description: ${PROJECT_DESCRIPTION}
+Description: JSON for Modern C++
 Version: ${PROJECT_VERSION}
 Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}