From 9b1c0588105fbd92a0bc257f28f340bd7abbba3d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 23 Jul 2017 18:11:34 +0200 Subject: [PATCH] :hammer: reorganized interfaces for parse/accept functions #623 We now rely on implicit conversions to an input_adapter object in the parse/accept functions. --- src/json.hpp | 289 +++++++++------------------------ test/src/unit-class_lexer.cpp | 2 +- test/src/unit-class_parser.cpp | 36 ++-- 3 files changed, 94 insertions(+), 233 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 6abe73fc..63f5d2b8 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1267,19 +1267,19 @@ constexpr T static_const::value; //////////////////// /// abstract input adapter interface -struct input_adapter +struct input_adapter_protocol { virtual int get_character() = 0; virtual std::string read(std::size_t offset, std::size_t length) = 0; - virtual ~input_adapter() = default; + virtual ~input_adapter_protocol() = default; }; /// a type to simplify interfaces -using input_adapter_t = std::shared_ptr; +using input_adapter_t = std::shared_ptr; /// input adapter for cached stream input template -class cached_input_stream_adapter : public input_adapter +class cached_input_stream_adapter : public input_adapter_protocol { public: explicit cached_input_stream_adapter(std::istream& i) @@ -1386,7 +1386,7 @@ class cached_input_stream_adapter : public input_adapter }; /// input adapter for buffer input -class input_buffer_adapter : public input_adapter +class input_buffer_adapter : public input_adapter_protocol { public: input_buffer_adapter(const char* b, std::size_t l) @@ -1429,27 +1429,22 @@ class input_buffer_adapter : public input_adapter const char* start; }; -struct input_adapter_factory +class input_adapter { + public: // native support /// input adapter for input stream - static std::shared_ptr create(std::istream& i) - { - return std::make_shared> (i); - } + input_adapter(std::istream& i) + : ia(std::make_shared>(i)) {} /// input adapter for input stream - static std::shared_ptr create(std::istream&& i) - { - return std::make_shared>(i); - } + input_adapter(std::istream&& i) + : ia(std::make_shared>(i)) {} /// input adapter for buffer - static std::shared_ptr create(const char* b, std::size_t l) - { - return std::make_shared(b, l); - } + input_adapter(const char* b, std::size_t l) + : ia(std::make_shared(b, l)) {} // derived support @@ -1461,11 +1456,9 @@ struct input_adapter_factory typename std::remove_pointer::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static std::shared_ptr create(CharT b) - { - return create(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))); - } + input_adapter(CharT b) + : input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} /// input adapter for iterator range with contiguous storage template ::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> - static std::shared_ptr create(IteratorType first, - IteratorType last) + input_adapter(IteratorType first, IteratorType last) { // assertion to check that the iterator range is indeed contiguous, // see http://stackoverflow.com/a/35008842/266378 for more discussion @@ -1496,22 +1488,19 @@ struct input_adapter_factory if (JSON_LIKELY(len > 0)) { // there is at least one element: use the address of first - return create(reinterpret_cast(&(*first)), len); + ia = std::make_shared(reinterpret_cast(&(*first)), len); } else { // the address of first cannot be used - use nullptr - return create(nullptr, len); + ia = std::make_shared(nullptr, len); } } /// input adapter for array template - static std::shared_ptr create(T (&array)[N]) - { - // delegate the call to the iterator-range overload - return create(std::begin(array), std::end(array)); - } + input_adapter(T (&array)[N]) + : input_adapter(std::begin(array), std::end(array)) {} /// input adapter for contiguous container template < @@ -1523,11 +1512,17 @@ struct input_adapter_factory std::declval()))>:: iterator_category>::value, int >::type = 0 > - static std::shared_ptr create(const ContiguousContainer& c) + input_adapter(const ContiguousContainer& c) + : input_adapter(std::begin(c), std::end(c)) {} + + operator input_adapter_t() { - // delegate the call to the iterator-range overload - return create(std::begin(c), std::end(c)); + return ia; } + + private: + /// the actual adapter + input_adapter_t ia = nullptr; }; ////////////////////// @@ -12817,15 +12812,36 @@ class basic_json /// @{ /*! - @brief deserialize from an array + @brief deserialize from a compatible input - This function reads from an array of 1-byte values. + This function reads from a compatible input. Examples are: + - an array of 1-byte values + - strings with character/literal type with size of 1 byte + - input streams + - 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 as they implement random-access iterators + and a contiguous storage. @pre Each element of the container has a size of 1 byte. Violating this precondition yields undefined behavior. **This precondition is enforced with a static assertion.** - @param[in] array array to read from + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @param[in] i input to read from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -12846,130 +12862,44 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function reading from an array.,parse__array__parser_callback_t} - @since version 2.0.3 - */ - 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); - } - - template - static bool accept(T (&array)[N]) - { - // delegate the call to the iterator-range accept overload - return accept(std::begin(array), std::end(array)); - } - - /*! - @brief deserialize from string literal - - @tparam CharT character/literal type with size of 1 byte - @param[in] s string literal to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - @note String containers like `std::string` or @ref string_t can be parsed - with @ref parse(const ContiguousContainer&, const parser_callback_t) - @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} - @sa @ref parse(std::istream&, const parser_callback_t) for a version that - reads from an input stream - - @since version 1.0.0 (originally for @ref string_t) - */ - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static basic_json parse(const CharT s, - const parser_callback_t cb = nullptr) - { - basic_json result; - parser(detail::input_adapter_factory::create(s), cb).parse(true, result); - return result; - } - - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static bool accept(const CharT s) - { - return parser(detail::input_adapter_factory::create(s)).accept(true); - } - - /*! - @brief deserialize from stream - - @param[in,out] i stream to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} - @sa @ref parse(const CharT, const parser_callback_t) for a version - that reads from a string + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - @since version 1.0.0 + @since version 2.0.3 (contiguous containers) */ - static basic_json parse(std::istream& i, + static basic_json parse(detail::input_adapter i, const parser_callback_t cb = nullptr) { basic_json result; - parser(detail::input_adapter_factory::create(i), cb).parse(true, result); + parser(i, cb).parse(true, result); return result; } - static bool accept(std::istream& i) - { - return parser(detail::input_adapter_factory::create(i)).accept(true); - } - /*! - @copydoc parse(std::istream&, const parser_callback_t) + @copydoc basic_json parse(detail::input_adapter, const parser_callback_t) */ - static basic_json parse(std::istream&& i, + static basic_json parse(detail::input_adapter& i, const parser_callback_t cb = nullptr) { basic_json result; - parser(detail::input_adapter_factory::create(i), cb).parse(true, result); + parser(i, cb).parse(true, result); return result; } - static bool accept(std::istream&& i) + static bool accept(detail::input_adapter i) { - return parser(detail::input_adapter_factory::create(i)).accept(true); + return parser(i).accept(true); + } + + static bool accept(detail::input_adapter& i) + { + return parser(i).accept(true); } /*! @@ -13025,7 +12955,7 @@ class basic_json const parser_callback_t cb = nullptr) { basic_json result; - parser(detail::input_adapter_factory::create(first, last), cb).parse(true, result); + parser(detail::input_adapter(first, last), cb).parse(true, result); return result; } @@ -13035,76 +12965,7 @@ class basic_json typename std::iterator_traits::iterator_category>::value, int>::type = 0> static bool accept(IteratorType first, IteratorType last) { - return parser(detail::input_adapter_factory::create(first, last)).accept(true); - } - - /*! - @brief deserialize from a container with contiguous storage - - This function reads from a container with contiguous storage of 1-byte - values. Compatible container types include `std::vector`, `std::string`, - `std::array`, and `std::initializer_list`. User-defined containers can be - used as long as they implement random-access iterators and a contiguous - storage. - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @tparam ContiguousContainer container type with contiguous storage - @param[in] c container to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function reading - from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - - @since version 2.0.3 - */ - template::value and - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits()))>::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::value and - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits()))>::iterator_category>::value - , int>::type = 0> - static bool accept(const ContiguousContainer& c) - { - // delegate the call to the iterator-range accept overload - return accept(std::begin(c), std::end(c)); + return parser(detail::input_adapter(first, last)).accept(true); } /*! @@ -13117,7 +12978,7 @@ class basic_json JSON_DEPRECATED friend std::istream& operator<<(basic_json& j, std::istream& i) { - parser(detail::input_adapter_factory::create(i)).parse(false, j); + parser(detail::input_adapter(i)).parse(false, j); return i; } @@ -13148,7 +13009,7 @@ class basic_json */ friend std::istream& operator>>(std::istream& i, basic_json& j) { - parser(detail::input_adapter_factory::create(i)).parse(false, j); + parser(detail::input_adapter(i)).parse(false, j); return i; } @@ -13478,7 +13339,7 @@ class basic_json static basic_json from_cbor(const std::vector& v, const std::size_t start_index = 0) { - binary_reader br(detail::input_adapter_factory::create(v.begin() + static_cast(start_index), v.end())); + binary_reader br(detail::input_adapter(v.begin() + static_cast(start_index), v.end())); return br.parse_cbor(); } @@ -13553,7 +13414,7 @@ class basic_json static basic_json from_msgpack(const std::vector& v, const std::size_t start_index = 0) { - binary_reader br(detail::input_adapter_factory::create(v.begin() + static_cast(start_index), v.end())); + binary_reader br(detail::input_adapter(v.begin() + static_cast(start_index), v.end())); return br.parse_msgpack(); } diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 794feb4e..fbb98b58 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -36,7 +36,7 @@ using nlohmann::json; json::lexer::token_type scan_string(const char* s); json::lexer::token_type scan_string(const char* s) { - return json::lexer(nlohmann::detail::input_adapter_factory::create(s)).scan(); + return json::lexer(nlohmann::detail::input_adapter(s)).scan(); } TEST_CASE("lexer class") diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 5175ce83..f23d2c18 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -40,13 +40,13 @@ bool accept_helper(const std::string& s); json parser_helper(const std::string& s) { json j; - json::parser(nlohmann::detail::input_adapter_factory::create(s)).parse(true, j); + json::parser(nlohmann::detail::input_adapter(s)).parse(true, j); return j; } bool accept_helper(const std::string& s) { - return json::parser(nlohmann::detail::input_adapter_factory::create(s)).accept(true); + return json::parser(nlohmann::detail::input_adapter(s)).accept(true); } TEST_CASE("parser class") @@ -1009,7 +1009,7 @@ TEST_CASE("parser class") case ('r'): case ('t'): { - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s.c_str()))).accept()); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s.c_str()))).accept()); break; } @@ -1022,7 +1022,7 @@ TEST_CASE("parser class") // any other combination of backslash and character is invalid default: { - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s.c_str()))).accept() == false); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s.c_str()))).accept() == false); break; } } @@ -1081,27 +1081,27 @@ TEST_CASE("parser class") if (valid(c)) { CAPTURE(s1); - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s1.c_str()))).accept()); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s1.c_str()))).accept()); CAPTURE(s2); - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s2.c_str()))).accept()); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s2.c_str()))).accept()); CAPTURE(s3); - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s3.c_str()))).accept()); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s3.c_str()))).accept()); CAPTURE(s4); - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s4.c_str()))).accept()); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s4.c_str()))).accept()); } else { CAPTURE(s1); - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s1.c_str()))).accept() == false); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s1.c_str()))).accept() == false); CAPTURE(s2); - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s2.c_str()))).accept() == false); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s2.c_str()))).accept() == false); CAPTURE(s3); - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s3.c_str()))).accept() == false); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s3.c_str()))).accept() == false); CAPTURE(s4); - CHECK(json::parser(nlohmann::detail::input_adapter_factory::create(std::string(s4.c_str()))).accept() == false); + CHECK(json::parser(nlohmann::detail::input_adapter(std::string(s4.c_str()))).accept() == false); } } } @@ -1311,7 +1311,7 @@ TEST_CASE("parser class") { std::vector v = {'t', 'r', 'u', 'e'}; json j; - json::parser(nlohmann::detail::input_adapter_factory::create(std::begin(v), std::end(v))).parse(true, j); + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); CHECK(j == json(true)); } @@ -1319,7 +1319,7 @@ TEST_CASE("parser class") { std::array v { {'t', 'r', 'u', 'e'} }; json j; - json::parser(nlohmann::detail::input_adapter_factory::create(std::begin(v), std::end(v))).parse(true, j); + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); CHECK(j == json(true)); } @@ -1327,7 +1327,7 @@ TEST_CASE("parser class") { uint8_t v[] = {'t', 'r', 'u', 'e'}; json j; - json::parser(nlohmann::detail::input_adapter_factory::create(std::begin(v), std::end(v))).parse(true, j); + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); CHECK(j == json(true)); } @@ -1340,7 +1340,7 @@ TEST_CASE("parser class") { std::string v = {'t', 'r', 'u', 'e'}; json j; - json::parser(nlohmann::detail::input_adapter_factory::create(std::begin(v), std::end(v))).parse(true, j); + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); CHECK(j == json(true)); } @@ -1348,7 +1348,7 @@ TEST_CASE("parser class") { std::initializer_list v = {'t', 'r', 'u', 'e'}; json j; - json::parser(nlohmann::detail::input_adapter_factory::create(std::begin(v), std::end(v))).parse(true, j); + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); CHECK(j == json(true)); } @@ -1356,7 +1356,7 @@ TEST_CASE("parser class") { std::valarray v = {'t', 'r', 'u', 'e'}; json j; - json::parser(nlohmann::detail::input_adapter_factory::create(std::begin(v), std::end(v))).parse(true, j); + json::parser(nlohmann::detail::input_adapter(std::begin(v), std::end(v))).parse(true, j); CHECK(j == json(true)); } }