changeset 242:d607ae858682

Update json11 code
author Chris Cannam <cannam@all-day-breakfast.com>
date Tue, 13 Jun 2017 17:16:03 +0100
parents fdab0a246298
children 498f899a91b6
files ext/json11/CMakeLists.txt ext/json11/Makefile ext/json11/json11.cpp ext/json11/json11.hpp ext/json11/json11.pc.in ext/json11/test.cpp
diffstat 6 files changed, 362 insertions(+), 193 deletions(-) [+]
line wrap: on
line diff
--- a/ext/json11/CMakeLists.txt	Tue Jun 13 16:24:24 2017 +0100
+++ b/ext/json11/CMakeLists.txt	Tue Jun 13 17:16:03 2017 +0100
@@ -1,21 +1,57 @@
-project(json11)
-
 cmake_minimum_required(VERSION 2.8)
+if (CMAKE_VERSION VERSION_LESS "3")
+  project(json11 CXX)
+else()
+  cmake_policy(SET CMP0048 NEW)
+  project(json11 VERSION 1.0.0 LANGUAGES CXX)
+endif()
 
 enable_testing()
 
-add_definitions(
-    -std=c++11
-    -fno-rtti
-    -fno-exceptions
-    -Wall
-    -Wextra
-    -Werror)
+option(JSON11_BUILD_TESTS "Build unit tests" OFF)
+option(JSON11_ENABLE_DR1467_CANARY "Enable canary test for DR 1467" OFF)
 
-set(json11_SRCS json11.cpp)
+if(CMAKE_VERSION VERSION_LESS "3")
+  add_definitions(-std=c++11)
+else()
+  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD_REQUIRED ON)
+endif()
 
-add_library(json11 STATIC ${json11_SRCS})
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+  set(CMAKE_INSTALL_PREFIX /usr)
+endif()
 
-add_test(json11_test json11_test)
+add_library(json11 json11.cpp)
+target_include_directories(json11 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_compile_options(json11
+  PRIVATE -fPIC -fno-rtti -fno-exceptions -Wall)
 
-add_executable(json11_test ${json11_SRCS} test.cpp)
+# Set warning flags, which may vary per platform
+include(CheckCXXCompilerFlag)
+set(_possible_warnings_flags /W4 /WX -Wextra -Werror)
+foreach(_warning_flag in ${_possible_warnings_flags})
+  CHECK_CXX_COMPILER_FLAG(_warning_flag _flag_supported)
+  if(${_flag_supported})
+    target_compile_options(json11 PRIVATE ${_warning_flag})
+  endif()
+endforeach()
+
+configure_file("json11.pc.in" "json11.pc" @ONLY)
+
+if (JSON11_BUILD_TESTS)
+
+  # enable test for DR1467, described here: https://llvm.org/bugs/show_bug.cgi?id=23812
+  if(JSON11_ENABLE_DR1467_CANARY)
+    add_definitions(-D JSON11_ENABLE_DR1467_CANARY=1)
+  else()
+    add_definitions(-D JSON11_ENABLE_DR1467_CANARY=0)
+  endif()
+
+  add_executable(json11_test test.cpp)
+  target_link_libraries(json11_test json11)
+endif()
+
+install(TARGETS json11 DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE})
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/json11.hpp" DESTINATION include/${CMAKE_LIBRARY_ARCHITECTURE})
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/json11.pc" DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig)
--- a/ext/json11/Makefile	Tue Jun 13 16:24:24 2017 +0100
+++ b/ext/json11/Makefile	Tue Jun 13 17:16:03 2017 +0100
@@ -1,5 +1,13 @@
+# Environment variable to enable or disable code which demonstrates the behavior change
+# in Xcode 7 / Clang 3.7, introduced by DR1467 and described here:
+# https://llvm.org/bugs/show_bug.cgi?id=23812
+# Defaults to on in order to act as a warning to anyone who's unaware of the issue.
+ifneq ($(JSON11_ENABLE_DR1467_CANARY),)
+CANARY_ARGS = -DJSON11_ENABLE_DR1467_CANARY=$(JSON11_ENABLE_DR1467_CANARY)
+endif
+
 test: json11.cpp json11.hpp test.cpp
