changeset 650:ff73f2dd1c82

json_util: split and style
author David Demelier <markand@malikania.fr>
date Tue, 23 Oct 2018 21:10:00 +0200
parents 3129f59002d2
children 728bdf008ec3
files cpp/json_util/CMakeLists.txt cpp/json_util/json_util.cpp cpp/json_util/json_util.hpp cpp/json_util/test/main.cpp
diffstat 4 files changed, 230 insertions(+), 154 deletions(-) [+]
line wrap: on
line diff
--- 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
 )
--- /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 <markand@malikania.fr>
+ *
+ * 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 <limits>
+#include <type_traits>
+
+#include "json_util.hpp"
+
+using nlohmann::json;
+
+namespace json_util {
+
+namespace {
+
+template <typename Int>
+auto clampi(const json& value) noexcept -> std::optional<Int>
+{
+	static_assert(std::is_signed<Int>::value, "Int must be signed");
+
+	if (!value.is_number_integer())
+		return std::nullopt;
+
+	const auto ret = value.get<std::int64_t>();
+
+	if (ret < std::numeric_limits<Int>::min() || ret > std::numeric_limits<Int>::max())
+		return std::nullopt;
+
+	return static_cast<Int>(ret);
+}
+
+template <typename Int>
+auto clampu(const json& value) noexcept -> std::optional<Int>
+{
+	static_assert(std::is_unsigned<Int>::value, "Int must be unsigned");
+
+	if (!value.is_number_unsigned())
+		return std::nullopt;
+
+	const auto ret = value.get<std::uint64_t>();
+
+	if (ret > std::numeric_limits<Int>::max())
+		return std::nullopt;
+
+	return static_cast<Int>(ret);
+}
+
+} // !namespace
+
+auto type_traits<bool>::get(const json& value) noexcept -> std::optional<bool>
+{
+	if (!value.is_boolean())
+		return std::nullopt;
+
+	return value.get<bool>();
+}
+
+auto type_traits<double>::get(const json& value) noexcept -> std::optional<double>
+{
+	if (!value.is_number_float())
+		return std::nullopt;
+
+	return value.get<double>();
+}
+
+auto type_traits<std::string>::get(const json& value) -> std::optional<std::string>
+{
+	if (!value.is_string())
+		return std::nullopt;
+
+	return value.get<std::string>();
+}
+
+auto type_traits<std::int8_t>::get(const json& value) -> std::optional<std::int8_t>
+{
+	return clampi<std::int8_t>(value);
+}
+
+auto type_traits<std::int16_t>::get(const json& value) -> std::optional<std::int16_t>
+{
+	return clampi<std::int16_t>(value);
+}
+
+auto type_traits<std::int32_t>::get(const json& value) -> std::optional<std::int32_t>
+{
+	return clampi<std::int32_t>(value);
+}
+
+auto type_traits<std::int64_t>::get(const json& value) noexcept -> std::optional<std::int64_t>
+{
+	if (!value.is_number_integer())
+		return std::nullopt;
+
+	return value.get<std::int64_t>();
+}
+
+auto type_traits<std::uint8_t>::get(const json& value) -> std::optional<std::uint8_t>
+{
+	return clampu<std::uint8_t>(value);
+}
+
+auto type_traits<std::uint16_t>::get(const json& value) -> std::optional<std::uint16_t>
+{
+	return clampu<std::uint16_t>(value);
+}
+
+auto type_traits<std::uint32_t>::get(const json& value) -> std::optional<std::uint32_t>
+{
+	return clampu<std::uint32_t>(value);
+}
+
+auto type_traits<std::uint64_t>::get(const json& value) noexcept -> std::optional<std::uint64_t>
+{
+	if (!value.is_number_unsigned())
+		return std::nullopt;
+
+	return value.get<std::uint64_t>();
+}
+
+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<std::string>();
+	case json::value_t::boolean:
+		return value.get<bool>() ? "true" : "false";
+	case json::value_t::number_integer:
+		return std::to_string(value.get<std::int64_t>());
+	case json::value_t::number_unsigned:
+		return std::to_string(value.get<std::uint64_t>());
+	case json::value_t::number_float:
+		return std::to_string(value.get<double>());
+	default:
+		return value.dump(indent);
+	}
+}
+
+} // !json_util
--- 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 <cstdint>
-#include <limits>
 #include <optional>
 #include <string>
-#include <type_traits>
 
 #include <json.hpp>
 
