From eef80590037f0aa4d1d100ad4495378082954849 Mon Sep 17 00:00:00 2001 From: Niels Date: Sat, 20 Aug 2016 20:29:33 +0200 Subject: [PATCH] allowing parsing from contiguous containers --- src/json.hpp | 51 ++++++++++++----- src/json.hpp.re2c | 51 ++++++++++++----- test/src/unit-deserialization.cpp | 94 ++++++++++++++++++++++--------- 3 files changed, 144 insertions(+), 52 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index f10e5960..8a1c9a77 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5956,11 +5956,13 @@ class basic_json @since version 1.0.0 */ + /* static basic_json parse(const string_t& s, const parser_callback_t cb = nullptr) { return parser(s, cb).parse(); } + */ /*! @brief deserialize from string literal @@ -6012,10 +6014,10 @@ class basic_json } /*! - @brief deserialize from a container with contiguous storage + @brief deserialize from a iterator range with contiguous storage - This function reads from a nonempty iterator range of a container with - contiguous storage of 1-byte values. Compatible container types include + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include `std::vector`, `std::string`, `std::array`, `std::valarray`, and `std::initializer_list`. Furthermore, C-style arrays can be used with `std::begin()`/`std::end()`. User-defined containers can be used as long @@ -6025,9 +6027,7 @@ class basic_json undefined behavior. **This precondition is enforced with an assertion.** @pre Each element in the range has a size of 1 byte. Violating this precondition yields undefined behavior. **This precondition is enforced - with an assertion.** - @pre The iterator range is nonempty. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** + with a static assertion.** @warning There is no way to enforce the preconditions at compile-time. If the function is called with noncompliant iterators, the behavior @@ -6053,7 +6053,9 @@ class basic_json */ template ::iterator_category, std::random_access_iterator_tag>::value + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits::iterator_category>::value , int>::type = 0> static basic_json parse(IteratorType first, IteratorType last, @@ -6069,17 +6071,40 @@ class basic_json }).first); // assertion to check that each element is 1 byte long - assert(std::all_of(first, last, [](decltype(*first) val) - { - return sizeof(val) == 1; - })); + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); - // assertion that the iterator range is not empty - assert(std::distance(first, last) > 0); + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } return parser(first, last, cb).parse(); } + template()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + /*! @brief deserialize from stream diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index b1f5a633..cb51f557 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -5956,11 +5956,13 @@ class basic_json @since version 1.0.0 */ + /* static basic_json parse(const string_t& s, const parser_callback_t cb = nullptr) { return parser(s, cb).parse(); } + */ /*! @brief deserialize from string literal @@ -6012,10 +6014,10 @@ class basic_json } /*! - @brief deserialize from a container with contiguous storage + @brief deserialize from a iterator range with contiguous storage - This function reads from a nonempty iterator range of a container with - contiguous storage of 1-byte values. Compatible container types include + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include `std::vector`, `std::string`, `std::array`, `std::valarray`, and `std::initializer_list`. Furthermore, C-style arrays can be used with `std::begin()`/`std::end()`. User-defined containers can be used as long @@ -6025,9 +6027,7 @@ class basic_json undefined behavior. **This precondition is enforced with an assertion.** @pre Each element in the range has a size of 1 byte. Violating this precondition yields undefined behavior. **This precondition is enforced - with an assertion.** - @pre The iterator range is nonempty. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** + with a static assertion.** @warning There is no way to enforce the preconditions at compile-time. If the function is called with noncompliant iterators, the behavior @@ -6053,7 +6053,9 @@ class basic_json */ template ::iterator_category, std::random_access_iterator_tag>::value + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits::iterator_category>::value , int>::type = 0> static basic_json parse(IteratorType first, IteratorType last, @@ -6069,17 +6071,40 @@ class basic_json }).first); // assertion to check that each element is 1 byte long - assert(std::all_of(first, last, [](decltype(*first) val) - { - return sizeof(val) == 1; - })); + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); - // assertion that the iterator range is not empty - assert(std::distance(first, last) > 0); + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } return parser(first, last, cb).parse(); } + template()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + /*! @brief deserialize from stream diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 965d1d07..b681e3df 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -82,40 +82,82 @@ TEST_CASE("deserialization") SECTION("contiguous containers") { - SECTION("from std::vector") + SECTION("directly") { - std::vector v = {'t', 'r', 'u', 'e', '\0'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + SECTION("from std::vector") + { + std::vector v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(v) == json(true)); + } + + SECTION("from std::array") + { + std::array v { {'t', 'r', 'u', 'e', '\0'} }; + CHECK(json::parse(v) == json(true)); + } + + SECTION("from array") + { + uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(v) == json(true)); + } + + SECTION("from std::string") + { + std::string v = {'t', 'r', 'u', 'e'}; + CHECK(json::parse(v) == json(true)); + } + + SECTION("from std::initializer_list") + { + std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(v) == json(true)); + } } - SECTION("from std::array") + SECTION("via iterator range") { - std::array v { {'t', 'r', 'u', 'e', '\0'} }; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); - } + SECTION("from std::vector") + { + std::vector v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } - SECTION("from array") - { - uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); - } + SECTION("from std::array") + { + std::array v { {'t', 'r', 'u', 'e', '\0'} }; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } - SECTION("from std::string") - { - std::string v = {'t', 'r', 'u', 'e'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); - } + SECTION("from array") + { + uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } - SECTION("from std::initializer_list") - { - std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); - } + SECTION("from std::string") + { + std::string v = {'t', 'r', 'u', 'e'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } - SECTION("from std::valarray") - { - std::valarray v = {'t', 'r', 'u', 'e', '\0'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + SECTION("from std::initializer_list") + { + std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("from std::valarray") + { + std::valarray v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("with empty range") + { + std::vector v; + CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); + } } } }