# HG changeset patch # User David Demelier # Date 1540321800 -7200 # Node ID ff73f2dd1c8202fa7e8be29e60c2e20ec7e1f04b # Parent 3129f59002d23c0cd555df0702e677dddea8deba json_util: split and style diff -r 3129f59002d2 -r ff73f2dd1c82 cpp/json_util/CMakeLists.txt --- a/cpp/json_util/CMakeLists.txt Tue Oct 23 20:13:00 2018 +0200 +++ b/cpp/json_util/CMakeLists.txt Tue Oct 23 21:10:00 2018 +0200 @@ -23,4 +23,5 @@ LIBRARIES json SOURCES ${json-util_SOURCE_DIR}/json_util.hpp + ${json-util_SOURCE_DIR}/json_util.cpp ) diff -r 3129f59002d2 -r ff73f2dd1c82 cpp/json_util/json_util.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpp/json_util/json_util.cpp Tue Oct 23 21:10:00 2018 +0200 @@ -0,0 +1,154 @@ +/* + * json_util.cpp -- utilities for JSON + * + * Copyright (c) 2018 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "json_util.hpp" + +using nlohmann::json; + +namespace json_util { + +namespace { + +template +auto clampi(const json& value) noexcept -> std::optional +{ + static_assert(std::is_signed::value, "Int must be signed"); + + if (!value.is_number_integer()) + return std::nullopt; + + const auto ret = value.get(); + + if (ret < std::numeric_limits::min() || ret > std::numeric_limits::max()) + return std::nullopt; + + return static_cast(ret); +} + +template +auto clampu(const json& value) noexcept -> std::optional +{ + static_assert(std::is_unsigned::value, "Int must be unsigned"); + + if (!value.is_number_unsigned()) + return std::nullopt; + + const auto ret = value.get(); + + if (ret > std::numeric_limits::max()) + return std::nullopt; + + return static_cast(ret); +} + +} // !namespace + +auto type_traits::get(const json& value) noexcept -> std::optional +{ + if (!value.is_boolean()) + return std::nullopt; + + return value.get(); +} + +auto type_traits::get(const json& value) noexcept -> std::optional +{ + if (!value.is_number_float()) + return std::nullopt; + + return value.get(); +} + +auto type_traits::get(const json& value) -> std::optional +{ + if (!value.is_string()) + return std::nullopt; + + return value.get(); +} + +auto type_traits::get(const json& value) -> std::optional +{ + return clampi(value); +} + +auto type_traits::get(const json& value) -> std::optional +{ + return clampi(value); +} + +auto type_traits::get(const json& value) -> std::optional +{ + return clampi(value); +} + +auto type_traits::get(const json& value) noexcept -> std::optional +{ + if (!value.is_number_integer()) + return std::nullopt; + + return value.get(); +} + +auto type_traits::get(const json& value) -> std::optional +{ + return clampu(value); +} + +auto type_traits::get(const json& value) -> std::optional +{ + return clampu(value); +} + +auto type_traits::get(const json& value) -> std::optional +{ + return clampu(value); +} + +auto type_traits::get(const json& value) noexcept -> std::optional +{ + if (!value.is_number_unsigned()) + return std::nullopt; + + return value.get(); +} + +auto pretty(const json& value, int indent) -> std::string +{ + switch (value.type()) { + case json::value_t::null: + return "null"; + case json::value_t::string: + return value.get(); + case json::value_t::boolean: + return value.get() ? "true" : "false"; + case json::value_t::number_integer: + return std::to_string(value.get()); + case json::value_t::number_unsigned: + return std::to_string(value.get()); + case json::value_t::number_float: + return std::to_string(value.get()); + default: + return value.dump(indent); + } +} + +} // !json_util diff -r 3129f59002d2 -r ff73f2dd1c82 cpp/json_util/json_util.hpp --- a/cpp/json_util/json_util.hpp Tue Oct 23 20:13:00 2018 +0200 +++ b/cpp/json_util/json_util.hpp Tue Oct 23 21:10:00 2018 +0200 @@ -25,10 +25,8 @@ */ #include -#include #include #include -#include #include @@ -38,52 +36,6 @@ namespace json_util { /** - * \cond JSON_UTIL_HIDDEN_SYMBOLS - */ - -namespace detail { - -template -class parser_type_traits_uint : public std::true_type { -public: - static std::optional get(const nlohmann::json& value) noexcept - { - if (!value.is_number_unsigned()) - return std::nullopt; - - const auto ret = value.get(); - - if (ret > std::numeric_limits::max()) - return std::nullopt; - - return static_cast(ret); - } -}; - -template -class parser_type_traits_int : public std::true_type { -public: - static std::optional get(const nlohmann::json& value) noexcept - { - if (!value.is_number_integer()) - return std::nullopt; - - const auto ret = value.get(); - - if (ret < std::numeric_limits::min() || ret > std::numeric_limits::max()) - return std::nullopt; - - return static_cast(ret); - } -}; - -} // !detail - -/** - * \endcond - */ - -/** * \brief Describe how to convert a JSON value. * * This class must be specialized for every type you want to convert from JSON @@ -102,158 +54,164 @@ * * - bool * - double - * - std::uint(8, 16, 32, 64) + * - std::uint(8, 16, 32, 64)_t * - std::string */ template -class parser_type_traits : public std::false_type { -}; +struct type_traits; /** * \brief Specialization for `bool`. */ template <> -class parser_type_traits : public std::true_type { -public: +struct type_traits { /** * Convert the JSON value to bool. * * \param value the value * \return the bool or none if not a boolean type */ - static std::optional get(const nlohmann::json& value) noexcept - { - if (!value.is_boolean()) - return std::nullopt; - - return value.get(); - } + static auto get(const nlohmann::json& value) noexcept -> std::optional; }; /** * \brief Specialization for `double`. */ template <> -class parser_type_traits : public std::true_type { -public: +struct type_traits { /** * Convert the JSON value to bool. * * \param value the value * \return the double or none if not a double type */ - static std::optional get(const nlohmann::json& value) noexcept - { - if (!value.is_number_float()) - return std::nullopt; - - return value.get(); - } + static auto get(const nlohmann::json& value) noexcept -> std::optional; }; /** * \brief Specialization for `std::string`. */ template <> -class parser_type_traits : public std::true_type { -public: +struct type_traits { /** - * Convert the JSON value to bool. + * Convert the JSON value to std::string. * * \param value the value * \return the string or none if not a string type */ - static std::optional get(const nlohmann::json& value) - { - if (!value.is_string()) - return std::nullopt; - - return value.get(); - } + static auto get(const nlohmann::json& value) -> std::optional; }; /** * \brief Specialization for `std::int8_t`. */ template <> -class parser_type_traits : public detail::parser_type_traits_int { +struct type_traits { + /** + * Convert the JSON value to std::int8_t. + * + * \param value the value + * \return the value or none if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional; }; /** * \brief Specialization for `std::int16_t`. */ template <> -class parser_type_traits : public detail::parser_type_traits_int { +struct type_traits { + /** + * Convert the JSON value to std::int16_t. + * + * \param value the value + * \return the value or none if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional; }; /** * \brief Specialization for `std::int32_t`. */ template <> -class parser_type_traits : public detail::parser_type_traits_int { +struct type_traits { + /** + * Convert the JSON value to std::int32_t. + * + * \param value the value + * \return the value or none if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional; }; /** * \brief Specialization for `std::int64_t`. */ template <> -class parser_type_traits : public std::true_type { -public: +struct type_traits { /** * Convert the JSON value to std::int64_t. * * \param value the value * \return the int or none if not a int type */ - static std::optional get(const nlohmann::json& value) noexcept - { - if (!value.is_number_integer()) - return std::nullopt; - - return value.get(); - } + static auto get(const nlohmann::json& value) noexcept -> std::optional; }; /** - * \brief Specialization for `std::int8_t`. + * \brief Specialization for `std::uint8_t`. */ template <> -class parser_type_traits : public detail::parser_type_traits_uint { +struct type_traits { + /** + * Convert the JSON value to std::uint8_t. + * + * \param value the value + * \return the value or none if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional; }; /** - * \brief Specialization for `std::int16_t`. + * \brief Specialization for `std::uint16_t`. */ template <> -class parser_type_traits : public detail::parser_type_traits_uint { +struct type_traits { + /** + * Convert the JSON value to std::uint16_t. + * + * \param value the value + * \return the value or none if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional; }; /** * \brief Specialization for `std::int32_t`. */ template <> -class parser_type_traits : public detail::parser_type_traits_uint { +struct type_traits { + /** + * Convert the JSON value to std::uint32_t. + * + * \param value the value + * \return the value or none if value does not fit between the range + */ + static auto get(const nlohmann::json& value) -> std::optional; }; /** - * \brief Specialization for `std::int64_t`. + * \brief Specialization for `std::uint64_t`. */ template <> -class parser_type_traits : public std::true_type { -public: +struct type_traits { /** * Convert the JSON value to std::uint64_t. * * \param value the value * \return the int or none if not a int type */ - static std::optional get(const nlohmann::json& value) noexcept - { - if (!value.is_number_unsigned()) - return std::nullopt; - - return value.get(); - } + static auto get(const nlohmann::json& value) noexcept -> std::optional; }; /** @@ -262,7 +220,7 @@ * This class helps destructuring insecure JSON input by returning optional * values if they are not present or invalid. */ -class document : public nlohmann::json { +class deserializer : public nlohmann::json { public: /** * Inherited constructor. @@ -276,16 +234,14 @@ * \return the value or std::nullopt if not found or not convertible */ template - inline std::optional get(const std::string& key) const noexcept + auto get(const std::string& key) const noexcept -> std::optional { - static_assert(parser_type_traits::value, "type not supported"); - const auto it = find(key); if (it == end()) return std::nullopt; - return parser_type_traits::get(*it); + return type_traits::get(*it); } /** @@ -299,16 +255,14 @@ * \return the value, std::nullopt or def */ template - inline std::optional optional(const std::string& key, DefaultValue&& def) const noexcept + auto optional(const std::string& key, DefaultValue&& def) const noexcept -> std::optional { - static_assert(parser_type_traits::value, "type not supported"); - const auto it = find(key); if (it == end()) return std::optional(std::forward(def)); - return parser_type_traits::get(*it); + return type_traits::get(*it); } }; @@ -320,25 +274,7 @@ * \param indent the optional indent for objects/arrays * \return the string */ -inline std::string pretty(const nlohmann::json& value, int indent = 4) -{ - switch (value.type()) { - case nlohmann::json::value_t::null: - return "null"; - case nlohmann::json::value_t::string: - return value.get(); - case nlohmann::json::value_t::boolean: - return value.get() ? "true" : "false"; - case nlohmann::json::value_t::number_integer: - return std::to_string(value.get()); - case nlohmann::json::value_t::number_unsigned: - return std::to_string(value.get()); - case nlohmann::json::value_t::number_float: - return std::to_string(value.get()); - default: - return value.dump(indent); - } -} +auto pretty(const nlohmann::json& value, int indent = 4) -> std::string; } // !json_util diff -r 3129f59002d2 -r ff73f2dd1c82 cpp/json_util/test/main.cpp --- a/cpp/json_util/test/main.cpp Tue Oct 23 20:13:00 2018 +0200 +++ b/cpp/json_util/test/main.cpp Tue Oct 23 21:10:00 2018 +0200 @@ -21,26 +21,11 @@ #include -namespace std { - -template -auto operator<<(ostream& out, const optional& value) -> ostream& -{ - if (!value) - out << ""; - else - out << *value; - - return out; -} - -} // !std - namespace json_util { class test_fixture { protected: - document document_{ + deserializer document_{ { "boolean", true }, { "int", -1234 }, { "uint", 1234U }, @@ -56,7 +41,7 @@ { const auto v = document_.get("boolean"); - BOOST_TEST(v); + BOOST_TEST(v.has_value()); BOOST_TEST(*v); BOOST_TEST(!document_.get("int")); BOOST_TEST(!document_.get("int")); @@ -68,7 +53,7 @@ { const auto v = document_.get("int"); - BOOST_TEST(v); + BOOST_TEST(v.has_value()); BOOST_TEST(*v == -1234); BOOST_TEST(!document_.get("bool")); BOOST_TEST(!document_.get("string")); @@ -78,7 +63,7 @@ { const auto v = document_.get("uint"); - BOOST_TEST(v); + BOOST_TEST(v.has_value()); BOOST_TEST(*v == 1234U); BOOST_TEST(!document_.get("bool")); BOOST_TEST(!document_.get("int")); @@ -89,7 +74,7 @@ { const auto v = document_.get("string"); - BOOST_TEST(v); + BOOST_TEST(v.has_value()); BOOST_TEST(*v == "str"); BOOST_TEST(!document_.get("bool")); BOOST_TEST(!document_.get("int"));