From c0c72b5b62c29f9c220c1eb56273ff2a6bcc273f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Delrieu?= <theo@tanker.io> Date: Wed, 30 Nov 2016 23:16:54 +0100 Subject: [PATCH] rewrite unit-udt: basic usage --- src/json.hpp | 138 ++++++++++++++++++++-- test/src/unit-class_const_iterator.cpp | 36 +++--- test/src/unit-class_iterator.cpp | 36 +++--- test/src/unit-udt.cpp | 156 ++++++++++++++++++++----- 4 files changed, 290 insertions(+), 76 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 2dfa7c02..fc7781ea 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -257,6 +257,30 @@ struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIn RealLimits::is_signed == CompatibleLimits::is_signed; }; +// quickfix, just trying to make things compile before refactoring +template <bool B, typename RealIntegerType, typename CompatibleEnumType> +struct is_compatible_enum_type_impl : std::false_type{}; + +template <typename RealIntegerType, typename CompatibleEnumType> +struct is_compatible_enum_type_impl<true, RealIntegerType, CompatibleEnumType> +{ + using RealLimits = std::numeric_limits<RealIntegerType>; + using CompatibleLimits = std::numeric_limits<typename std::underlying_type<CompatibleEnumType>::type>; + + static constexpr auto value = + CompatibleLimits::is_integer and + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template <typename RealIntegerType, typename CompatibleEnumType> +struct is_compatible_enum_type +{ + static constexpr auto value = is_compatible_enum_type_impl< +// quickfix for all enums + std::is_enum<CompatibleEnumType>::value, RealIntegerType, + CompatibleEnumType>::value; +}; + template <typename RealIntegerType, typename CompatibleNumberIntegerType> struct is_compatible_integer_type { @@ -281,6 +305,7 @@ struct is_compatible_basic_json_type std::is_constructible<typename BasicJson::string_t, T>::value or std::is_same<typename BasicJson::boolean_t, T>::value or is_compatible_array_type<BasicJson, T>::value or + is_compatible_enum_type<T, typename BasicJson::number_integer_t>::value or is_compatible_object_type<typename BasicJson::object_t, T>::value or is_compatible_float_type<typename BasicJson::number_float_t, T>::value or is_compatible_integer_type<typename BasicJson::number_integer_t, @@ -289,6 +314,16 @@ struct is_compatible_basic_json_type T>::value; }; +template <typename T, typename BasicJson, typename PrimitiveIterator> +struct is_basic_json_nested_class +{ + static auto constexpr value = std::is_same<T, typename BasicJson::iterator>::value or + std::is_same<T, typename BasicJson::const_iterator>::value or + std::is_same<T, typename BasicJson::reverse_iterator>::value or + std::is_same<T, typename BasicJson::const_reverse_iterator>::value or + std::is_same<T, PrimitiveIterator>::value or + std::is_same<T, typename BasicJson::json_pointer>::value; +}; // This trait checks if JSONSerializer<T>::from_json(json const&, udt&) exists template <template <typename, typename> class JSONSerializer, typename Json, @@ -344,8 +379,8 @@ public: // those declarations are needed to workaround a MSVC bug related to ADL // (taken from MSVC-Ranges implementation) -//void to_json(); -//void from_json(); +void to_json(); +void from_json(); struct to_json_fn { @@ -524,6 +559,7 @@ class basic_json using basic_json_t = basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>; + class primitive_iterator_t; public: // forward declarations @@ -1589,10 +1625,15 @@ class basic_json enable_if_t<not std::is_base_of<std::istream, uncvref_t<T>>::value and not detail::is_compatible_basic_json_type< uncvref_t<T>, basic_json_t>::value and + not detail::is_basic_json_nested_class<uncvref_t<T>, basic_json_t, primitive_iterator_t>::value and + not std::is_same<uncvref_t<T>, typename basic_json_t::array_t::iterator>::value and +// quickfix +not std::is_enum<uncvref_t<T>>::value and + not std::is_same<uncvref_t<T>, typename basic_json_t::object_t::iterator>::value and detail::has_to_json<JSONSerializer, basic_json, uncvref_t<T>>::value, int> = 0> - explicit basic_json(T &&val) + basic_json(T &&val) { JSONSerializer<uncvref_t<T>>::to_json(*this, std::forward<T>(val)); } @@ -1793,7 +1834,8 @@ class basic_json template < typename CompatibleNumberIntegerType, enable_if_t<detail::is_compatible_integer_type< - number_integer_t, CompatibleNumberIntegerType>::value, + number_integer_t, CompatibleNumberIntegerType>::value or +detail::is_compatible_enum_type<number_integer_t, CompatibleNumberIntegerType>::value, int> = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), @@ -8502,6 +8544,11 @@ class basic_json class primitive_iterator_t { public: + + difference_type get_value() const noexcept + { + return m_it; + } /// set iterator to a defined beginning void set_begin() noexcept { @@ -8526,16 +8573,85 @@ class basic_json return (m_it == end_value); } - /// return reference to the value to change and compare - operator difference_type& () noexcept + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - return m_it; + return lhs.m_it == rhs.m_it; } - /// return value to compare - constexpr operator difference_type () const noexcept + friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - return m_it; + return !(lhs == rhs); + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it <= rhs.m_it; + } + + friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it > rhs.m_it; + } + + friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it >= rhs.m_it; + } + + friend constexpr bool operator+(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it + rhs.m_it; + } + + friend constexpr bool operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) + { + return os << it.m_it; + } + + primitive_iterator_t& operator++() + { + ++m_it; + return *this; + } + + primitive_iterator_t& operator++(int) + { + m_it++; + return *this; + } + + primitive_iterator_t& operator--() + { + --m_it; + return *this; + } + + primitive_iterator_t& operator--(int) + { + m_it--; + return *this; + } + + primitive_iterator_t& operator+=(difference_type n) + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) + { + m_it -= n; + return *this; } private: @@ -9240,7 +9356,7 @@ class basic_json default: { - if (m_it.primitive_iterator == -n) + if (m_it.primitive_iterator.get_value() == -n) { return *m_object; } diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 13ce7c3f..a1f6b827 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -220,20 +220,20 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 0); + CHECK(it.m_it.primitive_iterator.m_it == 0); it++; - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("object") @@ -271,20 +271,20 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 0); + CHECK(it.m_it.primitive_iterator.m_it == 0); ++it; - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("object") @@ -322,18 +322,18 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); } SECTION("number") { json j(17); json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); it--; - CHECK(it.m_it.primitive_iterator == 0); + CHECK(it.m_it.primitive_iterator.m_it == 0); it--; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("object") @@ -371,18 +371,18 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); } SECTION("number") { json j(17); json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); --it; - CHECK(it.m_it.primitive_iterator == 0); + CHECK(it.m_it.primitive_iterator.m_it == 0); --it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("object") diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index 640bc816..1e8a3cec 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -204,20 +204,20 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("number") { json j(17); json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 0); + CHECK(it.m_it.primitive_iterator.m_it == 0); it++; - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("object") @@ -255,20 +255,20 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("number") { json j(17); json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 0); + CHECK(it.m_it.primitive_iterator.m_it == 0); ++it; - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("object") @@ -306,18 +306,18 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); } SECTION("number") { json j(17); json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); it--; - CHECK(it.m_it.primitive_iterator == 0); + CHECK(it.m_it.primitive_iterator.m_it == 0); it--; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("object") @@ -355,18 +355,18 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); } SECTION("number") { json j(17); json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); + CHECK(it.m_it.primitive_iterator.m_it == 1); --it; - CHECK(it.m_it.primitive_iterator == 0); + CHECK(it.m_it.primitive_iterator.m_it == 0); --it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + CHECK((it.m_it.primitive_iterator.m_it != 0 and it.m_it.primitive_iterator.m_it != 1)); } SECTION("object") diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index cd9ec1c4..28443344 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -37,35 +37,35 @@ namespace udt { struct age { - int val; + int m_val; }; struct name { - std::string val; + std::string m_val; }; struct address { - std::string val; + std::string m_val; }; struct person { - age age; - name name; + age m_age; + name m_name; }; struct contact { - person person; - address address; + person m_person; + address m_address; }; struct contact_book { - name book_name; - std::vector<contact> contacts; + name m_book_name; + std::vector<contact> m_contacts; }; } @@ -74,39 +74,105 @@ namespace udt { void to_json(nlohmann::json& j, age a) { - j = a.val; + j = a.m_val; } void to_json(nlohmann::json& j, name const& n) { - j = n.val; + j = n.m_val; } void to_json(nlohmann::json& j, person const& p) { using nlohmann::json; - j = json{{"age", json{p.age}}, {"name", json{p.name}}}; - - // this unfortunately does not compile ... - // j["age"] = p.age; - // j["name"] = p.name; + j = json{{"age", json{p.m_age}}, {"name", json{p.m_name}}}; } void to_json(nlohmann::json& j, address const& a) { - j = a.val; + j = a.m_val; } void to_json(nlohmann::json& j, contact const& c) { using nlohmann::json; - j = json{{"person", json{c.person}}, {"address", json{c.address}}}; + j = json{{"person", json{c.m_person}}, {"address", json{c.m_address}}}; } void to_json(nlohmann::json& j, contact_book const& cb) { using nlohmann::json; - j = json{{"name", json{cb.book_name}}, {"contacts", cb.contacts}}; + j = json{{"name", json{cb.m_book_name}}, {"contacts", cb.m_contacts}}; + } + + // operators + bool operator==(age lhs, age rhs) + { + return lhs.m_val == rhs.m_val; + } + + bool operator==(address const &lhs, address const &rhs) + { + return lhs.m_val == rhs.m_val; + } + + bool operator==(name const &lhs, name const &rhs) + { + return lhs.m_val == rhs.m_val; + } + + bool operator==(person const &lhs, person const &rhs) + { + return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age); + } + + bool operator==(contact const &lhs, contact const &rhs) + { + return std::tie(lhs.m_person, lhs.m_address) == + std::tie(rhs.m_person, rhs.m_address); + } + + bool operator==(contact_book const &lhs, contact_book const &rhs) + { + return std::tie(lhs.m_book_name, lhs.m_contacts) == + std::tie(rhs.m_book_name, rhs.m_contacts); + } +} + +// from_json methods for default basic_json +namespace udt +{ + void from_json(nlohmann::json const& j, age &a) + { + a.m_val = j.get<int>(); + } + + void from_json(nlohmann::json const& j, name &n) + { + n.m_val = j.get<std::string>(); + } + + void from_json(nlohmann::json const& j, person &p) + { + p.m_age = j["age"].get<age>(); + p.m_name = j["name"].get<name>(); + } + + void from_json(nlohmann::json const &j, address &a) + { + a.m_val = j.get<std::string>(); + } + + void from_json(nlohmann::json const& j, contact &c) + { + c.m_person = j["person"].get<person>(); + c.m_address = j["address"].get<address>(); + } + + void from_json(nlohmann::json const&j, contact_book &cb) + { + cb.m_book_name = j["name"].get<name>(); + cb.m_contacts = j["contacts"].get<std::vector<contact>>(); } } @@ -114,17 +180,49 @@ TEST_CASE("basic usage", "[udt]") { using nlohmann::json; + // a bit narcissic maybe :) ? + const udt::age a{23}; + const udt::name n{"theo"}; + const udt::person sfinae_addict{a, n}; + const udt::address addr{"Paris"}; + const udt::contact cpp_programmer{sfinae_addict, addr}; + const udt::contact_book book{{"C++"}, {cpp_programmer, cpp_programmer}}; + SECTION("conversion to json via free-functions") { - udt::age a{23}; - - CHECK(json{a} == json{23}); - - // a bit narcissic maybe :) ? - udt::name n{"theo"}; - CHECK(json{n} == json{"theo"}); - - udt::person sfinae_addict{a, n}; + CHECK(json{a} == json(23)); + CHECK(json{n} == json("theo")); CHECK(json{sfinae_addict} == R"({"name":"theo", "age":23})"_json); + CHECK(json("Paris") == json{addr}); + CHECK(json{cpp_programmer} == + R"({"person" : {"age":23, "name":"theo"}, "address":"Paris"})"_json); + + CHECK( + json{book} == + R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo"}, "address":"Paris"}, {"person" : {"age":23, "name":"theo"}, "address":"Paris"}]})"_json); } -} \ No newline at end of file + + SECTION("conversion from json via free-functions") + { + const auto big_json = + R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo"}, "address":"Paris"}, {"person" : {"age":23, "name":"theo"}, "address":"Paris"}]})"_json; + const auto parsed_book = big_json.get<udt::contact_book>(); + const auto book_name = big_json["name"].get<udt::name>(); + const auto contacts = big_json["contacts"].get<std::vector<udt::contact>>(); + const auto contact_json = big_json["contacts"].at(0); + const auto contact = contact_json.get<udt::contact>(); + const auto person = contact_json["person"].get<udt::person>(); + const auto address = contact_json["address"].get<udt::address>(); + const auto age = contact_json["person"]["age"].get<udt::age>(); + const auto name = contact_json["person"]["name"].get<udt::name>(); + + CHECK(age == a); + CHECK(name == n); + CHECK(address == addr); + CHECK(person == sfinae_addict); + CHECK(contact == cpp_programmer); + CHECK(contacts == book.m_contacts); + CHECK(book_name == udt::name{"C++"}); + CHECK(book == parsed_book); + } +}