From faccc37d0d99a30c9de6fc4a68f683482a790638 Mon Sep 17 00:00:00 2001 From: Vitaliy Manushkin Date: Sat, 10 Mar 2018 17:19:28 +0300 Subject: [PATCH 1/7] dump to alternate implementation of string, as defined in basic_json template --- include/nlohmann/detail/output/output_adapters.hpp | 10 +++++----- include/nlohmann/json.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/output/output_adapters.hpp b/include/nlohmann/detail/output/output_adapters.hpp index fe9e1b36..aa6217ec 100644 --- a/include/nlohmann/detail/output/output_adapters.hpp +++ b/include/nlohmann/detail/output/output_adapters.hpp @@ -68,11 +68,11 @@ class output_stream_adapter : public output_adapter_protocol }; /// output adapter for basic_string -template +template> class output_string_adapter : public output_adapter_protocol { public: - explicit output_string_adapter(std::basic_string& s) : str(s) {} + explicit output_string_adapter(StringType& s) : str(s) {} void write_character(CharType c) override { @@ -85,10 +85,10 @@ class output_string_adapter : public output_adapter_protocol } private: - std::basic_string& str; + StringType& str; }; -template +template> class output_adapter { public: @@ -98,7 +98,7 @@ class output_adapter output_adapter(std::basic_ostream& s) : oa(std::make_shared>(s)) {} - output_adapter(std::basic_string& s) + output_adapter(StringType& s) : oa(std::make_shared>(s)) {} operator output_adapter_t() diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 91de782f..a4f2748d 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1947,7 +1947,7 @@ class basic_json const bool ensure_ascii = false) const { string_t result; - serializer s(detail::output_adapter(result), indent_char); + serializer s(detail::output_adapter(result), indent_char); if (indent >= 0) { From ed6b1464f98afd179d0bddb332dc1ef370d14812 Mon Sep 17 00:00:00 2001 From: Vitaliy Manushkin Date: Sat, 10 Mar 2018 17:36:51 +0300 Subject: [PATCH 2/7] dump to alternate implementation of string, as defined in basic_json template (changes to amalgamated code) --- single_include/nlohmann/json.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 1d8e4e82..b37c21c6 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4777,11 +4777,11 @@ class output_stream_adapter : public output_adapter_protocol }; /// output adapter for basic_string -template +template> class output_string_adapter : public output_adapter_protocol { public: - explicit output_string_adapter(std::basic_string& s) : str(s) {} + explicit output_string_adapter(StringType& s) : str(s) {} void write_character(CharType c) override { @@ -4794,10 +4794,10 @@ class output_string_adapter : public output_adapter_protocol } private: - std::basic_string& str; + StringType& str; }; -template +template> class output_adapter { public: @@ -4807,7 +4807,7 @@ class output_adapter output_adapter(std::basic_ostream& s) : oa(std::make_shared>(s)) {} - output_adapter(std::basic_string& s) + output_adapter(StringType& s) : oa(std::make_shared>(s)) {} operator output_adapter_t() @@ -11556,7 +11556,7 @@ class basic_json const bool ensure_ascii = false) const { string_t result; - serializer s(detail::output_adapter(result), indent_char); + serializer s(detail::output_adapter(result), indent_char); if (indent >= 0) { From 830f3e5290bf0ab2cda15ea45a4e036c9f7d4919 Mon Sep 17 00:00:00 2001 From: Vitaliy Manushkin Date: Sat, 10 Mar 2018 23:58:16 +0300 Subject: [PATCH 3/7] forward alternative string class from output_adapter to output_string_adapter --- include/nlohmann/detail/output/output_adapters.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/output/output_adapters.hpp b/include/nlohmann/detail/output/output_adapters.hpp index aa6217ec..ff86a6e1 100644 --- a/include/nlohmann/detail/output/output_adapters.hpp +++ b/include/nlohmann/detail/output/output_adapters.hpp @@ -99,7 +99,7 @@ class output_adapter : oa(std::make_shared>(s)) {} output_adapter(StringType& s) - : oa(std::make_shared>(s)) {} + : oa(std::make_shared>(s)) {} operator output_adapter_t() { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index b37c21c6..4713d4da 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4808,7 +4808,7 @@ class output_adapter : oa(std::make_shared>(s)) {} output_adapter(StringType& s) - : oa(std::make_shared>(s)) {} + : oa(std::make_shared>(s)) {} operator output_adapter_t() { From 51349537fc4be2106e450a03fa0c8c09e5d3fa6c Mon Sep 17 00:00:00 2001 From: Vitaliy Manushkin Date: Sat, 10 Mar 2018 23:59:10 +0300 Subject: [PATCH 4/7] add unit test: checking dump to alternative string type --- test/src/unit-alt-string.cpp | 165 +++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 test/src/unit-alt-string.cpp diff --git a/test/src/unit-alt-string.cpp b/test/src/unit-alt-string.cpp new file mode 100644 index 00000000..f458aab2 --- /dev/null +++ b/test/src/unit-alt-string.cpp @@ -0,0 +1,165 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.1.1 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2018 Vitaliy Manushkin . + +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 "catch.hpp" + +#include +#include +#include + +/* + * This is virtually a string class. + * It covers std::string under the hood. + */ +class alt_string +{ + public: + using value_type = std::string::value_type; + + alt_string(const char* str): str_impl(str) {} + alt_string(const char* str, size_t count): str_impl(str, count) {} + alt_string(size_t count, char chr): str_impl(count, chr) {} + alt_string() = default; + + template + alt_string& append(TParams&& ...params) + { + str_impl.append(std::forward(params)...); + return *this; + } + + void push_back(char c) + { + str_impl.push_back(c); + } + + template + bool operator==(op_type&& op) const + { + return str_impl == op; + } + + template + bool operator!=(op_type&& op) const + { + return str_impl != op; + } + + size_t size() const noexcept + { + return str_impl.size(); + } + + void resize (size_t n) + { + str_impl.resize(n); + } + + void resize (size_t n, char c) + { + str_impl.resize(n, c); + } + + template + bool operator<(op_type&& op) const + { + return str_impl < op; + } + + bool operator<(const alt_string& op) const + { + return str_impl < op.str_impl; + } + + const char* c_str() const + { + return str_impl.c_str(); + } + + char& operator[](int index) + { + return str_impl[index]; + } + + const char& operator[](int index) const + { + return str_impl[index]; + } + + char& back() + { + return str_impl.back(); + } + + const char& back() const + { + return str_impl.back(); + } + + private: + std::string str_impl; +}; + + +using alt_json = nlohmann::basic_json < + std::map, + std::vector, + alt_string, + bool, + std::int64_t, + std::uint64_t, + double, + std::allocator, + nlohmann::adl_serializer >; + + + +TEST_CASE("alternative string type") +{ + SECTION("dump") + { + alt_json doc; + doc["pi"] = 3.141; + doc["happy"] = true; + doc["name"] = "I'm Batman"; + doc["nothing"] = nullptr; + doc["answer"]["everything"] = 42; + doc["list"] = { 1, 0, 2 }; + doc["object"] = { {"currency", "USD"}, {"value", 42.99} }; + + alt_string dump = doc.dump(); + + const char* const expect_str = + R"({"answer":{"everything":42},)" + R"("happy":true,"list":[1,0,2],)" + R"("name":"I'm Batman","nothing":null,)" + R"("object":{"currency":"USD","value":42.99},)" + R"("pi":3.141})"; + CHECK(dump == expect_str); + } +} From 392c03380503b8cd9679ba1a6374df69f346b549 Mon Sep 17 00:00:00 2001 From: Vitaliy Manushkin Date: Sun, 11 Mar 2018 00:29:04 +0300 Subject: [PATCH 5/7] test refactoring --- test/src/unit-alt-string.cpp | 62 ++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/test/src/unit-alt-string.cpp b/test/src/unit-alt-string.cpp index f458aab2..3f8b8c98 100644 --- a/test/src/unit-alt-string.cpp +++ b/test/src/unit-alt-string.cpp @@ -143,23 +143,53 @@ TEST_CASE("alternative string type") { SECTION("dump") { - alt_json doc; - doc["pi"] = 3.141; - doc["happy"] = true; - doc["name"] = "I'm Batman"; - doc["nothing"] = nullptr; - doc["answer"]["everything"] = 42; - doc["list"] = { 1, 0, 2 }; - doc["object"] = { {"currency", "USD"}, {"value", 42.99} }; + { + alt_json doc; + doc["pi"] = 3.141; + alt_string dump = doc.dump(); + CHECK(dump == R"({"pi":3.141})"); + } - alt_string dump = doc.dump(); + { + alt_json doc; + doc["happy"] = true; + alt_string dump = doc.dump(); + CHECK(dump == R"({"happy":true})"); + } - const char* const expect_str = - R"({"answer":{"everything":42},)" - R"("happy":true,"list":[1,0,2],)" - R"("name":"I'm Batman","nothing":null,)" - R"("object":{"currency":"USD","value":42.99},)" - R"("pi":3.141})"; - CHECK(dump == expect_str); + { + alt_json doc; + doc["name"] = "I'm Batman"; + alt_string dump = doc.dump(); + CHECK(dump == R"({"name":"I'm Batman"})"); + } + + { + alt_json doc; + doc["nothing"] = nullptr; + alt_string dump = doc.dump(); + CHECK(dump == R"({"nothing":null})"); + } + + { + alt_json doc; + doc["answer"]["everything"] = 42; + alt_string dump = doc.dump(); + CHECK(dump == R"({"answer":{"everything":42}})"); + } + + { + alt_json doc; + doc["list"] = { 1, 0, 2 }; + alt_string dump = doc.dump(); + CHECK(dump == R"({"list":[1,0,2]})"); + } + + { + alt_json doc; + doc["list"] = { 1, 0, 2 }; + alt_string dump = doc.dump(); + CHECK(dump == R"({"list":[1,0,2]})"); + } } } From b56ac864712948f98043ccd4d21d06ec00e9d6eb Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 12 Mar 2018 18:44:19 +0100 Subject: [PATCH 6/7] :memo: thanks for #1006 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1d065407..8c6eca8f 100644 --- a/README.md +++ b/README.md @@ -975,6 +975,7 @@ I deeply appreciate the help of the following people. - [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager. - [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise. - [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback. +- [agrianius](https://github.com/agrianius) allowed to dump JSON to an alternative string type Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone. From 8557151d903ec10f57d8f220708eb9960b3252ab Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 12 Mar 2018 19:15:11 +0100 Subject: [PATCH 7/7] :recycle: adjusting lexer/parser in symmetry to #1006 --- include/nlohmann/detail/input/lexer.hpp | 5 ++-- include/nlohmann/detail/input/parser.hpp | 3 ++- single_include/nlohmann/json.hpp | 8 ++++--- test/Makefile | 1 + test/src/unit-alt-string.cpp | 29 +++++++++++++++++++----- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index ea116093..98cc1b69 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -32,6 +32,7 @@ class lexer using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; public: /// token types for the parser @@ -1130,7 +1131,7 @@ scan_number_done: } /// return current string value (implicitly resets the token; useful only once) - std::string&& move_string() + string_t&& move_string() { return std::move(token_buffer); } @@ -1260,7 +1261,7 @@ scan_number_done: std::vector token_string {}; /// buffer for variable-length tokens (numbers, strings) - std::string token_buffer {}; + string_t token_buffer {}; /// a description of occurred lexer errors const char* error_message = ""; diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 58d42bbe..e58aaaf9 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -32,6 +32,7 @@ class parser using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; using lexer_t = lexer; using token_type = typename lexer_t::token_type; @@ -175,7 +176,7 @@ class parser } // parse values - std::string key; + string_t key; BasicJsonType value; while (true) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4713d4da..436ea89b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -1871,6 +1871,7 @@ class lexer using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; public: /// token types for the parser @@ -2969,7 +2970,7 @@ scan_number_done: } /// return current string value (implicitly resets the token; useful only once) - std::string&& move_string() + string_t&& move_string() { return std::move(token_buffer); } @@ -3099,7 +3100,7 @@ scan_number_done: std::vector token_string {}; /// buffer for variable-length tokens (numbers, strings) - std::string token_buffer {}; + string_t token_buffer {}; /// a description of occurred lexer errors const char* error_message = ""; @@ -3155,6 +3156,7 @@ class parser using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; using lexer_t = lexer; using token_type = typename lexer_t::token_type; @@ -3298,7 +3300,7 @@ class parser } // parse values - std::string key; + string_t key; BasicJsonType value; while (true) { diff --git a/test/Makefile b/test/Makefile index 51edae66..f373451f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -9,6 +9,7 @@ CPPFLAGS += -I ../single_include -I . -I thirdparty/catch -I thirdparty/fifo_map SOURCES = src/unit.cpp \ src/unit-algorithms.cpp \ src/unit-allocator.cpp \ + src/unit-alt-string.cpp \ src/unit-capacity.cpp \ src/unit-cbor.cpp \ src/unit-class_const_iterator.cpp \ diff --git a/test/src/unit-alt-string.cpp b/test/src/unit-alt-string.cpp index 3f8b8c98..30159fc3 100644 --- a/test/src/unit-alt-string.cpp +++ b/test/src/unit-alt-string.cpp @@ -42,7 +42,7 @@ class alt_string using value_type = std::string::value_type; alt_string(const char* str): str_impl(str) {} - alt_string(const char* str, size_t count): str_impl(str, count) {} + alt_string(const char* str, std::size_t count): str_impl(str, count) {} alt_string(size_t count, char chr): str_impl(count, chr) {} alt_string() = default; @@ -70,17 +70,17 @@ class alt_string return str_impl != op; } - size_t size() const noexcept + std::size_t size() const noexcept { return str_impl.size(); } - void resize (size_t n) + void resize (std::size_t n) { str_impl.resize(n); } - void resize (size_t n, char c) + void resize (std::size_t n, char c) { str_impl.resize(n, c); } @@ -101,12 +101,12 @@ class alt_string return str_impl.c_str(); } - char& operator[](int index) + char& operator[](std::size_t index) { return str_impl[index]; } - const char& operator[](int index) const + const char& operator[](std::size_t index) const { return str_impl[index]; } @@ -121,6 +121,16 @@ class alt_string return str_impl.back(); } + void clear() + { + str_impl.clear(); + } + + const value_type* data() + { + return str_impl.data(); + } + private: std::string str_impl; }; @@ -192,4 +202,11 @@ TEST_CASE("alternative string type") CHECK(dump == R"({"list":[1,0,2]})"); } } + + SECTION("parse") + { + auto doc = alt_json::parse("{\"foo\": \"bar\"}"); + alt_string dump = doc.dump(); + CHECK(dump == R"({"foo":"bar"})"); + } }