diff --git a/src/util/json/array.h b/src/util/json/array.h index 5ab64ca..e3456e0 100644 --- a/src/util/json/array.h +++ b/src/util/json/array.h @@ -31,6 +31,7 @@ public: } void emplace_back(Value value); + void reserve(size_t size) { m_values.reserve(size); } Value& operator[](size_t index); diff --git a/src/util/json/tojson.h b/src/util/json/tojson.h new file mode 100644 index 0000000..615aeb7 --- /dev/null +++ b/src/util/json/tojson.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2022 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#ifndef JSON_TO_JSON_H +#define JSON_TO_JSON_H + +#include // assert +#include // nullptr_t +#include +#include +#include +#include // forward + +#include "util/json/array.h" +#include "util/json/conversion.h" +#include "util/json/object.h" + +namespace Json { + +namespace Detail { + +struct jsonConstructor { + template + static void construct(Json& json, bool boolean) + { + json.clear(); + json.m_type = Json::Type::Bool; + json.m_value.asBool = boolean; + } + + template + static void construct(Json& json, int number) + { + json.clear(); + json.m_type = Json::Type::Number; + json.m_value.asDouble = (double)number; + } + + template + static void construct(Json& json, double number) + { + json.clear(); + json.m_type = Json::Type::Number; + json.m_value.asDouble = number; + } + + template + static void construct(Json& json, const char* string) + { + json.clear(); + json.m_type = Json::Type::String; + json.m_value.asString = new std::string(string); + } + + template + static void construct(Json& json, const std::string& string) + { + json.clear(); + json.m_type = Json::Type::String; + json.m_value.asString = new std::string(string); + } + + template + static void construct(Json& json, const Array& array) + { + json.clear(); + json.m_type = Json::Type::Array; + json.m_value.asArray = new Array(array); + } + + template + static void construct(Json& json, const std::vector& array) + { + json.clear(); + json.m_type = Json::Type::Array; + json.m_value.asArray = new Array; + json.m_value.asArray->reserve(array.size()); + for (const T& value : array) { + json.m_value.asArray->emplace_back(value); + } + } + + template + static void construct(Json& json, const Object& object) + { + json.clear(); + json.m_type = Json::Type::Object; + json.m_value.asObject = new Object(object); + } + + template + static void construct(Json& json, const std::map& object) + { + json.clear(); + json.m_type = Json::Type::Object; + json.m_value.asObject = new Object; + for (const auto& [name, value] : object) { + json.m_value.asObject->emplace(name, value); + } + } + + template + static void construct(Json& json, const std::unordered_map& object) + { + json.clear(); + json.m_type = Json::Type::Object; + json.m_value.asObject = new Object; + for (const auto& [name, value] : object) { + json.m_value.asObject->emplace(name, value); + } + } +}; + +template +void toJson(Json& json, const T& value) +{ + jsonConstructor::construct(json, value); +} + +struct toJsonFunction { + template + auto operator()(Json& json, T&& value) const + { + return toJson(json, std::forward(value)); + } +}; + +} // namespace Detail + +// Anonymous namespace prevents multiple definition of the reference +namespace { +// Function object +constexpr const auto& toJson = Detail::staticConst; // NOLINT (misc-definitions-in-headers) +} // namespace + +} // namespace Json + +#endif // JSON_TO_JSON_H + +// Customization Points +// 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::Detail::fromJsonFunction. In the Json::Detail namespace are the +// fromJson free functions. The function call operator of fromJsonFunction makes +// an unqualified call to fromJson which, since it shares the Detail namespace +// with the fromJson free functions, will consider those in addition to any +// overloads that are found by argument-dependent lookup. + +// Variable templates are linked externally, therefor every translation unit +// will see the same address for Detail::staticConst. +// Since Json::fromJson is a reference to the variable template, it too will +// have the same address in all translation units. diff --git a/src/util/json/value.cpp b/src/util/json/value.cpp index 4167bc1..7c74ca3 100644 --- a/src/util/json/value.cpp +++ b/src/util/json/value.cpp @@ -19,19 +19,9 @@ namespace Json { -Value::Value(const Value& other) -{ - copyFrom(other); -} - -Value& Value::operator=(const Value& other) +Value::Value(std::nullptr_t) + : Value(Type::Null) { - if (this != &other) { - clear(); - copyFrom(other); - } - - return *this; } Value::Value(Type type) @@ -40,48 +30,6 @@ Value::Value(Type type) create(); } -Value::Value(bool value) - : m_type(Type::Bool) -{ - m_value.asBool = value; -} - -Value::Value(int value) - : m_type(Type::Number) -{ - m_value.asDouble = value; -} - -Value::Value(double value) - : m_type(Type::Number) -{ - m_value.asDouble = value; -} - -Value::Value(const char* value) - : m_type(Type::String) -{ - m_value.asString = new std::string(value); -} - -Value::Value(const std::string& value) - : m_type(Type::String) -{ - m_value.asString = new std::string(value); -} - -Value::Value(const Array& value) - : m_type(Type::Array) -{ - m_value.asArray = new Array(value); -} - -Value::Value(const Object& value) - : m_type(Type::Object) -{ - m_value.asObject = new Object(value); -} - Value::Value(const std::initializer_list& values) { bool isObject = std::all_of(values.begin(), values.end(), [](const Value& value) { @@ -105,6 +53,23 @@ Value::Value(const std::initializer_list& values) } } +// Copy constructor +Value::Value(const Value& other) +{ + copyFrom(other); +} + +// Assignment operator +Value& Value::operator=(const Value& other) +{ + if (this != &other) { + clear(); + copyFrom(other); + } + + return *this; +} + // ------------------------------------------ Value Value::parse(const std::string& input) diff --git a/src/util/json/value.h b/src/util/json/value.h index 7ac9e4d..66f3674 100644 --- a/src/util/json/value.h +++ b/src/util/json/value.h @@ -12,8 +12,10 @@ #include #include // istream, ostream #include +#include // forward #include "util/json/fromjson.h" +#include "util/json/tojson.h" namespace Json { @@ -21,6 +23,9 @@ class Array; class Object; class Value { +private: + friend Detail::jsonConstructor; + public: enum class Type { Null, // null (case sensitive!) @@ -31,24 +36,25 @@ public: Object, // {} }; - Value(std::nullptr_t = nullptr) {} + // Constructors + Value(std::nullptr_t = nullptr); + Value(Type type); + Value(const std::initializer_list& values); + template + Value(T value) + { + toJson(*this, std::forward(value)); + } + + // Destructor virtual ~Value() { clear(); } // Copy constructor Value(const Value& other); + // Assignment operator Value& operator=(const Value& other); - Value(Type type); - Value(bool value); - Value(int value); - Value(double value); - Value(const char* value); - Value(const std::string& value); - Value(const Array& value); - Value(const Object& value); - Value(const std::initializer_list& values); - // -------------------------------------- static Value parse(const std::string& input);