Mercurial > code
changeset 478:c7825b8c6145
Json:
- Finalize iterators,
- Add more tests,
- Add more asserts.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 10 Nov 2015 14:11:14 +0100 |
parents | b681299e6987 |
children | c7d83d2b462b |
files | C++/modules/Json/Json.cpp C++/modules/Json/Json.h C++/tests/Json/main.cpp |
diffstat | 3 files changed, 330 insertions(+), 184 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Json/Json.cpp Mon Nov 09 16:52:38 2015 +0100 +++ b/C++/modules/Json/Json.cpp Tue Nov 10 14:11:14 2015 +0100 @@ -112,26 +112,23 @@ void Value::copy(const Value &other) { switch (other.m_type) { - case Type::String: - new (&m_string) std::string(); - m_string = std::move(other.m_string); + case Type::Array: + new (&m_array) std::deque<Value>(other.m_array); break; - case Type::Real: - m_number = other.m_number; + case Type::Boolean: + m_boolean = other.m_boolean; break; case Type::Int: m_integer = other.m_integer; break; - case Type::Boolean: - m_boolean = other.m_boolean; + case Type::Object: + new (&m_object) std::map<std::string, Value>(other.m_object); break; - case Type::Object: - new (&m_object) std::map<std::string, Value>(); - m_object = std::move(other.m_object); + case Type::Real: + m_number = other.m_number; break; - case Type::Array: - new (&m_array) std::deque<Value>(); - m_array = std::move(other.m_array); + case Type::String: + new (&m_string) std::string(other.m_string); break; default: break; @@ -143,23 +140,23 @@ void Value::move(Value &&other) { switch (other.m_type) { - case Type::String: - new (&m_string) std::string(std::move(other.m_string)); + case Type::Array: + new (&m_array) std::deque<Value>(std::move(other.m_array)); break; - case Type::Real: - m_number = other.m_number; + case Type::Boolean: + m_boolean = other.m_boolean; break; case Type::Int: m_integer = other.m_integer; break; - case Type::Boolean: - m_boolean = other.m_boolean; - break; case Type::Object: new (&m_object) std::map<std::string, Value>(std::move(other.m_object)); break; - case Type::Array: - new (&m_array) std::deque<Value>(std::move(other.m_array)); + case Type::Real: + m_number = other.m_number; + break; + case Type::String: + new (&m_string) std::string(std::move(other.m_string)); break; default: break; @@ -171,24 +168,24 @@ Value::Value(Type type) : m_type{type} { - switch (type) { - case Type::String: - new (&m_string) std::string(); - break; + switch (m_type) { case Type::Array: new (&m_array) std::deque<Value>(); break; + case Type::Boolean: + m_boolean = false; + break; + case Type::Int: + m_integer = 0; + break; case Type::Object: new (&m_object) std::map<std::string, Value>(); break; case Type::Real: m_number = 0; break; - case Type::Int: - m_integer = 0; - break; - case Type::Boolean: - m_boolean = false; + case Type::String: + new (&m_string) std::string(); break; default: break; @@ -198,14 +195,14 @@ Value::~Value() { switch (m_type) { - case Type::String: - m_string.~basic_string(); + case Type::Array: + m_array.~deque<Value>(); break; case Type::Object: m_object.~map<std::string, Value>(); break; - case Type::Array: - m_array.~deque<Value>(); + case Type::String: + m_string.~basic_string(); break; default: break;
--- a/C++/modules/Json/Json.h Mon Nov 09 16:52:38 2015 +0100 +++ b/C++/modules/Json/Json.h Tue Nov 10 14:11:14 2015 +0100 @@ -185,9 +185,20 @@ void copy(const Value &); void move(Value &&); + /** + * @class BaseIterator + * @brief This is the base class for iterator and const_iterator + * + * This iterator works for both arrays and objects. Because of that purpose, it is only available + * as forward iterator. + * + * When iterator comes from an object, you can use key() otherwise you can use index(). + */ template <typename ValueType, typename ArrayIteratorType, typename ObjectIteratorType> - class BaseIterator { + class BaseIterator : public std::iterator<std::forward_iterator_tag, ValueType> { private: + friend class Value; + ValueType &m_value; ArrayIteratorType m_ita; ObjectIteratorType m_itm; @@ -201,17 +212,19 @@ } } - public: - explicit BaseIterator(ValueType &value, bool end = false) + BaseIterator(ValueType &value, ObjectIteratorType it) : m_value(value) + , m_itm(it) { - if (m_value.isObject()) { - m_itm = (end) ? m_value.m_object.end() : m_value.m_object.begin(); - } else { - m_ita = (end) ? m_value.m_array.end() : m_value.m_array.begin(); - } } + BaseIterator(ValueType &value, ArrayIteratorType it) + : m_value(value) + , m_ita(it) + { + } + + public: /** * Get the iterator key (for objects). * @@ -253,11 +266,7 @@ assert((m_value.isArray() && m_ita != m_value.m_array.end()) || (m_value.isObject() && m_itm != m_value.m_object.end())); - if (m_value.isObject()) { - return m_itm->second; - } - - return *m_ita; + return (m_value.m_type == Type::Object) ? m_itm->second : *m_ita; } /** @@ -268,7 +277,10 @@ */ inline ValueType *operator->() noexcept { - return &(*this); + assert((m_value.isArray() && m_ita != m_value.m_array.end()) || + (m_value.isObject() && m_itm != m_value.m_object.end())); + + return (m_value.m_type == Type::Object) ? &m_itm->second : &(*m_ita); } /** @@ -332,7 +344,14 @@ } }; + /** + * Forward iterator. + */ using iterator = BaseIterator<Value, typename std::deque<Value>::iterator, typename std::map<std::string, Value>::iterator>; + + /** + * Const forward iterator. + */ using const_iterator = BaseIterator<const Value, typename std::deque<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>; public: @@ -344,22 +363,6 @@ } /** - * Construct a value from a buffer. - * - * @param buffer the text - * @throw Error on errors - */ - Value(const Buffer &buffer); - - /** - * Construct a value from a file. - * - * @param file the file - * @throw Error on errors - */ - Value(const File &file); - - /** * Create a value with a specified type, this is usually only needed when you want to create an object or * an array. * @@ -407,7 +410,7 @@ inline Value(const char *value) : m_type{Type::String} { - new (&m_string) std::string{value}; + new (&m_string) std::string{value ? value : ""}; } /** @@ -433,6 +436,50 @@ } /** + * Create an object from a map. + * + * @param values the values + * @see fromObject + */ + inline Value(std::map<std::string, Value> values) + : Value{Type::Object} + { + for (const auto &pair : values) { + insert(pair.first, pair.second); + } + } + + /** + * Create an array from a deque. + * + * @param values the values + * @see fromArray + */ + inline Value(std::deque<Value> values) + : Value{Type::Array} + { + for (Value value : values) { + append(std::move(value)); + } + } + + /** + * Construct a value from a buffer. + * + * @param buffer the text + * @throw Error on errors + */ + Value(const Buffer &buffer); + + /** + * Construct a value from a file. + * + * @param file the file + * @throw Error on errors + */ + Value(const File &file); + + /** * Move constructor. * * @param other the value to move from @@ -482,34 +529,82 @@ */ ~Value(); + /** + * Get an iterator to the beginning. + * + * @pre must be an array or object + * @return the iterator + */ inline iterator begin() noexcept { - return iterator(*this); + assert(isArray() || isObject()); + + return m_type == Type::Object ? iterator(*this, m_object.begin()) : iterator(*this, m_array.begin()); } + /** + * Overloaded function. + * + * @pre must be an array or object + * @return the iterator + */ inline const_iterator begin() const noexcept { - return const_iterator(*this); + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(*this, m_object.begin()) : const_iterator(*this, m_array.begin()); } + /** + * Overloaded function. + * + * @pre must be an array or object + * @return the iterator + */ inline const_iterator cbegin() const noexcept { - return const_iterator(*this); + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(*this, m_object.cbegin()) : const_iterator(*this, m_array.cbegin()); } + /** + * Get an iterator to the end. + * + * @pre must be an array or object + * @return the iterator + */ inline iterator end() noexcept { - return iterator(*this, true); + assert(isArray() || isObject()); + + return m_type == Type::Object ? iterator(*this, m_object.end()) : iterator(*this, m_array.end()); } + /** + * Get an iterator to the end. + * + * @pre must be an array or object + * @return the iterator + */ inline const_iterator end() const noexcept { - return const_iterator(*this, true); + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(*this, m_object.end()) : const_iterator(*this, m_array.end()); } + /** + * Get an iterator to the end. + * + * @pre must be an array or object + * @return the iterator + */ inline const_iterator cend() const noexcept { - return const_iterator(*this, true); + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(*this, m_object.cend()) : const_iterator(*this, m_array.cend()); } /** @@ -921,6 +1016,48 @@ } /** + * Get a value from the object. + * + * @pre must be an object + * @param name the value key + * @return the value + */ + inline const Value &operator[](const std::string &name) const + { + assert(isObject()); + + return m_object.at(name); + } + + /** + * Find a value by key. + * + * @pre must be an object + * @param key the property key + * @return the iterator or past the end if not found + */ + inline iterator find(const std::string &key) + { + assert(isObject()); + + return iterator(*this, m_object.find(key)); + } + + /** + * Overloaded function. + * + * @pre must be an object + * @param key the property key + * @return the iterator or past the end if not found + */ + inline const_iterator find(const std::string &key) const + { + assert(isObject()); + + return const_iterator(*this, m_object.find(key)); + } + + /** * Insert a new value. * * @pre must be an object @@ -929,6 +1066,8 @@ */ inline void insert(std::string name, const Value &value) { + assert(isObject()); + m_object.insert({std::move(name), value}); } @@ -941,27 +1080,35 @@ */ inline void insert(std::string name, Value &&value) { + assert(isObject()); + m_object.insert({std::move(name), std::move(value)}); } /** * Check if a value exists. * + * @pre must be an object * @param key the key value * @return true if exists */ inline bool contains(const std::string &key) const noexcept { + assert(isObject()); + return m_object.find(key) != m_object.end(); } /** * Remove a value of the specified key. * + * @pre must be an object * @param key the value key */ inline void erase(const std::string &key) { + assert(isObject()); + m_object.erase(key); } }; @@ -974,6 +1121,28 @@ */ std::string escape(const std::string &input); +/** + * Convenient function for creating array from initializer list. + * + * @param values the values + * @return the array + */ +inline Value array(std::initializer_list<Value> values) +{ + return Value(std::deque<Value>(values.begin(), values.end())); +} + +/** + * Convenient function for creating object from initializer list. + * + * @param values the values + * @return the object + */ +inline Value object(std::initializer_list<std::pair<std::string, Value>> values) +{ + return Value(std::map<std::string, Value>(values.begin(), values.end())); +} + } // !json #endif // !_JSON_H_
--- a/C++/tests/Json/main.cpp Mon Nov 09 16:52:38 2015 +0100 +++ b/C++/tests/Json/main.cpp Tue Nov 10 14:11:14 2015 +0100 @@ -23,9 +23,10 @@ #include "Json.h" -/* -------------------------------------------------------- +/* * Miscellaneous - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(Misc, copy) { @@ -87,9 +88,10 @@ ASSERT_EQ(expected, json::escape(input)); } -/* -------------------------------------------------------- +/* * json::Value constructors - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(Constructors, null) { @@ -154,9 +156,10 @@ } } -/* -------------------------------------------------------- +/* * Object - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(Object, set) { @@ -215,15 +218,13 @@ } } -#if 0 - TEST(Object, valueOr) { try { - json::Object object{ + json::Value object = json::object({ { "x", 10 }, { "y", 20 } - }; + }); ASSERT_EQ(10, object.valueOr("x", -9999).toInt()); ASSERT_EQ(20, object.valueOr("y", -9999).toInt()); @@ -236,10 +237,10 @@ TEST(ObjectInitializer, simple) { try { - json::Object object{ + json::Value object = json::object({ { "username", "jean" }, { "age", 99 } - }; + }); ASSERT_EQ(2, static_cast<int>(object.size())); ASSERT_EQ("jean", object["username"].toString()); @@ -252,15 +253,15 @@ TEST(ObjectInitializer, deep) { try { - json::Object object{ + json::Value object = json::object({ { "username", "jean" }, { "age", 99 }, - { "network", json::Object{ + { "network", json::object({ { "port", 9999 }, { "host", "localhost" } - } + }) } - }; + }); // First ASSERT_EQ(3, static_cast<int>(object.size())); @@ -268,7 +269,7 @@ ASSERT_EQ(99, object["age"].toInt()); // Second - json::Object network = object["network"].toObject(); + const json::Value &network = object["network"]; ASSERT_TRUE(network.isObject()); ASSERT_EQ(2, static_cast<int>(network.size())); ASSERT_EQ(9999, network["port"].toInt()); @@ -278,11 +279,10 @@ } } -#endif - -/* -------------------------------------------------------- +/* * Array - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(Array, push) { @@ -380,12 +380,10 @@ } } -#if 0 - TEST(Array, valueOr) { try { - json::Array array{-10, -20}; + json::Value array = json::array({-10, -20}); ASSERT_EQ(-10, array.valueOr(0, -9999).toInt()); ASSERT_EQ(-20, array.valueOr(1, -9999).toInt()); @@ -398,7 +396,7 @@ TEST(ArrayInitializer, simple) { try { - json::Array array{123, true, "hello"}; + json::Value array = json::array({123, true, "hello"}); ASSERT_EQ(3, static_cast<int>(array.size())); ASSERT_EQ(123, array[0].toInt()); @@ -412,16 +410,16 @@ TEST(ArrayInitializer, deep) { try { - json::Array array{ + json::Value array = json::array({ 123, true, "hello", - json::Array{ + json::array({ 321, false, "olleh" - } - }; + }) + }); // First ASSERT_EQ(4, static_cast<int>(array.size())); @@ -430,7 +428,7 @@ ASSERT_EQ("hello", array[2].toString()); // Second - json::Array array2 = array[3].toArray(); + const json::Value &array2 = array[3]; ASSERT_TRUE(array.isArray()); ASSERT_EQ(3, static_cast<int>(array2.size())); ASSERT_EQ(321, array2[0].toInt()); @@ -441,11 +439,10 @@ } } -#endif - -/* -------------------------------------------------------- +/* * I/O - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(FileRead, simple) { @@ -491,22 +488,21 @@ } } -#if 0 - -/* -------------------------------------------------------- +/* * Object read - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ class ObjectRead : public testing::Test { protected: - json::Object m_object; - json::Object m_objectAll; + json::Value m_object; + json::Value m_objectAll; public: ObjectRead() + : m_object(json::File{"Json/object.json"}) + , m_objectAll(json::File{"Json/object-all.json"}) { - m_object = json::Document(json::File{"Json/object.json"}).toObject(); - m_objectAll = json::Document(json::File{"Json/object-all.json"}).toObject(); } }; @@ -556,20 +552,21 @@ } } -/* -------------------------------------------------------- +/* * Array read - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ class ArrayRead : public testing::Test { protected: - json::Array m_array; - json::Array m_arrayAll; + json::Value m_array; + json::Value m_arrayAll; public: ArrayRead() + : m_array(json::File{"Json/array.json"}) + , m_arrayAll(json::File{"Json/array-all.json"}) { - m_array = json::Document(json::File{"Json/array.json"}).toArray(); - m_arrayAll = json::Document(json::File{"Json/array-all.json"}).toArray(); } }; @@ -613,13 +610,14 @@ } } -/* -------------------------------------------------------- +/* * Object iterators - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ class ObjectIteratorsTest : public testing::Test { protected: - json::Object m_object; + json::Value m_object{json::Type::Object}; public: ObjectIteratorsTest() @@ -641,8 +639,9 @@ auto end = m_object.end(); while (it != end) { - values.insert({it->first, it->second}); - result.insert((*it++).first); + values.insert({it.key(), *it}); + result.insert(it.key()); + it++; } ASSERT_EQ(expected, result); @@ -660,8 +659,9 @@ auto end = m_object.cend(); while (it != end) { - values.insert({it->first, it->second}); - result.insert((*it++).first); + values.insert({it.key(), *it}); + result.insert(it.key()); + it++; } ASSERT_EQ(expected, result); @@ -676,23 +676,39 @@ // Assign (non const) { auto it = m_object.begin(); - auto key = it->first; + auto key = it.key(); - it->second = json::Value("CHANGED"); + *it = json::Value("CHANGED"); ASSERT_EQ("CHANGED", m_object[key].toString()); - ASSERT_EQ("CHANGED", it->second.toString()); + ASSERT_EQ("CHANGED", it->toString()); ASSERT_EQ(3, static_cast<int>(m_object.size())); } } -/* -------------------------------------------------------- +TEST_F(ObjectIteratorsTest, find) +{ + auto it = m_object.find("integer"); + + ASSERT_TRUE(it != m_object.end()); + ASSERT_EQ(1, it->toInt()); +} + +TEST_F(ObjectIteratorsTest, find2) +{ + auto it = m_object.find("not exists"); + + ASSERT_TRUE(it == m_object.end()); +} + +/* * Array iterators - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ class ArrayIteratorsTest : public testing::Test { protected: - json::Array m_array; + json::Value m_array{json::Type::Array}; public: ArrayIteratorsTest() @@ -709,68 +725,34 @@ { auto it = m_array.begin(); + // 1 ASSERT_EQ(1, (*it).toInt()); ASSERT_EQ(1, it->toInt()); - ASSERT_EQ("hello", it[1].toString()); - ASSERT_TRUE(it[2].toBool()); - - auto it2 = it + 1; - ASSERT_EQ(1, it2[-1].toInt()); - ASSERT_EQ("hello", it2->toString()); - ASSERT_TRUE(it2[1].toBool()); - - auto it3 = it; - ASSERT_TRUE(it2 != it); - ASSERT_FALSE(it3 != it); - ASSERT_FALSE(it2 == it); - ASSERT_TRUE(it3 == it); - - ASSERT_TRUE(it3 >= it); - ASSERT_TRUE(it2 >= it); + // "hello" + it++; + ASSERT_EQ("hello", it->toString()); - ASSERT_TRUE(it2 > it); - ASSERT_FALSE(it3 > it); - - ASSERT_FALSE(it2 <= it); - ASSERT_TRUE(it3 <= it); - - ASSERT_FALSE(it2 < it); - ASSERT_FALSE(it3 < it); + // true + it++; + ASSERT_TRUE(it->toBool()); } // Read only (const) { auto it = m_array.cbegin(); + // 1 ASSERT_EQ(1, (*it).toInt()); ASSERT_EQ(1, it->toInt()); - ASSERT_EQ("hello", it[1].toString()); - ASSERT_TRUE(it[2].toBool()); - - auto it2 = it + 1; - ASSERT_EQ(1, it2[-1].toInt()); - ASSERT_EQ("hello", it2->toString()); - ASSERT_TRUE(it2[1].toBool()); - - auto it3 = it; - ASSERT_TRUE(it2 != it); - ASSERT_FALSE(it3 != it); - ASSERT_FALSE(it2 == it); - ASSERT_TRUE(it3 == it); - - ASSERT_TRUE(it3 >= it); - ASSERT_TRUE(it2 >= it); + // "hello" + it++; + ASSERT_EQ("hello", it->toString()); - ASSERT_TRUE(it2 > it); - ASSERT_FALSE(it3 > it); - - ASSERT_FALSE(it2 <= it); - ASSERT_TRUE(it3 <= it); - - ASSERT_FALSE(it2 < it); - ASSERT_FALSE(it3 < it); + // true + it++; + ASSERT_TRUE(it->toBool()); } } @@ -790,7 +772,7 @@ TEST_F(ArrayIteratorsTest, castToRef) { - json::Array array{1, 2, 3}; + json::Value array = json::array({1, 2, 3}); int i = 1; for (const json::Value &v : array) { @@ -798,8 +780,6 @@ } } -#endif - int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);