Browse Source

Util: Implement copy-and-swap idiom and the Rule of Five in Value

master
Riyyi 2 years ago
parent
commit
3e69abdfa0
  1. 1
      src/util/json/array.h
  2. 1
      src/util/json/object.h
  3. 38
      src/util/json/tojson.h
  4. 157
      src/util/json/value.cpp
  5. 22
      src/util/json/value.h

1
src/util/json/array.h

@ -30,6 +30,7 @@ public:
{ {
} }
void clear() { m_elements.clear(); }
void emplace_back(Value element); void emplace_back(Value element);
void reserve(size_t size) { m_elements.reserve(size); } void reserve(size_t size) { m_elements.reserve(size); }

1
src/util/json/object.h

@ -27,6 +27,7 @@ public:
{ {
} }
void clear() { m_members.clear(); }
void emplace(const std::string& name, Value value); void emplace(const std::string& name, Value value);
Value& operator[](const std::string& name); Value& operator[](const std::string& name);

38
src/util/json/tojson.h

@ -26,7 +26,7 @@ struct jsonConstructor {
template<typename Json> template<typename Json>
static void construct(Json& json, bool boolean) static void construct(Json& json, bool boolean)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::Bool; json.m_type = Json::Type::Bool;
json.m_value.boolean = boolean; json.m_value.boolean = boolean;
} }
@ -34,7 +34,7 @@ struct jsonConstructor {
template<typename Json> template<typename Json>
static void construct(Json& json, int number) static void construct(Json& json, int number)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::Number; json.m_type = Json::Type::Number;
json.m_value.number = (double)number; json.m_value.number = (double)number;
} }
@ -42,7 +42,7 @@ struct jsonConstructor {
template<typename Json> template<typename Json>
static void construct(Json& json, double number) static void construct(Json& json, double number)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::Number; json.m_type = Json::Type::Number;
json.m_value.number = number; json.m_value.number = number;
} }
@ -50,7 +50,7 @@ struct jsonConstructor {
template<typename Json> template<typename Json>
static void construct(Json& json, const char* string) static void construct(Json& json, const char* string)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::String; json.m_type = Json::Type::String;
json.m_value.string = new std::string(string); json.m_value.string = new std::string(string);
} }
@ -58,7 +58,7 @@ struct jsonConstructor {
template<typename Json> template<typename Json>
static void construct(Json& json, const std::string& string) static void construct(Json& json, const std::string& string)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::String; json.m_type = Json::Type::String;
json.m_value.string = new std::string(string); json.m_value.string = new std::string(string);
} }
@ -66,7 +66,7 @@ struct jsonConstructor {
template<typename Json> template<typename Json>
static void construct(Json& json, const Array& array) static void construct(Json& json, const Array& array)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::Array; json.m_type = Json::Type::Array;
json.m_value.array = new Array(array); json.m_value.array = new Array(array);
} }
@ -74,7 +74,7 @@ struct jsonConstructor {
template<typename Json, typename T> template<typename Json, typename T>
static void construct(Json& json, const std::vector<T>& array) static void construct(Json& json, const std::vector<T>& array)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::Array; json.m_type = Json::Type::Array;
json.m_value.array = new Array; json.m_value.array = new Array;
json.m_value.array->reserve(array.size()); json.m_value.array->reserve(array.size());
@ -86,7 +86,7 @@ struct jsonConstructor {
template<typename Json> template<typename Json>
static void construct(Json& json, const Object& object) static void construct(Json& json, const Object& object)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::Object; json.m_type = Json::Type::Object;
json.m_value.object = new Object(object); json.m_value.object = new Object(object);
} }
@ -94,7 +94,7 @@ struct jsonConstructor {
template<typename Json, typename T> template<typename Json, typename T>
static void construct(Json& json, const std::map<std::string, T>& object) static void construct(Json& json, const std::map<std::string, T>& object)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::Object; json.m_type = Json::Type::Object;
json.m_value.object = new Object; json.m_value.object = new Object;
for (const auto& [name, value] : object) { for (const auto& [name, value] : object) {
@ -105,7 +105,7 @@ struct jsonConstructor {
template<typename Json, typename T> template<typename Json, typename T>
static void construct(Json& json, const std::unordered_map<std::string, T>& object) static void construct(Json& json, const std::unordered_map<std::string, T>& object)
{ {
json.clear(); json.destroy();
json.m_type = Json::Type::Object; json.m_type = Json::Type::Object;
json.m_value.object = new Object; json.m_value.object = new Object;
for (const auto& [name, value] : object) { for (const auto& [name, value] : object) {
@ -143,14 +143,14 @@ constexpr const auto& toJson = Detail::staticConst<Detail::toJsonFunction>; // N
// Customization Points // Customization Points
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
// Json::fromJson is a function object, the type of which is // Json::toJson is a function object, the type of which is
// Json::Detail::fromJsonFunction. In the Json::Detail namespace are the // Json::Detail::toJsonFunction. In the Json::Detail namespace are the toJson
// fromJson free functions. The function call operator of fromJsonFunction makes // free functions. The function call operator of toJsonFunction makes an
// an unqualified call to fromJson which, since it shares the Detail namespace // unqualified call to toJson which, since it shares the Detail namespace with
// with the fromJson free functions, will consider those in addition to any // the toJson free functions, will consider those in addition to any overloads
// overloads that are found by argument-dependent lookup. // that are found by argument-dependent lookup.
// Variable templates are linked externally, therefor every translation unit // Variable templates are linked externally, therefor every translation unit
// will see the same address for Detail::staticConst<Detail::fromJsonFunction>. // will see the same address for Detail::staticConst<Detail::toJsonFunction>.
// Since Json::fromJson is a reference to the variable template, it too will // Since Json::toJson is a reference to the variable template, it too will have
// have the same address in all translation units. // the same address in all translation units.

157
src/util/json/value.cpp

@ -9,7 +9,7 @@
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
#include <iostream> // istream, ostream #include <iostream> // istream, ostream
#include <string> #include <string>
#include <utility> // move #include <utility> // move, swap
#include "util/json/array.h" #include "util/json/array.h"
#include "util/json/job.h" #include "util/json/job.h"
@ -27,7 +27,26 @@ Value::Value(std::nullptr_t)
Value::Value(Type type) Value::Value(Type type)
: m_type(type) : m_type(type)
{ {
create(); switch (m_type) {
case Type::Bool:
m_value.boolean = false;
break;
case Type::Number:
m_value.number = 0.0;
break;
case Type::String:
m_value.string = new std::string;
break;
case Type::Array:
m_value.array = new Array;
break;
case Type::Object:
m_value.object = new Object;
break;
case Type::Null:
default:
break;
}
} }
Value::Value(const std::initializer_list<Value>& values) Value::Value(const std::initializer_list<Value>& values)
@ -55,29 +74,87 @@ Value::Value(const std::initializer_list<Value>& values)
// Copy constructor // Copy constructor
Value::Value(const Value& other) Value::Value(const Value& other)
: m_type(other.m_type)
{ {
copyFrom(other); switch (m_type) {
case Type::Bool:
m_value.boolean = other.m_value.boolean;
break;
case Type::Number:
m_value.number = other.m_value.number;
break;
case Type::String:
m_value.string = new std::string(*other.m_value.string);
break;
case Type::Array:
m_value.array = new Array(*other.m_value.array);
break;
case Type::Object:
m_value.object = new Object(*other.m_value.object);
break;
case Type::Null:
default:
break;
}
} }
// Assignment operator // Move constructor
Value& Value::operator=(const Value& other) Value::Value(Value&& other) noexcept
: Value(Type::Null) // Initialize via default construction
{ {
if (this != &other) { // Allow std::swap as a fallback on ADL failure
clear(); using std::swap;
copyFrom(other); // Unqualified call to swap, allow ADL to operate and find best match
swap(*this, other);
} }
// Copy assignment
// Move assignment
Value& Value::operator=(Value other)
{
// Allow std::swap as a fallback on ADL failure
using std::swap;
// Unqualified call to swap, allow ADL to operate and find best match
swap(*this, other);
return *this; return *this;
} }
void swap(Value& left, Value& right) noexcept
{
std::swap(left.m_type, right.m_type);
std::swap(left.m_value, right.m_value);
}
// ------------------------------------------ // ------------------------------------------
Value Value::parse(const std::string& input) void Value::clear()
{ {
Job job(input); switch (m_type) {
Value value = job.fire(); case Type::Bool:
m_value.boolean = false;
break;
case Type::Number:
m_value.number = 0.0;
break;
case Type::String:
m_value.string->clear();
break;
case Type::Array:
m_value.array->clear();
break;
case Type::Object:
m_value.object->clear();
break;
case Type::Null:
default:
break;
}
}
return value; Value Value::parse(const std::string& input)
{
return Job(input).fire();
} }
std::string Value::dump(const uint32_t indent, const char indentCharacter) const std::string Value::dump(const uint32_t indent, const char indentCharacter) const
@ -190,45 +267,21 @@ size_t Value::size() const
switch (m_type) { switch (m_type) {
case Type::Null: case Type::Null:
return 0; return 0;
case Type::Bool:
case Type::Number:
case Type::String:
return 1;
case Type::Array: case Type::Array:
return m_value.array->size(); return m_value.array->size();
case Type::Object: case Type::Object:
return m_value.object->size(); return m_value.object->size();
default:
return 1;
}
}
// ------------------------------------------
void Value::create()
{
switch (m_type) {
case Type::Bool: case Type::Bool:
m_value.boolean = false;
break;
case Type::Number: case Type::Number:
m_value.number = 0.0;
break;
case Type::String: case Type::String:
m_value.string = new std::string;
break;
case Type::Array:
m_value.array = new Array;
break;
case Type::Object:
m_value.object = new Object;
break;
default: default:
break; return 1;
} }
} }
void Value::clear() // ------------------------------------------
void Value::destroy()
{ {
switch (m_type) { switch (m_type) {
case Type::String: case Type::String:
@ -240,31 +293,9 @@ void Value::clear()
case Type::Object: case Type::Object:
delete m_value.object; delete m_value.object;
break; break;
default: case Type::Null:
break;
}
}
void Value::copyFrom(const Value& other)
{
m_type = other.m_type;
switch (m_type) {
case Type::Bool: case Type::Bool:
m_value.boolean = other.m_value.boolean;
break;
case Type::Number: case Type::Number:
m_value.number = other.m_value.number;
break;
case Type::String:
m_value.string = new std::string(*other.m_value.string);
break;
case Type::Array:
m_value.array = new Array(*other.m_value.array);
break;
case Type::Object:
m_value.object = new Object(*other.m_value.object);
break;
default: default:
break; break;
} }

22
src/util/json/value.h

@ -38,6 +38,8 @@ public:
Object, // {} Object, // {}
}; };
// --------------------------------------
// Constructors // Constructors
Value(std::nullptr_t = nullptr); Value(std::nullptr_t = nullptr);
Value(Type type); Value(Type type);
@ -48,20 +50,26 @@ public:
toJson(*this, std::forward<T>(value)); toJson(*this, std::forward<T>(value));
} }
// Destructor // Rule of Five:
virtual ~Value() { clear(); }
// Copy constructor // Copy constructor
Value(const Value& other); Value(const Value& other);
// Move constructor
Value(Value&& other) noexcept;
// Copy assignment
// Move assignment
Value& operator=(Value other);
// Destructor
virtual ~Value() { destroy(); }
// Assignment operator friend void swap(Value& left, Value& right) noexcept;
Value& operator=(const Value& other);
// -------------------------------------- // --------------------------------------
static Value parse(const std::string& input); static Value parse(const std::string& input);
std::string dump(const uint32_t indent = 0, const char indentCharacter = ' ') const; std::string dump(const uint32_t indent = 0, const char indentCharacter = ' ') const;
void clear();
void emplace_back(Value value); void emplace_back(Value value);
void emplace(const std::string& key, Value value); void emplace(const std::string& key, Value value);
@ -109,9 +117,7 @@ public:
const Object& asObject() const { return *m_value.object; } const Object& asObject() const { return *m_value.object; }
private: private:
void create(); void destroy();
void clear();
void copyFrom(const Value& other);
Type m_type { Type::Null }; Type m_type { Type::Null };

Loading…
Cancel
Save