-	$(CXX) -O -std=c++11 json11.cpp test.cpp -o test -fno-rtti -fno-exceptions
+	$(CXX) $(CANARY_ARGS) -O -std=c++11 json11.cpp test.cpp -o test -fno-rtti -fno-exceptions
 
 clean:
 	if [ -e test ]; then rm test; fi
--- a/ext/json11/json11.cpp	Tue Jun 13 16:24:24 2017 +0100
+++ b/ext/json11/json11.cpp	Tue Jun 13 17:16:03 2017 +0100
@@ -37,11 +37,20 @@
 using std::initializer_list;
 using std::move;
 
+/* Helper for representing null - just a do-nothing struct, plus comparison
+ * operators so the helpers in JsonValue work. We can't use nullptr_t because
+ * it may not be orderable.
+ */
+struct NullStruct {
+    bool operator==(NullStruct) const { return true; }
+    bool operator<(NullStruct) const { return false; }
+};
+
 /* * * * * * * * * * * * * * * * * * * *
  * Serialization
  */
 
-static void dump(std::nullptr_t, string &out) {
+static void dump(NullStruct, string &out) {
     out += "null";
 }
 
@@ -208,9 +217,9 @@
     explicit JsonObject(Json::object &&value)      : Value(move(value)) {}
 };
 