@@ -38,52 +36,6 @@
 namespace json_util {
 
 /**
- * \cond JSON_UTIL_HIDDEN_SYMBOLS
- */
-
-namespace detail {
-
-template <typename Int>
-class parser_type_traits_uint : public std::true_type {
-public:
-	static std::optional<Int> get(const nlohmann::json& value) noexcept
-	{
-		if (!value.is_number_unsigned())
-			return std::nullopt;
-
-		const auto ret = value.get<std::uint64_t>();
-
-		if (ret > std::numeric_limits<Int>::max())
-			return std::nullopt;
-
-		return static_cast<Int>(ret);
-	}
-};
-
-template <typename Int>
-class parser_type_traits_int : public std::true_type {
-public:
-	static std::optional<Int> get(const nlohmann::json& value) noexcept
-	{
-		if (!value.is_number_integer())
-			return std::nullopt;
-
-		const auto ret = value.get<std::int64_t>();
-
-		if (ret < std::numeric_limits<Int>::min() || ret > std::numeric_limits<Int>::max())
-			return std::nullopt;
-
-		return static_cast<Int>(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 <typename T>
-class parser_type_traits : public std::false_type {
-};
+struct type_traits;
 
 /**
  * \brief Specialization for `bool`.
  */
 template <>
-class parser_type_traits<bool> : public std::true_type {
-public:
+struct type_traits<bool> {
 	/**
 	 * Convert the JSON value to bool.
 	 *
 	 * \param value the value
 	 * \return the bool or none if not a boolean type
 	 */
-	static std::optional<bool> get(const nlohmann::json& value) noexcept
-	{
-		if (!value.is_boolean())
-			return std::nullopt;
-
-		return value.get<bool>();
-	}
+	static auto get(const nlohmann::json& value) noexcept -> std::optional<bool>;
 };
 
 /**
  * \brief Specialization for `double`.
  */
 template <>
-class parser_type_traits<double> : public std::true_type {
-public:
+struct type_traits<double> {
 	/**
 	 * Convert the JSON value to bool.
 	 *
 	 * \param value the value
 	 * \return the double or none if not a double type
 	 */
-	static std::optional<double> get(const nlohmann::json& value) noexcept
-	{
-		if (!value.is_number_float())
-			return std::nullopt;
-
-		return value.get<double>();
-	}
+	static auto get(const nlohmann::json& value) noexcept -> std::optional<double>;
 };
 
 /**
  * \brief Specialization for `std::string`.
  */
 template <>
-class parser_type_traits<std::string> : public std::true_type {
-public:
+struct type_traits<std::string> {
 	/**
-	 * 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<std::string> get(const nlohmann::json& value)
-	{
-		if (!value.is_string())
-			return std::nullopt;
-
-		return value.get<std::string>();
-	}
+	static auto get(const nlohmann::json& value) -> std::optional<std::string>;
 };
 
 /**
  * \brief Specialization for `std::int8_t`.
  */
 template <>
-class parser_type_traits<std::int8_t> : public detail::parser_type_traits_int<std::int8_t> {
+struct type_traits<std::int8_t> {
+	/**
+	 * 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<std::int8_t>;
 };
 
 /**
  * \brief Specialization for `std::int16_t`.
  */
 template <>
-class parser_type_traits<std::int16_t> : public detail::parser_type_traits_int<std::int16_t> {
+struct type_traits<std::int16_t> {
+	/**
+	 * 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<std::int16_t>;
 };
 
 /**
  * \brief Specialization for `std::int32_t`.
  */
 template <>
-class parser_type_traits<std::int32_t> : public detail::parser_type_traits_int<std::int32_t> {
+struct type_traits<std::int32_t> {
+	/**
+	 * 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<std::int32_t>;
 };
 
 /**
  * \brief Specialization for `std::int64_t`.
  */
 template <>
-class parser_type_traits<std::int64_t> : public std::true_type {
-public:
+struct type_traits<std::int64_t> {
 	/**
 	 * 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<std::int64_t> get(const nlohmann::json& value) noexcept
-	{
-		if (!value.is_number_integer())
-			return std::nullopt;
-
-		return value.get<std::int64_t>();
-	}
+	static auto get(const nlohmann::json& value) noexcept -> std::optional<std::int64_t>;
 };
 
 /**
- * \brief Specialization for `std::int8_t`.
+ * \brief Specialization for `std::uint8_t`.
  */
 template <>
-class parser_type_traits<std::uint8_t> : public detail::parser_type_traits_uint<std::uint8_t> {
+struct type_traits<std::uint8_t> {
+	/**
+	 * 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<std::uint8_t>;
 };
 
 /**
- * \brief Specialization for `std::int16_t`.
+ * \brief Specialization for `std::uint16_t`.
  */
 template <>
-class parser_type_traits<std::uint16_t> : public detail::parser_type_traits_uint<std::uint16_t> {
+struct type_traits<std::uint16_t> {
+	/**
+	 * 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<std::uint16_t>;
 };
 
 /**
  * \brief Specialization for `std::int32_t`.
  */
 template <>
-class parser_type_traits<std::uint32_t> : public detail::parser_type_traits_uint<std::uint32_t> {
+struct type_traits<std::uint32_t> {
+	/**
+	 * 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<std::uint32_t>;
 };
 
 /**
- * \brief Specialization for `std::int64_t`.
+ * \brief Specialization for `std::uint64_t`.
  */
 template <>
-class parser_type_traits<std::uint64_t> : public std::true_type {
-public:
+struct type_traits<std::uint64_t> {
 	/**
 	 * 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<std::uint64_t> get(const nlohmann::json& value) noexcept
-	{
-		if (!value.is_number_unsigned())
-			return std::nullopt;
-
-		return value.get<std::uint64_t>();
-	}
+	static auto get(const nlohmann::json& value) noexcept -> std::optional<std::uint64_t>;
 };
 
 /**
@@ -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 <typename Type>
-	inline std::optional<Type> get(const std::string& key) const noexcept
+	auto get(const std::string& key) const noexcept -> std::optional<Type>
 	{
-		static_assert(parser_type_traits<Type>::value, "type not supported");
-
 		const auto it = find(key);
 
 		if (it == end())
 			return std::nullopt;
 
-		return parser_type_traits<Type>::get(*it);
+		return type_traits<Type>::get(*it);
 	}
 
 	/**
@@ -299,16 +255,14 @@
 	 * \return the value, std::nullopt or def
 	 */
 	template <typename Type, typename DefaultValue>
-	inline std::optional<Type> optional(const std::string& key, DefaultValue&& def) const noexcept
+	auto optional(const std::string& key, DefaultValue&& def) const noexcept -> std::optional<Type>
 	{
-		static_assert(parser_type_traits<Type>::value, "type not supported");
-
 		const auto it = find(key);
 
 		if (it == end())
 			return std::optional<Type>(std::forward<DefaultValue>(def));
 
-		return parser_type_traits<Type>::get(*it);
+		return type_traits<Type>::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<std::string>();
-	case nlohmann::json::value_t::boolean:
-		return value.get<bool>() ? "true" : "false";
-	case nlohmann::json::value_t::number_integer:
-		return std::to_string(value.get<std::int64_t>());
-	case nlohmann::json::value_t::number_unsigned:
-		return std::to_string(value.get<std::uint64_t>());
-	case nlohmann::json::value_t::number_float:
-		return std::to_string(value.get<double>());
-	default:
-		return value.dump(indent);
-	}
-}
+auto pretty(const nlohmann::json& value, int indent = 4) -> std::string;
 
 } // !json_util
 
--- 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 <json_util.hpp>
 
-namespace std {
-
-template <typename T>
-auto operator<<(ostream& out, const optional<T>& value) -> ostream&
-{
-	if (!value)
-		out << "<empty>";
-	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<bool>("boolean");
 
-	BOOST_TEST(v);
+	BOOST_TEST(v.has_value());
 	BOOST_TEST(*v);
 	BOOST_TEST(!document_.get<bool>("int"));
 	BOOST_TEST(!document_.get<bool>("int"));
@@ -68,7 +53,7 @@
 {
 	const auto v = document_.get<int>("int");
 
-	BOOST_TEST(v);
+	BOOST_TEST(v.has_value());
 	BOOST_TEST(*v == -1234);
 	BOOST_TEST(!document_.get<int>("bool"));
 	BOOST_TEST(!document_.get<int>("string"));
@@ -78,7 +63,7 @@
 {
 	const auto v = document_.get<unsigned>("uint");
 
-	BOOST_TEST(v);
+	BOOST_TEST(v.has_value());
 	BOOST_TEST(*v == 1234U);
 	BOOST_TEST(!document_.get<unsigned>("bool"));
 	BOOST_TEST(!document_.get<unsigned>("int"));
@@ -89,7 +74,7 @@
 {
 	const auto v = document_.get<std::string>("string");
 
-	BOOST_TEST(v);
+	BOOST_TEST(v.has_value());
 	BOOST_TEST(*v == "str");
 	BOOST_TEST(!document_.get<std::string>("bool"));
 	BOOST_TEST(!document_.get<std::string>("int"));