-class JsonNull final : public Value<Json::NUL, std::nullptr_t> {
+class JsonNull final : public Value<Json::NUL, NullStruct> {
 public:
-    JsonNull() : Value(nullptr) {}
+    JsonNull() : Value({}) {}
 };
 
 /* * * * * * * * * * * * * * * * * * * *
@@ -291,6 +300,8 @@
  */
 
 bool Json::operator== (const Json &other) const {
+    if (m_ptr == other.m_ptr)
+        return true;
     if (m_ptr->type() != other.m_ptr->type())
         return false;
 
@@ -298,6 +309,8 @@
 }
 
 bool Json::operator< (const Json &other) const {
+    if (m_ptr == other.m_ptr)
+        return false;
     if (m_ptr->type() != other.m_ptr->type())
         return m_ptr->type() < other.m_ptr->type();
 
@@ -326,11 +339,12 @@
     return (x >= lower && x <= upper);
 }
 
+namespace {
 /* JsonParser
  *
  * Object that tracks all state of an in-progress parse.
  */
-struct JsonParser {
+struct JsonParser final {
 
     /* State
      */
@@ -374,38 +388,31 @@
       if (str[i] == '/') {
         i++;
         if (i == str.size())
-          return fail("unexpected end of input inside comment", 0);
+          return fail("unexpected end of input after start of comment", false);
         if (str[i] == '/') { // inline comment
           i++;
-          if (i == str.size())
-            return fail("unexpected end of input inside inline comment", 0);
-          // advance until next line
-          while (str[i] != '\n') {
+          // advance until next line, or end of input
+          while (i < str.size() && str[i] != '\n') {
             i++;
-            if (i == str.size())
-              return fail("unexpected end of input inside inline comment", 0);
           }
           comment_found = true;
         }
         else if (str[i] == '*') { // multiline comment
           i++;
           if (i > str.size()-2)
-            return fail("unexpected end of input inside multi-line comment", 0);
+            return fail("unexpected end of input inside multi-line comment", false);
           // advance until closing tokens
           while (!(str[i] == '*' && str[i+1] == '/')) {
             i++;
             if (i > str.size()-2)
               return fail(
-                "unexpected end of input inside multi-line comment", 0);
+                "unexpected end of input inside multi-line comment", false);
           }
           i += 2;
-          if (i == str.size())
-            return fail(
-              "unexpected end of input inside multi-line comment", 0);
           comment_found = true;
         }
         else
-          return fail("malformed comment", 0);
+          return fail("malformed comment", false);
       }
       return comment_found;
     }
@@ -420,6 +427,7 @@
         bool comment_found = false;
         do {
           comment_found = consume_comment();
+          if (failed) return;
           consume_whitespace();
         }
         while(comment_found);
@@ -433,8 +441,9 @@
      */
     char get_next_token() {
         consume_garbage();
+        if (failed) return (char)0;
         if (i == str.size())
-            return fail("unexpected end of input", 0);
+            return fail("unexpected end of input", (char)0);
 
         return str[i++];
     }
@@ -508,7 +517,7 @@
                 if (esc.length() < 4) {
                     return fail("bad \\u escape: " + esc, "");
                 }
-                for (int j = 0; j < 4; j++) {
+                for (size_t j = 0; j < 4; j++) {
                     if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
                             && !in_range(esc[j], '0', '9'))
                         return fail("bad \\u escape: " + esc, "");
@@ -718,6 +727,7 @@
         return fail("expected value, got " + esc(ch));
     }
 };
+}//namespace {
 
 Json Json::parse(const string &in, string &err, JsonParse strategy) {
     JsonParser parser { in, 0, err, false, strategy };
@@ -725,6 +735,8 @@
 
     // Check for any trailing garbage
     parser.consume_garbage();
+    if (parser.failed)
+        return Json();
     if (parser.i != in.size())
         return parser.fail("unexpected trailing " + esc(in[parser.i]));
 
@@ -733,15 +745,22 @@
 
 // Documented in json11.hpp
 vector<Json> Json::parse_multi(const string &in,
+                               std::string::size_type &parser_stop_pos,
                                string &err,
                                JsonParse strategy) {
     JsonParser parser { in, 0, err, false, strategy };
-
+    parser_stop_pos = 0;
     vector<Json> json_vec;
     while (parser.i != in.size() && !parser.failed) {
         json_vec.push_back(parser.parse_json(0));
+        if (parser.failed)
+            break;
+
         // Check for another object
         parser.consume_garbage();
+        if (parser.failed)
+            break;
+        parser_stop_pos = parser.i;
     }
     return json_vec;
 }
--- a/ext/json11/json11.hpp	Tue Jun 13 16:24:24 2017 +0100
+++ b/ext/json11/json11.hpp	Tue Jun 13 17:16:03 2017 +0100
@@ -56,6 +56,18 @@
 #include <memory>
 #include <initializer_list>
 
+#ifdef _MSC_VER
+    #if _MSC_VER <= 1800 // VS 2013
+        #ifndef noexcept
+            #define noexcept throw()
+        #endif
+
+        #ifndef snprintf
+            #define snprintf _snprintf_s
+        #endif
+    #endif
+#endif
+
 namespace json11 {
 
 enum JsonParse {
@@ -165,9 +177,18 @@
     // Parse multiple objects, concatenated or separated by whitespace
     static std::vector<Json> parse_multi(
         const std::string & in,
+        std::string::size_type & parser_stop_pos,
         std::string & err,
         JsonParse strategy = JsonParse::STANDARD);
 
+    static inline std::vector<Json> parse_multi(
+        const std::string & in,
+        std::string & err,
+        JsonParse strategy = JsonParse::STANDARD) {
+        std::string::size_type parser_stop_pos;
+        return parse_multi(in, parser_stop_pos, err, strategy);
+    }
+
     bool operator== (const Json &rhs) const;
     bool operator<  (const Json &rhs) const;
     bool operator!= (const Json &rhs) const { return !(*this == rhs); }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/json11/json11.pc.in	Tue Jun 13 17:16:03 2017 +0100
@@ -0,0 +1,9 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+libdir=${prefix}/lib/@CMAKE_LIBRARY_ARCHITECTURE@
+includedir=${prefix}/include/@CMAKE_LIBRARY_ARCHITECTURE@
+
+Name: @PROJECT_NAME@
+Description: json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
+Version: @PROJECT_VERSION@
+Libs: -L${libdir} -ljson11
+Cflags: -I${includedir}
--- a/ext/json11/test.cpp	Tue Jun 13 16:24:24 2017 +0100
+++ b/ext/json11/test.cpp	Tue Jun 13 17:16:03 2017 +0100
@@ -1,19 +1,55 @@
+/*
+ * Define JSON11_TEST_CUSTOM_CONFIG to 1 if you want to build this tester into
+ * your own unit-test framework rather than a stand-alone program.  By setting
+ * The values of the variables included below, you can insert your own custom
+ * code into this file as it builds, in order to make it into a test case for
+ * your favorite framework.
+ */
+#if !JSON11_TEST_CUSTOM_CONFIG
+#define JSON11_TEST_CPP_PREFIX_CODE
+#define JSON11_TEST_CPP_SUFFIX_CODE
+#define JSON11_TEST_STANDALONE_MAIN 1
+#define JSON11_TEST_CASE(name) static void name()
+#define JSON11_TEST_ASSERT(b) assert(b)
+#ifdef NDEBUG
+#undef NDEBUG//at now assert will work even in Release build
+#endif
+#endif // JSON11_TEST_CUSTOM_CONFIG
+
+/*
+ * Enable or disable code which demonstrates the behavior change in Xcode 7 / Clang 3.7,
+ * introduced by DR1467 and described here: https://github.com/dropbox/json11/issues/86
+ * Defaults to off since it doesn't appear the standards committee is likely to act
+ * on this, so it needs to be considered normal behavior.
+ */
+#ifndef JSON11_ENABLE_DR1467_CANARY
+#define JSON11_ENABLE_DR1467_CANARY 0
+#endif
+
+/*
+ * Beginning of standard source file, which makes use of the customizations above.
+ */
+#include <cassert>
 #include <string>
 #include <cstdio>
 #include <cstring>
 #include <iostream>
 #include <sstream>
 #include "json11.hpp"
-#include <cassert>
 #include <list>
 #include <set>
 #include <unordered_map>
+#include <algorithm>
+#include <type_traits>
+
+// Insert user-defined prefix code (includes, function declarations, etc)
+// to set up a custom test suite
+JSON11_TEST_CPP_PREFIX_CODE
 
 using namespace json11;
 using std::string;
 
 // Check that Json has the properties we want.
-#include <type_traits>
 #define CHECK_TRAIT(x) static_assert(std::x::value, #x)
 CHECK_TRAIT(is_nothrow_constructible<Json>);
 CHECK_TRAIT(is_nothrow_default_constructible<Json>);
@@ -23,7 +59,197 @@
 CHECK_TRAIT(is_nothrow_move_assignable<Json>);
 CHECK_TRAIT(is_nothrow_destructible<Json>);
 
-void parse_from_stdin() {
+JSON11_TEST_CASE(json11_test) {
+    const string simple_test =
+        R"({"k1":"v1", "k2":42, "k3":["a",123,true,false,null]})";
+
+    string err;
+    const auto json = Json::parse(simple_test, err);
+
+    std::cout << "k1: " << json["k1"].string_value() << "\n";
+    std::cout << "k3: " << json["k3"].dump() << "\n";
+
+    for (auto &k : json["k3"].array_items()) {
+        std::cout << "    - " << k.dump() << "\n";
+    }
+
+    string comment_test = R"({
+      // comment /* with nested comment */
+      "a": 1,
+      // comment
+      // continued
+      "b": "text",
+      /* multi
+         line
+         comment
+        // line-comment-inside-multiline-comment
+      */
+      // and single-line comment
+      // and single-line comment /* multiline inside single line */
+      "c": [1, 2, 3]
+      // and single-line comment at end of object
+    })";
+
+    string err_comment;
+    auto json_comment = Json::parse(
+      comment_test, err_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(!json_comment.is_null());
+    JSON11_TEST_ASSERT(err_comment.empty());
+
+    comment_test = "{\"a\": 1}//trailing line comment";
+    json_comment = Json::parse(
+      comment_test, err_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(!json_comment.is_null());
+    JSON11_TEST_ASSERT(err_comment.empty());
+
+    comment_test = "{\"a\": 1}/*trailing multi-line comment*/";
+    json_comment = Json::parse(
+      comment_test, err_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(!json_comment.is_null());
+    JSON11_TEST_ASSERT(err_comment.empty());
+
+    string failing_comment_test = "{\n/* unterminated comment\n\"a\": 1,\n}";
+    string err_failing_comment;
+    auto json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(json_failing_comment.is_null());
+    JSON11_TEST_ASSERT(!err_failing_comment.empty());
+
+    failing_comment_test = "{\n/* unterminated trailing comment }";
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(json_failing_comment.is_null());
+    JSON11_TEST_ASSERT(!err_failing_comment.empty());
+
+    failing_comment_test = "{\n/ / bad comment }";
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(json_failing_comment.is_null());
+    JSON11_TEST_ASSERT(!err_failing_comment.empty());
+
+    failing_comment_test = "{// bad comment }";
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(json_failing_comment.is_null());
+    JSON11_TEST_ASSERT(!err_failing_comment.empty());
+
+    failing_comment_test = "{\n\"a\": 1\n}/";
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(json_failing_comment.is_null());
+    JSON11_TEST_ASSERT(!err_failing_comment.empty());
+
+    failing_comment_test = "{/* bad\ncomment *}";
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    JSON11_TEST_ASSERT(json_failing_comment.is_null());
+    JSON11_TEST_ASSERT(!err_failing_comment.empty());
+
+    std::list<int> l1 { 1, 2, 3 };
+    std::vector<int> l2 { 1, 2, 3 };
+    std::set<int> l3 { 1, 2, 3 };
+    JSON11_TEST_ASSERT(Json(l1) == Json(l2));
+    JSON11_TEST_ASSERT(Json(l2) == Json(l3));
+
+    std::map<string, string> m1 { { "k1", "v1" }, { "k2", "v2" } };
+    std::unordered_map<string, string> m2 { { "k1", "v1" }, { "k2", "v2" } };
+    JSON11_TEST_ASSERT(Json(m1) == Json(m2));
+
+    // Json literals
+    const Json obj = Json::object({
+        { "k1", "v1" },
+        { "k2", 42.0 },
+        { "k3", Json::array({ "a", 123.0, true, false, nullptr }) },
+    });
+
+    std::cout << "obj: " << obj.dump() << "\n";
+    JSON11_TEST_ASSERT(obj.dump() == "{\"k1\": \"v1\", \"k2\": 42, \"k3\": [\"a\", 123, true, false, null]}");
+
+    JSON11_TEST_ASSERT(Json("a").number_value() == 0);
+    JSON11_TEST_ASSERT(Json("a").string_value() == "a");
+    JSON11_TEST_ASSERT(Json().number_value() == 0);
+
+    JSON11_TEST_ASSERT(obj == json);
+    JSON11_TEST_ASSERT(Json(42) == Json(42.0));
+    JSON11_TEST_ASSERT(Json(42) != Json(42.1));
+
+    const string unicode_escape_test =
+        R"([ "blah\ud83d\udca9blah\ud83dblah\udca9blah\u0000blah\u1234" ])";
+
+    const char utf8[] = "blah" "\xf0\x9f\x92\xa9" "blah" "\xed\xa0\xbd" "blah"
+                        "\xed\xb2\xa9" "blah" "\0" "blah" "\xe1\x88\xb4";
+
+    Json uni = Json::parse(unicode_escape_test, err);
+    JSON11_TEST_ASSERT(uni[0].string_value().size() == (sizeof utf8) - 1);
+    JSON11_TEST_ASSERT(std::memcmp(uni[0].string_value().data(), utf8, sizeof utf8) == 0);
+
+    // Demonstrates the behavior change in Xcode 7 / Clang 3.7, introduced by DR1467
+    // and described here: https://llvm.org/bugs/show_bug.cgi?id=23812
+    if (JSON11_ENABLE_DR1467_CANARY) {
+        Json nested_array = Json::array { Json::array { 1, 2, 3 } };
+        JSON11_TEST_ASSERT(nested_array.is_array());
+        JSON11_TEST_ASSERT(nested_array.array_items().size() == 1);
+        JSON11_TEST_ASSERT(nested_array.array_items()[0].is_array());
+        JSON11_TEST_ASSERT(nested_array.array_items()[0].array_items().size() == 3);
+    }
+
+    {
+        const std::string good_json = R"( {"k1" : "v1"})";
+        const std::string bad_json1 = good_json + " {";
+        const std::string bad_json2 = good_json + R"({"k2":"v2", "k3":[)";
+        struct TestMultiParse {
+            std::string input;
+            std::string::size_type expect_parser_stop_pos;
+            size_t expect_not_empty_elms_count;
+            Json expect_parse_res;
+        } tests[] = {
+            {" {", 0, 0, {}},
+            {good_json, good_json.size(), 1, Json(std::map<string, string>{ { "k1", "v1" } })},
+            {bad_json1, good_json.size() + 1, 1, Json(std::map<string, string>{ { "k1", "v1" } })},
+            {bad_json2, good_json.size(), 1, Json(std::map<string, string>{ { "k1", "v1" } })},
+            {"{}", 2, 1, Json::object{}},
+        };
+        for (const auto &tst : tests) {
+            std::string::size_type parser_stop_pos;
+            std::string err;
+            auto res = Json::parse_multi(tst.input, parser_stop_pos, err);
+            JSON11_TEST_ASSERT(parser_stop_pos == tst.expect_parser_stop_pos);
+            JSON11_TEST_ASSERT(
+                (size_t)std::count_if(res.begin(), res.end(),
+                                      [](const Json& j) { return !j.is_null(); })
+                == tst.expect_not_empty_elms_count);
+            if (!res.empty()) {
+                JSON11_TEST_ASSERT(tst.expect_parse_res == res[0]);
+            }
+        }
+    }
+
+    Json my_json = Json::object {
+        { "key1", "value1" },
+        { "key2", false },
+        { "key3", Json::array { 1, 2, 3 } },
+    };
+    std::string json_obj_str = my_json.dump();
+    std::cout << "json_obj_str: " << json_obj_str << "\n";
+    JSON11_TEST_ASSERT(json_obj_str == "{\"key1\": \"value1\", \"key2\": false, \"key3\": [1, 2, 3]}");
+
+    class Point {
+    public:
+        int x;
+        int y;
+        Point (int x, int y) : x(x), y(y) {}
+        Json to_json() const { return Json::array { x, y }; }
+    };
+
+    std::vector<Point> points = { { 1, 2 }, { 10, 20 }, { 100, 200 } };
+    std::string points_json = Json(points).dump();
+    std::cout << "points_json: " << points_json << "\n";
+    JSON11_TEST_ASSERT(points_json == "[[1, 2], [10, 20], [100, 200]]");
+}
+
+#if JSON11_TEST_STANDALONE_MAIN
+
+static void parse_from_stdin() {
     string buf;
     string line;
     while (std::getline(std::cin, line)) {
@@ -45,161 +271,11 @@
         return 0;
     }
 
-    const string simple_test =
-        R"({"k1":"v1", "k2":42, "k3":["a",123,true,false,null]})";
+    json11_test();
+}
 
-    string err;
-    auto json = Json::parse(simple_test, err);
+#endif // JSON11_TEST_STANDALONE_MAIN
 
-    std::cout << "k1: " << json["k1"].string_value() << "\n";
-    std::cout << "k3: " << json["k3"].dump() << "\n";
-
-    for (auto &k : json["k3"].array_items()) {
-        std::cout << "    - " << k.dump() << "\n";
-    }
-
-    const string comment_test = R"({
-      // comment /* with nested comment */
-      "a": 1,
-      // comment
-      // continued
-      "b": "text",
-      /* multi
-         line
-         comment */
-      // and single-line comment
-      "c": [1, 2, 3]
-    })";
-
-    string err_comment;
-    auto json_comment = Json::parse(
-      comment_test, err_comment, JsonParse::COMMENTS);
-    if (!err_comment.empty()) {
-        printf("Failed: %s\n", err_comment.c_str());
-    } else {
-        printf("Result: %s\n", json_comment.dump().c_str());
-    }
-
-    string failing_comment_test = R"({
-      /* bad comment
-      "a": 1,
-    })";
-
-    string err_failing_comment;
-    auto json_failing_comment = Json::parse(
-      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
-    if (!err_failing_comment.empty()) {
-        printf("Failed: %s\n", err_failing_comment.c_str());
-    } else {
-        printf("Result: %s\n", json_failing_comment.dump().c_str());
-    }
-
-    failing_comment_test = R"({
-      / / bad comment })";
-
-    json_failing_comment = Json::parse(
-      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
-    if (!err_failing_comment.empty()) {
-        printf("Failed: %s\n", err_failing_comment.c_str());
-    } else {
-        printf("Result: %s\n", json_failing_comment.dump().c_str());
-    }
-
-    failing_comment_test = R"({// bad comment })";
-
-    json_failing_comment = Json::parse(
-      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
-    if (!err_failing_comment.empty()) {
-        printf("Failed: %s\n", err_failing_comment.c_str());
-    } else {
-        printf("Result: %s\n", json_failing_comment.dump().c_str());
-    }
-
-    failing_comment_test = R"({
-          "a": 1
-        }/)";
-
-    json_failing_comment = Json::parse(
-      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
-    if (!err_failing_comment.empty()) {
-        printf("Failed: %s\n", err_failing_comment.c_str());
-    } else {
-        printf("Result: %s\n", json_failing_comment.dump().c_str());
-    }
-
-    failing_comment_test = R"({/* bad
-                                  comment *})";
-
-    json_failing_comment = Json::parse(
-      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
-    if (!err_failing_comment.empty()) {
-        printf("Failed: %s\n", err_failing_comment.c_str());
-    } else {
-        printf("Result: %s\n", json_failing_comment.dump().c_str());
-    }
-
-    std::list<int> l1 { 1, 2, 3 };
-    std::vector<int> l2 { 1, 2, 3 };
-    std::set<int> l3 { 1, 2, 3 };
-    assert(Json(l1) == Json(l2));
-    assert(Json(l2) == Json(l3));
-
-    std::map<string, string> m1 { { "k1", "v1" }, { "k2", "v2" } };
-    std::unordered_map<string, string> m2 { { "k1", "v1" }, { "k2", "v2" } };
-    assert(Json(m1) == Json(m2));
-
-    // Json literals
-    Json obj = Json::object({
-        { "k1", "v1" },
-        { "k2", 42.0 },
-        { "k3", Json::array({ "a", 123.0, true, false, nullptr }) },
-    });
-
-    std::cout << "obj: " << obj.dump() << "\n";
-
-    assert(Json("a").number_value() == 0);
-    assert(Json("a").string_value() == "a");
-    assert(Json().number_value() == 0);
-
-    assert(obj == json);
-    assert(Json(42) == Json(42.0));
-    assert(Json(42) != Json(42.1));
-
-    const string unicode_escape_test =
-        R"([ "blah\ud83d\udca9blah\ud83dblah\udca9blah\u0000blah\u1234" ])";
-
-    const char utf8[] = "blah" "\xf0\x9f\x92\xa9" "blah" "\xed\xa0\xbd" "blah"
-                        "\xed\xb2\xa9" "blah" "\0" "blah" "\xe1\x88\xb4";
-
-    Json uni = Json::parse(unicode_escape_test, err);
-    assert(uni[0].string_value().size() == (sizeof utf8) - 1);
-    assert(std::memcmp(uni[0].string_value().data(), utf8, sizeof utf8) == 0);
-
-    // Demonstrates the behavior change in Xcode 7 / Clang 3.7 described
-    // here: https://llvm.org/bugs/show_bug.cgi?id=23812
-    Json nested_array = Json::array { Json::array { 1, 2, 3 } };
-    assert(nested_array.is_array());
-    assert(nested_array.array_items().size() == 1);
-    assert(nested_array.array_items()[0].is_array());
-    assert(nested_array.array_items()[0].array_items().size() == 3);
-
-    Json my_json = Json::object {
-        { "key1", "value1" },
-        { "key2", false },
-        { "key3", Json::array { 1, 2, 3 } },
-    };
-    std::string json_str = my_json.dump();
-    printf("%s\n", json_str.c_str());
-
-    class Point {
-    public:
-        int x;
-        int y;
-        Point (int x, int y) : x(x), y(y) {}
-        Json to_json() const { return Json::array { x, y }; }
-    };
-
-    std::vector<Point> points = { { 1, 2 }, { 10, 20 }, { 100, 200 } };
-    std::string points_json = Json(points).dump();
-    printf("%s\n", points_json.c_str());
-}
+// Insert user-defined suffix code (function definitions, etc)
+// to set up a custom test suite
+JSON11_TEST_CPP_SUFFIX_CODE