From 44b0feee37904bb19eb4ca25ca0d756a177f5c0c Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 20:14:20 +0200 Subject: [PATCH 01/18] cppjson::JsonObject primitive, tagged union Signed-off-by: TymianekPL --- Test/Test.cpp | 10 ++- cppjson/include/cppjson/object.hpp | 42 ++++++++++++ cppjson/src/object.cpp | 106 +++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 cppjson/include/cppjson/object.hpp create mode 100644 cppjson/src/object.cpp diff --git a/Test/Test.cpp b/Test/Test.cpp index 34b0e90..9024f74 100644 --- a/Test/Test.cpp +++ b/Test/Test.cpp @@ -1,3 +1,9 @@ -#include +#include +#include -int main() { cppjson::hello_world(); } +int main() +{ + cppjson::JsonObject object{}; + object.As() = "Purr world!"; + std::println("{}", object.As()); +} diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp new file mode 100644 index 0000000..f4c4ab7 --- /dev/null +++ b/cppjson/include/cppjson/object.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace cppjson +{ + enum struct JsonType + { + Null, + String, + Object, + Number, + Bool, + // TODO: Array + }; + + class JsonObject + { + public: + explicit JsonObject(); + ~JsonObject(); + + template + T& As() noexcept(false); + + template + const T& As() const noexcept(false); + private: + JsonType _dataType{}; + std::byte* _dataStorage{}; + + void Destroy(); + template + T& DangerousAs() noexcept { return *std::launder(reinterpret_cast(this->_dataStorage)); } + template + const T& DangerousAs() const noexcept { return *std::launder(reinterpret_cast(this->_dataStorage)); } + }; +} diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp new file mode 100644 index 0000000..f1482c1 --- /dev/null +++ b/cppjson/src/object.cpp @@ -0,0 +1,106 @@ +#include "cppjson/object.hpp" +#include +#include + +constexpr std::size_t DataStorageSize = std::max({ sizeof(std::string), sizeof(cppjson::JsonObject), sizeof(double), sizeof(bool) }); + +cppjson::JsonObject::JsonObject() + : _dataStorage(static_cast(::operator new(DataStorageSize))) +{ +} + +cppjson::JsonObject::~JsonObject() +{ + this->Destroy(); + ::operator delete(this->_dataStorage); +} + +void cppjson::JsonObject::Destroy(void) +{ + using std::string; + + switch (std::exchange(this->_dataType, JsonType::Null)) + { + case JsonType::Null: + case JsonType::Number: + case JsonType::Bool: break; + case JsonType::String: DangerousAs().~string(); + } +} + +template<> +std::string& cppjson::JsonObject::As() noexcept(false) +{ + if (this->_dataType == JsonType::Null) + { + this->_dataType = JsonType::String; + return *new(this->_dataStorage) std::string{}; + } + + if (this->_dataType != JsonType::String) throw std::logic_error("Cannot convert this object to a string"); + return DangerousAs(); +} + +template<> +double& cppjson::JsonObject::As() noexcept(false) +{ + if (this->_dataType == JsonType::Null) + { + this->_dataType = JsonType::Number; + return *new(this->_dataStorage) double{}; + } + + if (this->_dataType != JsonType::Number) throw std::logic_error("Cannot convert this object to a double"); + return DangerousAs(); +} + +template<> +bool& cppjson::JsonObject::As() noexcept(false) +{ + if (this->_dataType == JsonType::Null) + { + this->_dataType = JsonType::Bool; + return *new(this->_dataStorage) bool{}; + } + + if (this->_dataType != JsonType::Bool) throw std::logic_error("Cannot convert this object to a bool"); + return DangerousAs(); +} + +template<> +std::nullptr_t& cppjson::JsonObject::As() noexcept(false) +{ + if (std::exchange(this->_dataType, JsonType::Null) == JsonType::Null) + return DangerousAs(); + + return *new(this->_dataStorage) std::nullptr_t{}; +} + + +template<> +const std::string& cppjson::JsonObject::As() const noexcept(false) +{ + if (this->_dataType != JsonType::String) throw std::logic_error("Cannot convert this object to a string"); + return DangerousAs(); +} + +template<> +const double& cppjson::JsonObject::As() const noexcept(false) +{ + if (this->_dataType != JsonType::Number) throw std::logic_error("Cannot convert this object to a double"); + return DangerousAs(); +} + +template<> +const bool& cppjson::JsonObject::As() const noexcept(false) +{ + if (this->_dataType != JsonType::Bool) throw std::logic_error("Cannot convert this object to a bool"); + return DangerousAs(); +} + +template<> +const std::nullptr_t& cppjson::JsonObject::As() const noexcept(false) +{ + if (this->_dataType != JsonType::Null) throw std::logic_error("Cannot convert this object to a null"); + return DangerousAs(); +} From 7a6d53f5d75f4ed51f8334b8069da30a1538c957 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 20:18:53 +0200 Subject: [PATCH 02/18] std::formatter specialisation for json objects Signed-off-by: TymianekPL --- Test/Test.cpp | 2 +- cppjson/include/cppjson/object.hpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Test/Test.cpp b/Test/Test.cpp index 9024f74..1ff448a 100644 --- a/Test/Test.cpp +++ b/Test/Test.cpp @@ -5,5 +5,5 @@ int main() { cppjson::JsonObject object{}; object.As() = "Purr world!"; - std::println("{}", object.As()); + std::println("{}", object); } diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index f4c4ab7..7daf744 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace cppjson { @@ -38,5 +39,32 @@ namespace cppjson T& DangerousAs() noexcept { return *std::launder(reinterpret_cast(this->_dataStorage)); } template const T& DangerousAs() const noexcept { return *std::launder(reinterpret_cast(this->_dataStorage)); } + + friend struct std::formatter; + }; +} + +namespace std +{ + template<> + struct formatter + { + constexpr auto parse(std::format_parse_context& context) + { + return context.begin(); + } + + auto format(const cppjson::JsonObject& object, std::format_context& context) const + { + switch (object._dataType) + { + case cppjson::JsonType::Null: return std::format_to(context.out(), "null"); + case cppjson::JsonType::Bool: return std::format_to(context.out(), "{}", object.DangerousAs()); + case cppjson::JsonType::Number: return std::format_to(context.out(), "{}", object.DangerousAs()); + case cppjson::JsonType::String: return std::format_to(context.out(), "{}", object.DangerousAs()); + } + + throw std::logic_error("Unknown type"); + } }; } From 8702a4d510e51b09dc9e7b34d0b6a0f8cbcb535e Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 20:24:01 +0200 Subject: [PATCH 03/18] =?UTF-8?q?make=20formatter=20specialisation=20sexy?= =?UTF-8?q?=20=E2=9A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: TymianekPL --- cppjson/include/cppjson/object.hpp | 35 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 7daf744..9a1fffc 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -44,27 +44,24 @@ namespace cppjson }; } -namespace std +template<> +struct std::formatter { - template<> - struct formatter + constexpr auto parse(std::format_parse_context& context) { - constexpr auto parse(std::format_parse_context& context) - { - return context.begin(); - } + return context.begin(); + } - auto format(const cppjson::JsonObject& object, std::format_context& context) const + auto format(const cppjson::JsonObject& object, std::format_context& context) const + { + switch (object._dataType) { - switch (object._dataType) - { - case cppjson::JsonType::Null: return std::format_to(context.out(), "null"); - case cppjson::JsonType::Bool: return std::format_to(context.out(), "{}", object.DangerousAs()); - case cppjson::JsonType::Number: return std::format_to(context.out(), "{}", object.DangerousAs()); - case cppjson::JsonType::String: return std::format_to(context.out(), "{}", object.DangerousAs()); - } - - throw std::logic_error("Unknown type"); + case cppjson::JsonType::Null: return std::format_to(context.out(), "null"); + case cppjson::JsonType::Bool: return std::format_to(context.out(), "{}", object.DangerousAs()); + case cppjson::JsonType::Number: return std::format_to(context.out(), "{}", object.DangerousAs()); + case cppjson::JsonType::String: return std::format_to(context.out(), "{}", object.DangerousAs()); } - }; -} + + throw std::logic_error("Unknown type"); + } +}; From d61bbbe4372f339f0f705b6ebb91980a4e931bc4 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 20:49:18 +0200 Subject: [PATCH 04/18] sync with pr3: Changes C++23 => C++20 in .clang-format Signed-off-by: TymianekPL --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 1e35c08..89a7883 100644 --- a/.clang-format +++ b/.clang-format @@ -5,7 +5,7 @@ AlwaysBreakTemplateDeclarations: Yes BreakBeforeBraces: Attach ColumnLimit: 160 SpaceAfterTemplateKeyword: true -Standard: c++23 +Standard: c++20 TabWidth: 4 IndentWidth: 4 UseTab: Always From b09cfd39b34018e5ca292e51957d40e192774a85 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 21:12:51 +0200 Subject: [PATCH 05/18] Add cppjson::Object Signed-off-by: TymianekPL --- cppjson/include/cppjson/object.hpp | 26 ++++++++++++++++++++++++++ cppjson/src/object.cpp | 24 ++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 9a1fffc..9187573 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace cppjson { @@ -42,6 +43,31 @@ namespace cppjson friend struct std::formatter; }; + + class Object + { + public: + explicit Object() = default; + Object(const Object&) = default; + Object(Object&&) = default; + Object& operator=(const Object&) = default; + Object& operator=(Object&&) = default; + ~Object() = default; + + template + T& operator[](const std::string& key) + { + return this->_nodes[key].As(); + } + template + const T& operator[](const std::string& key) const + { + if (!this->_nodes.contains(key)) throw std::logic_error("Invalid key" + key); + return this->_nodes.at(key).As(); + } + private: + std::unordered_map _nodes{}; + }; } template<> diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp index f1482c1..65fe2cf 100644 --- a/cppjson/src/object.cpp +++ b/cppjson/src/object.cpp @@ -2,7 +2,7 @@ #include #include -constexpr std::size_t DataStorageSize = std::max({ sizeof(std::string), sizeof(cppjson::JsonObject), sizeof(double), sizeof(bool) }); +constexpr std::size_t DataStorageSize = std::max({ sizeof(std::string), sizeof(cppjson::Object), sizeof(double), sizeof(bool) }); cppjson::JsonObject::JsonObject() : _dataStorage(static_cast(::operator new(DataStorageSize))) @@ -67,6 +67,19 @@ bool& cppjson::JsonObject::As() noexcept(false) return DangerousAs(); } +template<> +cppjson::Object& cppjson::JsonObject::As() noexcept(false) +{ + if (this->_dataType == JsonType::Null) + { + this->_dataType = JsonType::Object; + return *new(this->_dataStorage) cppjson::Object{}; + } + + if (this->_dataType != JsonType::Object) throw std::logic_error("Cannot convert this object to a bool"); + return DangerousAs(); +} + template<> std::nullptr_t& cppjson::JsonObject::As() noexcept(false) { @@ -76,7 +89,6 @@ std::nullptr_t& cppjson::JsonObject::As() noexcept(false) return *new(this->_dataStorage) std::nullptr_t{}; } - template<> const std::string& cppjson::JsonObject::As() const noexcept(false) { @@ -98,6 +110,14 @@ const bool& cppjson::JsonObject::As() const noexcept(false) return DangerousAs(); } +template<> +const cppjson::Object& cppjson::JsonObject::As() const noexcept(false) +{ + if (this->_dataType != JsonType::Object) throw std::logic_error("Cannot convert this object to an object"); + return DangerousAs(); +} + + template<> const std::nullptr_t& cppjson::JsonObject::As() const noexcept(false) { From 4e6c9c13b5c68892d4989c1f5db3e196344607d4 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 21:20:33 +0200 Subject: [PATCH 06/18] Fix clang-format Signed-off-by: TymianekPL --- .clang-format | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 89a7883..725b1f5 100644 --- a/.clang-format +++ b/.clang-format @@ -2,7 +2,7 @@ Language: Cpp BasedOnStyle: LLVM AlwaysBreakTemplateDeclarations: Yes -BreakBeforeBraces: Attach +BreakBeforeBraces: Allman ColumnLimit: 160 SpaceAfterTemplateKeyword: true Standard: c++20 @@ -29,3 +29,4 @@ IncludeCategories: Priority: 30 PointerAlignment: Left QualifierAlignment: Left +NamespaceIndentation: All From 3a2e6a9e2a6a3c8121c65cdd62d97b4320807873 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 21:38:43 +0200 Subject: [PATCH 07/18] cppjson::Object Signed-off-by: TymianekPL --- Test/Test.cpp | 5 +- cppjson/include/cppjson/cppjson.hpp | 7 +- cppjson/include/cppjson/object.hpp | 125 ++++++++++++++++++++++------ cppjson/src/cppjson.cpp | 2 +- cppjson/src/object.cpp | 43 +++++----- 5 files changed, 128 insertions(+), 54 deletions(-) diff --git a/Test/Test.cpp b/Test/Test.cpp index 1ff448a..f5efb57 100644 --- a/Test/Test.cpp +++ b/Test/Test.cpp @@ -3,7 +3,8 @@ int main() { - cppjson::JsonObject object{}; - object.As() = "Purr world!"; + cppjson::Object object{}; + std::println("{}", object); + object["test"] = "Hello World"; std::println("{}", object); } diff --git a/cppjson/include/cppjson/cppjson.hpp b/cppjson/include/cppjson/cppjson.hpp index 2955248..db8d8fb 100644 --- a/cppjson/include/cppjson/cppjson.hpp +++ b/cppjson/include/cppjson/cppjson.hpp @@ -1,5 +1,6 @@ -#pragma once +#pragma once -namespace cppjson { -void hello_world(); +namespace cppjson +{ + void hello_world(); } // namespace cppjson diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 9187573..ade0a2e 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -1,12 +1,13 @@ #pragma once -#include -#include #include -#include #include +#include #include +#include #include +#include +#include namespace cppjson { @@ -22,7 +23,7 @@ namespace cppjson class JsonObject { - public: + public: explicit JsonObject(); ~JsonObject(); @@ -31,52 +32,105 @@ namespace cppjson template const T& As() const noexcept(false); - private: + + private: JsonType _dataType{}; std::byte* _dataStorage{}; void Destroy(); template - T& DangerousAs() noexcept { return *std::launder(reinterpret_cast(this->_dataStorage)); } + T& DangerousAs() noexcept + { + return *std::launder(reinterpret_cast(this->_dataStorage)); + } template - const T& DangerousAs() const noexcept { return *std::launder(reinterpret_cast(this->_dataStorage)); } + const T& DangerousAs() const noexcept + { + return *std::launder(reinterpret_cast(this->_dataStorage)); + } friend struct std::formatter; }; class Object { - public: + public: explicit Object() = default; Object(const Object&) = default; - Object(Object&&) = default; + Object(Object&&) = default; Object& operator=(const Object&) = default; Object& operator=(Object&&) = default; ~Object() = default; - template - T& operator[](const std::string& key) + class ObjectProxy { - return this->_nodes[key].As(); - } - template - const T& operator[](const std::string& key) const + public: + explicit ObjectProxy(JsonObject& object) : _object(std::ref(object)) {} + + template + operator T&() + { + return this->_object.get().As(); + } + + template + operator const T&() const + { + return this->_object.get().As(); + } + + template + T& operator=(T&& assignment) + { + return static_cast(*this) = std::forward(assignment); + } + + template + std::string& operator=(const char(&str)[N]) + { + return static_cast(*this) = std::string{ str }; + } + private: + std::reference_wrapper _object; + }; + + + class ConstObjectProxy + { + public: + explicit ConstObjectProxy(const JsonObject& object) : _object(std::ref(object)) {} + template + operator const T&() const + { + return this->_object.get().As(); + } + private: + std::reference_wrapper _object; + }; + + ObjectProxy operator[](const std::string& key) + { + return ObjectProxy{ this->_nodes[key] }; + } + + ConstObjectProxy operator[](const std::string& key) const { if (!this->_nodes.contains(key)) throw std::logic_error("Invalid key" + key); - return this->_nodes.at(key).As(); + + return ConstObjectProxy{ this->_nodes.at(key) }; } - private: + + private: std::unordered_map _nodes{}; + + friend struct std::formatter; }; -} +} // namespace cppjson -template<> +template <> struct std::formatter { - constexpr auto parse(std::format_parse_context& context) - { - return context.begin(); - } + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } auto format(const cppjson::JsonObject& object, std::format_context& context) const { @@ -85,9 +139,32 @@ struct std::formatter case cppjson::JsonType::Null: return std::format_to(context.out(), "null"); case cppjson::JsonType::Bool: return std::format_to(context.out(), "{}", object.DangerousAs()); case cppjson::JsonType::Number: return std::format_to(context.out(), "{}", object.DangerousAs()); - case cppjson::JsonType::String: return std::format_to(context.out(), "{}", object.DangerousAs()); + case cppjson::JsonType::String: return std::format_to(context.out(), "\"{}\"", object.DangerousAs()); } throw std::logic_error("Unknown type"); } }; + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::Object& object, std::format_context& context) const + { + std::string built = "{ "; + for (const auto& [key, value] : object._nodes) + built += std::format("\"{}\": {}, ", key, value); + + if (!object._nodes.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " }"; + } + else built += "}"; + + return std::format_to(context.out(), "{}", built); + } +}; diff --git a/cppjson/src/cppjson.cpp b/cppjson/src/cppjson.cpp index e12af32..b35c1a8 100644 --- a/cppjson/src/cppjson.cpp +++ b/cppjson/src/cppjson.cpp @@ -1,4 +1,4 @@ -#include +#include #include void cppjson::hello_world() { std::println("Hewwo wowld"); } diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp index 65fe2cf..c6aac9c 100644 --- a/cppjson/src/object.cpp +++ b/cppjson/src/object.cpp @@ -1,13 +1,10 @@ -#include "cppjson/object.hpp" +#include "cppjson/object.hpp" #include #include -constexpr std::size_t DataStorageSize = std::max({ sizeof(std::string), sizeof(cppjson::Object), sizeof(double), sizeof(bool) }); +constexpr std::size_t DataStorageSize = std::max({sizeof(std::string), sizeof(cppjson::Object), sizeof(double), sizeof(bool)}); -cppjson::JsonObject::JsonObject() - : _dataStorage(static_cast(::operator new(DataStorageSize))) -{ -} +cppjson::JsonObject::JsonObject() : _dataStorage(static_cast(::operator new(DataStorageSize))) {} cppjson::JsonObject::~JsonObject() { @@ -28,97 +25,95 @@ void cppjson::JsonObject::Destroy(void) } } -template<> +template <> std::string& cppjson::JsonObject::As() noexcept(false) { if (this->_dataType == JsonType::Null) { this->_dataType = JsonType::String; - return *new(this->_dataStorage) std::string{}; + return *new (this->_dataStorage) std::string{}; } if (this->_dataType != JsonType::String) throw std::logic_error("Cannot convert this object to a string"); return DangerousAs(); } -template<> +template <> double& cppjson::JsonObject::As() noexcept(false) { if (this->_dataType == JsonType::Null) { this->_dataType = JsonType::Number; - return *new(this->_dataStorage) double{}; + return *new (this->_dataStorage) double{}; } if (this->_dataType != JsonType::Number) throw std::logic_error("Cannot convert this object to a double"); return DangerousAs(); } -template<> +template <> bool& cppjson::JsonObject::As() noexcept(false) { if (this->_dataType == JsonType::Null) { this->_dataType = JsonType::Bool; - return *new(this->_dataStorage) bool{}; + return *new (this->_dataStorage) bool{}; } if (this->_dataType != JsonType::Bool) throw std::logic_error("Cannot convert this object to a bool"); return DangerousAs(); } -template<> +template <> cppjson::Object& cppjson::JsonObject::As() noexcept(false) { if (this->_dataType == JsonType::Null) { this->_dataType = JsonType::Object; - return *new(this->_dataStorage) cppjson::Object{}; + return *new (this->_dataStorage) cppjson::Object{}; } if (this->_dataType != JsonType::Object) throw std::logic_error("Cannot convert this object to a bool"); return DangerousAs(); } -template<> +template <> std::nullptr_t& cppjson::JsonObject::As() noexcept(false) { - if (std::exchange(this->_dataType, JsonType::Null) == JsonType::Null) - return DangerousAs(); + if (std::exchange(this->_dataType, JsonType::Null) == JsonType::Null) return DangerousAs(); - return *new(this->_dataStorage) std::nullptr_t{}; + return *new (this->_dataStorage) std::nullptr_t{}; } -template<> +template <> const std::string& cppjson::JsonObject::As() const noexcept(false) { if (this->_dataType != JsonType::String) throw std::logic_error("Cannot convert this object to a string"); return DangerousAs(); } -template<> +template <> const double& cppjson::JsonObject::As() const noexcept(false) { if (this->_dataType != JsonType::Number) throw std::logic_error("Cannot convert this object to a double"); return DangerousAs(); } -template<> +template <> const bool& cppjson::JsonObject::As() const noexcept(false) { if (this->_dataType != JsonType::Bool) throw std::logic_error("Cannot convert this object to a bool"); return DangerousAs(); } -template<> +template <> const cppjson::Object& cppjson::JsonObject::As() const noexcept(false) { if (this->_dataType != JsonType::Object) throw std::logic_error("Cannot convert this object to an object"); return DangerousAs(); } - -template<> +template <> const std::nullptr_t& cppjson::JsonObject::As() const noexcept(false) { if (this->_dataType != JsonType::Null) throw std::logic_error("Cannot convert this object to a null"); From bc8a64d8fe506195017c772493d27b2ebd9ec8dd Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 21:41:00 +0200 Subject: [PATCH 08/18] mark conversions as explicitly implicit Signed-off-by: TymianekPL --- Test/Test.cpp | 3 ++- cppjson/include/cppjson/object.hpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Test/Test.cpp b/Test/Test.cpp index f5efb57..cbb597b 100644 --- a/Test/Test.cpp +++ b/Test/Test.cpp @@ -5,6 +5,7 @@ int main() { cppjson::Object object{}; std::println("{}", object); - object["test"] = "Hello World"; + object["test1"] = "Hello World"; + object["test2"] = 123.0; std::println("{}", object); } diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index ade0a2e..0859b4d 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -68,13 +68,13 @@ namespace cppjson explicit ObjectProxy(JsonObject& object) : _object(std::ref(object)) {} template - operator T&() + explicit(false) operator T&() { return this->_object.get().As(); } template - operator const T&() const + explicit(false) operator const T&() const { return this->_object.get().As(); } @@ -100,7 +100,7 @@ namespace cppjson public: explicit ConstObjectProxy(const JsonObject& object) : _object(std::ref(object)) {} template - operator const T&() const + explicit(false) operator const T&() const { return this->_object.get().As(); } From 1e4d45c990954095f4cb8c4241ccd3d5d519d1e2 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 21:43:50 +0200 Subject: [PATCH 09/18] formatter specialisations for proxies Signed-off-by: TymianekPL --- Test/Test.cpp | 1 + cppjson/include/cppjson/object.hpp | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/Test/Test.cpp b/Test/Test.cpp index cbb597b..935a733 100644 --- a/Test/Test.cpp +++ b/Test/Test.cpp @@ -8,4 +8,5 @@ int main() object["test1"] = "Hello World"; object["test2"] = 123.0; std::println("{}", object); + std::println("object[\"test1\"] = {}", object["test1"]); } diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 0859b4d..834f72b 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -92,6 +92,8 @@ namespace cppjson } private: std::reference_wrapper _object; + + friend struct std::formatter; }; @@ -106,6 +108,8 @@ namespace cppjson } private: std::reference_wrapper _object; + + friend struct std::formatter; }; ObjectProxy operator[](const std::string& key) @@ -146,6 +150,29 @@ struct std::formatter } }; +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::Object::ObjectProxy& object, std::format_context& context) const + { + return std::format_to(context.out(), "{}", object._object.get()); + } +}; + + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::Object::ConstObjectProxy& object, std::format_context& context) const + { + return std::format_to(context.out(), "{}", object._object.get()); + } +}; + template <> struct std::formatter { From 38b32ab2574778617458389ff6648d8524d71873 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Sun, 18 May 2025 22:11:02 +0200 Subject: [PATCH 10/18] Sub objects Signed-off-by: TymianekPL --- Test/Test.cpp | 4 ++ cppjson/include/cppjson/object.hpp | 67 +++++++++++++++++++++--------- cppjson/src/object.cpp | 11 +++++ 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Test/Test.cpp b/Test/Test.cpp index 935a733..3d7ab75 100644 --- a/Test/Test.cpp +++ b/Test/Test.cpp @@ -7,6 +7,10 @@ int main() std::println("{}", object); object["test1"] = "Hello World"; object["test2"] = 123.0; + object["sub"]["veryNested"] = 6.0; + std::println("{}", object); std::println("object[\"test1\"] = {}", object["test1"]); + const std::string test = object["test1"]; + std::println("test = {}", test); } diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 834f72b..8c2cf57 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -68,12 +68,14 @@ namespace cppjson explicit ObjectProxy(JsonObject& object) : _object(std::ref(object)) {} template + requires (!std::same_as, JsonObject>) explicit(false) operator T&() { return this->_object.get().As(); } template + requires (!std::same_as, JsonObject>) explicit(false) operator const T&() const { return this->_object.get().As(); @@ -90,6 +92,8 @@ namespace cppjson { return static_cast(*this) = std::string{ str }; } + + ObjectProxy operator[](const std::string& key); private: std::reference_wrapper _object; @@ -106,6 +110,8 @@ namespace cppjson { return this->_object.get().As(); } + + ConstObjectProxy operator[](const std::string& key) const; private: std::reference_wrapper _object; @@ -127,10 +133,12 @@ namespace cppjson private: std::unordered_map _nodes{}; + friend struct std::formatter; friend struct std::formatter; }; } // namespace cppjson + template <> struct std::formatter { @@ -144,6 +152,24 @@ struct std::formatter case cppjson::JsonType::Bool: return std::format_to(context.out(), "{}", object.DangerousAs()); case cppjson::JsonType::Number: return std::format_to(context.out(), "{}", object.DangerousAs()); case cppjson::JsonType::String: return std::format_to(context.out(), "\"{}\"", object.DangerousAs()); + case cppjson::JsonType::Object: + { + const auto& node = object.DangerousAs(); + + std::string built = "{ "; + for (const auto& [key, value] : node._nodes) + built += std::format("\"{}\": {}, ", key, value); + + if (!node._nodes.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " }"; + } + else built += "}"; + + return std::format_to(context.out(), "{}", built); + } } throw std::logic_error("Unknown type"); @@ -151,47 +177,48 @@ struct std::formatter }; template <> -struct std::formatter +struct std::formatter { constexpr auto parse(std::format_parse_context& context) { return context.begin(); } - auto format(const cppjson::Object::ObjectProxy& object, std::format_context& context) const + auto format(const cppjson::Object& object, std::format_context& context) const { - return std::format_to(context.out(), "{}", object._object.get()); + std::string built = "{ "; + for (const auto& [key, value] : object._nodes) + built += std::format("\"{}\": {}, ", key, value); + + if (!object._nodes.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " }"; + } + else built += "}"; + + return std::format_to(context.out(), "{}", built); } }; template <> -struct std::formatter +struct std::formatter { constexpr auto parse(std::format_parse_context& context) { return context.begin(); } - auto format(const cppjson::Object::ConstObjectProxy& object, std::format_context& context) const + auto format(const cppjson::Object::ObjectProxy& object, std::format_context& context) const { return std::format_to(context.out(), "{}", object._object.get()); } }; + template <> -struct std::formatter +struct std::formatter { constexpr auto parse(std::format_parse_context& context) { return context.begin(); } - auto format(const cppjson::Object& object, std::format_context& context) const + auto format(const cppjson::Object::ConstObjectProxy& object, std::format_context& context) const { - std::string built = "{ "; - for (const auto& [key, value] : object._nodes) - built += std::format("\"{}\": {}, ", key, value); - - if (!object._nodes.empty()) // remove trailing commas - { - built.pop_back(); - built.pop_back(); - built += " }"; - } - else built += "}"; - - return std::format_to(context.out(), "{}", built); + return std::format_to(context.out(), "{}", object._object.get()); } }; diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp index c6aac9c..8641b9d 100644 --- a/cppjson/src/object.cpp +++ b/cppjson/src/object.cpp @@ -119,3 +119,14 @@ const std::nullptr_t& cppjson::JsonObject::As() const noexcept(f if (this->_dataType != JsonType::Null) throw std::logic_error("Cannot convert this object to a null"); return DangerousAs(); } + +cppjson::Object::ObjectProxy cppjson::Object::ObjectProxy::operator[](const std::string& key) +{ + return ObjectProxy{ this->_object.get().As()[key] }; +} + + +cppjson::Object::ConstObjectProxy cppjson::Object::ConstObjectProxy::operator[](const std::string& key) const +{ + return ConstObjectProxy{ this->_object.get().As()[key] }; +} From 1770090fcb6142111ef6b8b5ce35538e2472e011 Mon Sep 17 00:00:00 2001 From: Tymianek Date: Mon, 19 May 2025 08:48:02 +0200 Subject: [PATCH 11/18] Fix violation of rule of 5 in JsonObject (#4) * Add declarations for JsonObject's special functions * Define copy/move constructors * Define copy/move assignment operators * Implicitly create bytes * Implicitly create bytes in exchange() * Fix includes lol (utility, cstring) * Destroy() objects --- cppjson/include/cppjson/object.hpp | 4 +++ cppjson/src/object.cpp | 39 +++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 8c2cf57..1599134 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -25,6 +25,10 @@ namespace cppjson { public: explicit JsonObject(); + JsonObject(const JsonObject& other); + JsonObject(JsonObject&& other); + JsonObject& operator=(const JsonObject& other); + JsonObject& operator=(JsonObject&& other); ~JsonObject(); template diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp index 8641b9d..ea50613 100644 --- a/cppjson/src/object.cpp +++ b/cppjson/src/object.cpp @@ -1,11 +1,45 @@ #include "cppjson/object.hpp" #include #include +#include +#include constexpr std::size_t DataStorageSize = std::max({sizeof(std::string), sizeof(cppjson::Object), sizeof(double), sizeof(bool)}); cppjson::JsonObject::JsonObject() : _dataStorage(static_cast(::operator new(DataStorageSize))) {} +cppjson::JsonObject::JsonObject(const cppjson::JsonObject& other) +{ + if (other._dataStorage == nullptr) return; + + this->_dataType = other._dataType; + this->_dataStorage = static_cast(::operator new(DataStorageSize)); + std::memcpy(this->_dataStorage, other._dataStorage, DataStorageSize); +} +cppjson::JsonObject::JsonObject(JsonObject&& other) +{ + this->_dataType = std::exchange(other._dataType, cppjson::JsonType::Null); + this->_dataStorage = std::exchange(other._dataStorage, static_cast(::operator new(DataStorageSize))); +} +cppjson::JsonObject& cppjson::JsonObject::operator=(const cppjson::JsonObject& other) +{ + if (&other != this) + { + this->_dataType = other._dataType; + this->_dataStorage = static_cast(::operator new(DataStorageSize)); + std::memcpy(this->_dataStorage, other._dataStorage, DataStorageSize); + } + return *this; +} +cppjson::JsonObject& cppjson::JsonObject::operator=(cppjson::JsonObject&& other) +{ + if (&other != this) + { + this->_dataType = std::exchange(other._dataType, cppjson::JsonType::Null); + this->_dataStorage = std::exchange(other._dataStorage, static_cast(::operator new(DataStorageSize))); + } + return *this; +} cppjson::JsonObject::~JsonObject() { this->Destroy(); @@ -15,13 +49,16 @@ cppjson::JsonObject::~JsonObject() void cppjson::JsonObject::Destroy(void) { using std::string; + using cppjson::Object; switch (std::exchange(this->_dataType, JsonType::Null)) { case JsonType::Null: case JsonType::Number: case JsonType::Bool: break; - case JsonType::String: DangerousAs().~string(); + case JsonType::String: DangerousAs().~string(); break; + case JsonType::Object: DangerousAs().~Object(); break; + // TODO: Array } } From 7b082cade2f9b4b4fd724cccda362e699c203020 Mon Sep 17 00:00:00 2001 From: Tymianek Date: Mon, 19 May 2025 17:43:12 +0200 Subject: [PATCH 12/18] Fix ambiguous operator[] (#6) * Fix ambiguous operator[] * template typo lol --- cppjson/include/cppjson/object.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 1599134..7ac0f78 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -98,6 +98,8 @@ namespace cppjson } ObjectProxy operator[](const std::string& key); + template + ObjectProxy operator[](const char(&key)[N]) { return (*this)[std::string{key}]; } private: std::reference_wrapper _object; From f280e77e62631e9eb24a79f8197b298a273d1288 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Mon, 19 May 2025 17:52:06 +0200 Subject: [PATCH 13/18] Call Destroy() in type erase Signed-off-by: TymianekPL --- cppjson/src/object.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp index ea50613..a32ce8b 100644 --- a/cppjson/src/object.cpp +++ b/cppjson/src/object.cpp @@ -114,11 +114,25 @@ cppjson::Object& cppjson::JsonObject::As() noexcept(false) return DangerousAs(); } +template <> +cppjson::Array& cppjson::JsonObject::As() noexcept(false) +{ + if (this->_dataType == JsonType::Null) + { + this->_dataType = JsonType::Array; + return *new (this->_dataStorage) cppjson::Array{}; + } + + if (this->_dataType != JsonType::Array) throw std::logic_error("Cannot convert this object to an array"); + return DangerousAs(); +} + template <> std::nullptr_t& cppjson::JsonObject::As() noexcept(false) { if (std::exchange(this->_dataType, JsonType::Null) == JsonType::Null) return DangerousAs(); + Destroy(); return *new (this->_dataStorage) std::nullptr_t{}; } @@ -150,6 +164,13 @@ const cppjson::Object& cppjson::JsonObject::As() const noexcept return DangerousAs(); } +template <> +const cppjson::Array& cppjson::JsonObject::As() const noexcept(false) +{ + if (this->_dataType != JsonType::Array) throw std::logic_error("Cannot convert this object to an Array"); + return DangerousAs(); +} + template <> const std::nullptr_t& cppjson::JsonObject::As() const noexcept(false) { From 55e2fd6e1acb467b79bea91dbb8e3a4ca71f38e5 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Mon, 19 May 2025 17:53:17 +0200 Subject: [PATCH 14/18] Ensure safe cleanup of arrays Signed-off-by: TymianekPL --- cppjson/src/object.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp index a32ce8b..1a62512 100644 --- a/cppjson/src/object.cpp +++ b/cppjson/src/object.cpp @@ -50,15 +50,16 @@ void cppjson::JsonObject::Destroy(void) { using std::string; using cppjson::Object; + using cppjson::Array; switch (std::exchange(this->_dataType, JsonType::Null)) { case JsonType::Null: case JsonType::Number: case JsonType::Bool: break; - case JsonType::String: DangerousAs().~string(); break; - case JsonType::Object: DangerousAs().~Object(); break; - // TODO: Array + case JsonType::String: return DangerousAs().~string(); + case JsonType::Object: return DangerousAs().~Object(); + case JsonType::Array: return DangerousAs().~Array(); } } From c45adda16b0ed227eab136951f69a3ca72c70a7b Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Mon, 19 May 2025 18:17:38 +0200 Subject: [PATCH 15/18] Arrays Signed-off-by: TymianekPL --- Test/Test.cpp | 13 +++++ cppjson/include/cppjson/object.hpp | 82 ++++++++++++++++++++++++++++-- cppjson/src/object.cpp | 4 +- 3 files changed, 92 insertions(+), 7 deletions(-) diff --git a/Test/Test.cpp b/Test/Test.cpp index 3d7ab75..e5329ed 100644 --- a/Test/Test.cpp +++ b/Test/Test.cpp @@ -8,6 +8,19 @@ int main() object["test1"] = "Hello World"; object["test2"] = 123.0; object["sub"]["veryNested"] = 6.0; + cppjson::Array& array = object["array"]; + array[] = 2; + array[] = 6.0; + array[0] = 1; + array[] = "Stirng"; + try + { + array[2] = true; + } + catch (const std::logic_error& error) + { + std::println("Error = {}", error.what()); + } std::println("{}", object); std::println("object[\"test1\"] = {}", object["test1"]); diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 7ac0f78..1e2199a 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace cppjson { @@ -18,7 +19,7 @@ namespace cppjson Object, Number, Bool, - // TODO: Array + Array }; class JsonObject @@ -26,9 +27,9 @@ namespace cppjson public: explicit JsonObject(); JsonObject(const JsonObject& other); - JsonObject(JsonObject&& other); + JsonObject(JsonObject&& other) noexcept; JsonObject& operator=(const JsonObject& other); - JsonObject& operator=(JsonObject&& other); + JsonObject& operator=(JsonObject&& other) noexcept; ~JsonObject(); template @@ -86,9 +87,10 @@ namespace cppjson } template - T& operator=(T&& assignment) + std::conditional_t && !std::same_as, void, T&> operator=(T&& assignment) { - return static_cast(*this) = std::forward(assignment); + if constexpr (std::integral && !std::same_as) static_cast(*this) = static_cast(assignment); + else return static_cast(*this) = std::forward(assignment); } template @@ -142,6 +144,35 @@ namespace cppjson friend struct std::formatter; friend struct std::formatter; }; + + class Array + { + public: + explicit Array() = default; + ~Array() = default; + + Object::ObjectProxy operator[]() + { + return Object::ObjectProxy{ this->_objects.emplace_back() }; + } + + Object::ObjectProxy operator[](const int index) + { + if (index >= this->_objects.size()) throw std::logic_error("Out of bound"); + return Object::ObjectProxy{ this->_objects.at(index) }; + } + + Object::ConstObjectProxy operator[](const int index) const + { + if (index >= this->_objects.size()) throw std::logic_error("Out of bound"); + return Object::ConstObjectProxy{ this->_objects.at(index) }; + } + private: + std::vector _objects{}; + + friend struct std::formatter; + friend struct std::formatter; + }; } // namespace cppjson @@ -176,6 +207,24 @@ struct std::formatter return std::format_to(context.out(), "{}", built); } + case cppjson::JsonType::Array: + { + const auto& array = object.DangerousAs(); + + std::string built = "[ "; + for (const auto& element : array._objects) + built += std::format("{}, ", element); + + if (!array._objects.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " ]"; + } + else built += "]"; + + return std::format_to(context.out(), "{}", built); + } } throw std::logic_error("Unknown type"); @@ -205,6 +254,29 @@ struct std::formatter } }; +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::Array& array, std::format_context& context) const + { + std::string built = "[ "; + for (const auto& element : array._objects) + built += std::format("{}, ", element); + + if (!array._objects.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " ]"; + } + else built += "]"; + + return std::format_to(context.out(), "{}", built); + } +}; + template <> struct std::formatter diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp index 1a62512..feb29e1 100644 --- a/cppjson/src/object.cpp +++ b/cppjson/src/object.cpp @@ -16,7 +16,7 @@ cppjson::JsonObject::JsonObject(const cppjson::JsonObject& other) this->_dataStorage = static_cast(::operator new(DataStorageSize)); std::memcpy(this->_dataStorage, other._dataStorage, DataStorageSize); } -cppjson::JsonObject::JsonObject(JsonObject&& other) +cppjson::JsonObject::JsonObject(JsonObject&& other) noexcept { this->_dataType = std::exchange(other._dataType, cppjson::JsonType::Null); this->_dataStorage = std::exchange(other._dataStorage, static_cast(::operator new(DataStorageSize))); @@ -31,7 +31,7 @@ cppjson::JsonObject& cppjson::JsonObject::operator=(const cppjson::JsonObject& o } return *this; } -cppjson::JsonObject& cppjson::JsonObject::operator=(cppjson::JsonObject&& other) +cppjson::JsonObject& cppjson::JsonObject::operator=(cppjson::JsonObject&& other) noexcept { if (&other != this) { From ceadea554aa8fefff42e12488b28a47f5e278233 Mon Sep 17 00:00:00 2001 From: Tymianek Date: Tue, 20 May 2025 07:19:23 +0200 Subject: [PATCH 16/18] add emplace --- cppjson/include/cppjson/object.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 1e2199a..9f00119 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -156,6 +156,13 @@ namespace cppjson return Object::ObjectProxy{ this->_objects.emplace_back() }; } +Object::ObjectProxy EmplaceBack(const auto& object = nullptr) + { + if constexpr(std::same_as) + return Object::ObjectProxy{ this->_objects.emplace_back() }; + else return Object::ObjectProxy{ this->_objects.emplace_back(object) }; + } + Object::ObjectProxy operator[](const int index) { if (index >= this->_objects.size()) throw std::logic_error("Out of bound"); From 4ba8562dc6ba717870793b8db06a4f57599aa9d3 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Fri, 23 May 2025 16:51:37 +0200 Subject: [PATCH 17/18] Split into multiple files & add finish arrays Signed-off-by: TymianekPL --- Test/Test.cpp | 3 +- cppjson/include/cppjson/cppjson.hpp | 6 +- cppjson/include/cppjson/formatter.hpp | 125 ++++++++++++++++ cppjson/include/cppjson/object.hpp | 206 ++++++-------------------- cppjson/src/cppjson.cpp | 4 - 5 files changed, 171 insertions(+), 173 deletions(-) create mode 100644 cppjson/include/cppjson/formatter.hpp diff --git a/Test/Test.cpp b/Test/Test.cpp index e5329ed..789fd22 100644 --- a/Test/Test.cpp +++ b/Test/Test.cpp @@ -1,4 +1,4 @@ -#include +#include #include int main() @@ -13,6 +13,7 @@ int main() array[] = 6.0; array[0] = 1; array[] = "Stirng"; + array.EmplaceBack(nullptr); try { array[2] = true; diff --git a/cppjson/include/cppjson/cppjson.hpp b/cppjson/include/cppjson/cppjson.hpp index db8d8fb..817f431 100644 --- a/cppjson/include/cppjson/cppjson.hpp +++ b/cppjson/include/cppjson/cppjson.hpp @@ -1,6 +1,4 @@ #pragma once -namespace cppjson -{ - void hello_world(); -} // namespace cppjson +#include "object.hpp" +#include "formatter.hpp" diff --git a/cppjson/include/cppjson/formatter.hpp b/cppjson/include/cppjson/formatter.hpp new file mode 100644 index 0000000..800f038 --- /dev/null +++ b/cppjson/include/cppjson/formatter.hpp @@ -0,0 +1,125 @@ +#include "object.hpp" +#include + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::JsonObject& object, std::format_context& context) const + { + switch (object._dataType) + { + case cppjson::JsonType::Null: return std::format_to(context.out(), "null"); + case cppjson::JsonType::Bool: return std::format_to(context.out(), "{}", object.DangerousAs()); + case cppjson::JsonType::Number: return std::format_to(context.out(), "{}", object.DangerousAs()); + case cppjson::JsonType::String: return std::format_to(context.out(), "\"{}\"", object.DangerousAs()); + case cppjson::JsonType::Object: + { + const auto& node = object.DangerousAs(); + + std::string built = "{ "; + for (const auto& [key, value] : node._nodes) built += std::format("\"{}\": {}, ", key, value); + + if (!node._nodes.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " }"; + } + else + built += "}"; + + return std::format_to(context.out(), "{}", built); + } + case cppjson::JsonType::Array: + { + const auto& array = object.DangerousAs(); + + std::string built = "[ "; + for (const auto& element : array._objects) built += std::format("{}, ", element); + + if (!array._objects.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " ]"; + } + else + built += "]"; + + return std::format_to(context.out(), "{}", built); + } + } + + throw std::logic_error("Unknown type"); + } +}; + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::Object& object, std::format_context& context) const + { + std::string built = "{ "; + for (const auto& [key, value] : object._nodes) built += std::format("\"{}\": {}, ", key, value); + + if (!object._nodes.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " }"; + } + else + built += "}"; + + return std::format_to(context.out(), "{}", built); + } +}; + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::Array& array, std::format_context& context) const + { + std::string built = "[ "; + for (const auto& element : array._objects) built += std::format("{}, ", element); + + if (!array._objects.empty()) // remove trailing commas + { + built.pop_back(); + built.pop_back(); + built += " ]"; + } + else + built += "]"; + + return std::format_to(context.out(), "{}", built); + } +}; + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::Object::ObjectProxy& object, std::format_context& context) const + { + return std::format_to(context.out(), "{}", object._object.get()); + } +}; + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& context) { return context.begin(); } + + auto format(const cppjson::Object::ConstObjectProxy& object, std::format_context& context) const + { + return std::format_to(context.out(), "{}", object._object.get()); + } +}; diff --git a/cppjson/include/cppjson/object.hpp b/cppjson/include/cppjson/object.hpp index 9f00119..c192cbd 100644 --- a/cppjson/include/cppjson/object.hpp +++ b/cppjson/include/cppjson/object.hpp @@ -1,13 +1,13 @@ #pragma once #include +#include #include #include #include +#include #include #include -#include -#include #include namespace cppjson @@ -26,10 +26,10 @@ namespace cppjson { public: explicit JsonObject(); - JsonObject(const JsonObject& other); - JsonObject(JsonObject&& other) noexcept; - JsonObject& operator=(const JsonObject& other); - JsonObject& operator=(JsonObject&& other) noexcept; + JsonObject(const JsonObject& other); + JsonObject(JsonObject&& other) noexcept; + JsonObject& operator=(const JsonObject& other); + JsonObject& operator=(JsonObject&& other) noexcept; ~JsonObject(); template @@ -69,18 +69,18 @@ namespace cppjson class ObjectProxy { - public: + public: explicit ObjectProxy(JsonObject& object) : _object(std::ref(object)) {} template - requires (!std::same_as, JsonObject>) + requires(!std::same_as, JsonObject>) explicit(false) operator T&() { return this->_object.get().As(); } template - requires (!std::same_as, JsonObject>) + requires(!std::same_as, JsonObject>) explicit(false) operator const T&() const { return this->_object.get().As(); @@ -90,28 +90,32 @@ namespace cppjson std::conditional_t && !std::same_as, void, T&> operator=(T&& assignment) { if constexpr (std::integral && !std::same_as) static_cast(*this) = static_cast(assignment); - else return static_cast(*this) = std::forward(assignment); + else + return static_cast(*this) = std::forward(assignment); } template - std::string& operator=(const char(&str)[N]) + std::string& operator=(const char (&str)[N]) { - return static_cast(*this) = std::string{ str }; + return static_cast(*this) = std::string{str}; } - + ObjectProxy operator[](const std::string& key); - template - ObjectProxy operator[](const char(&key)[N]) { return (*this)[std::string{key}]; } - private: + template + ObjectProxy operator[](const char (&key)[N]) + { + return (*this)[std::string{key}]; + } + + private: std::reference_wrapper _object; friend struct std::formatter; }; - class ConstObjectProxy { - public: + public: explicit ConstObjectProxy(const JsonObject& object) : _object(std::ref(object)) {} template explicit(false) operator const T&() const @@ -120,22 +124,20 @@ namespace cppjson } ConstObjectProxy operator[](const std::string& key) const; - private: + + private: std::reference_wrapper _object; friend struct std::formatter; }; - ObjectProxy operator[](const std::string& key) - { - return ObjectProxy{ this->_nodes[key] }; - } + ObjectProxy operator[](const std::string& key) { return ObjectProxy{this->_nodes[key]}; } ConstObjectProxy operator[](const std::string& key) const { if (!this->_nodes.contains(key)) throw std::logic_error("Invalid key" + key); - return ConstObjectProxy{ this->_nodes.at(key) }; + return ConstObjectProxy{this->_nodes.at(key)}; } private: @@ -147,163 +149,39 @@ namespace cppjson class Array { - public: + public: explicit Array() = default; ~Array() = default; - Object::ObjectProxy operator[]() - { - return Object::ObjectProxy{ this->_objects.emplace_back() }; - } + Object::ObjectProxy operator[]() { return Object::ObjectProxy{this->_objects.emplace_back()}; } -Object::ObjectProxy EmplaceBack(const auto& object = nullptr) + Object::ObjectProxy EmplaceBack(const auto& object) { - if constexpr(std::same_as) - return Object::ObjectProxy{ this->_objects.emplace_back() }; - else return Object::ObjectProxy{ this->_objects.emplace_back(object) }; + if constexpr (std::same_as) return Object::ObjectProxy{this->_objects.emplace_back()}; + else + { + auto& emplaced = this->_objects.emplace_back(); + emplaced.As>() = object; + return Object::ObjectProxy{emplaced}; + } } - Object::ObjectProxy operator[](const int index) + Object::ObjectProxy operator[](const std::size_t index) { if (index >= this->_objects.size()) throw std::logic_error("Out of bound"); - return Object::ObjectProxy{ this->_objects.at(index) }; + return Object::ObjectProxy{this->_objects.at(index)}; } - Object::ConstObjectProxy operator[](const int index) const + Object::ConstObjectProxy operator[](const std::size_t index) const { if (index >= this->_objects.size()) throw std::logic_error("Out of bound"); - return Object::ConstObjectProxy{ this->_objects.at(index) }; + return Object::ConstObjectProxy{this->_objects.at(index)}; } - private: + + private: std::vector _objects{}; friend struct std::formatter; friend struct std::formatter; }; } // namespace cppjson - - -template <> -struct std::formatter -{ - constexpr auto parse(std::format_parse_context& context) { return context.begin(); } - - auto format(const cppjson::JsonObject& object, std::format_context& context) const - { - switch (object._dataType) - { - case cppjson::JsonType::Null: return std::format_to(context.out(), "null"); - case cppjson::JsonType::Bool: return std::format_to(context.out(), "{}", object.DangerousAs()); - case cppjson::JsonType::Number: return std::format_to(context.out(), "{}", object.DangerousAs()); - case cppjson::JsonType::String: return std::format_to(context.out(), "\"{}\"", object.DangerousAs()); - case cppjson::JsonType::Object: - { - const auto& node = object.DangerousAs(); - - std::string built = "{ "; - for (const auto& [key, value] : node._nodes) - built += std::format("\"{}\": {}, ", key, value); - - if (!node._nodes.empty()) // remove trailing commas - { - built.pop_back(); - built.pop_back(); - built += " }"; - } - else built += "}"; - - return std::format_to(context.out(), "{}", built); - } - case cppjson::JsonType::Array: - { - const auto& array = object.DangerousAs(); - - std::string built = "[ "; - for (const auto& element : array._objects) - built += std::format("{}, ", element); - - if (!array._objects.empty()) // remove trailing commas - { - built.pop_back(); - built.pop_back(); - built += " ]"; - } - else built += "]"; - - return std::format_to(context.out(), "{}", built); - } - } - - throw std::logic_error("Unknown type"); - } -}; - -template <> -struct std::formatter -{ - constexpr auto parse(std::format_parse_context& context) { return context.begin(); } - - auto format(const cppjson::Object& object, std::format_context& context) const - { - std::string built = "{ "; - for (const auto& [key, value] : object._nodes) - built += std::format("\"{}\": {}, ", key, value); - - if (!object._nodes.empty()) // remove trailing commas - { - built.pop_back(); - built.pop_back(); - built += " }"; - } - else built += "}"; - - return std::format_to(context.out(), "{}", built); - } -}; - -template <> -struct std::formatter -{ - constexpr auto parse(std::format_parse_context& context) { return context.begin(); } - - auto format(const cppjson::Array& array, std::format_context& context) const - { - std::string built = "[ "; - for (const auto& element : array._objects) - built += std::format("{}, ", element); - - if (!array._objects.empty()) // remove trailing commas - { - built.pop_back(); - built.pop_back(); - built += " ]"; - } - else built += "]"; - - return std::format_to(context.out(), "{}", built); - } -}; - - -template <> -struct std::formatter -{ - constexpr auto parse(std::format_parse_context& context) { return context.begin(); } - - auto format(const cppjson::Object::ObjectProxy& object, std::format_context& context) const - { - return std::format_to(context.out(), "{}", object._object.get()); - } -}; - - -template <> -struct std::formatter -{ - constexpr auto parse(std::format_parse_context& context) { return context.begin(); } - - auto format(const cppjson::Object::ConstObjectProxy& object, std::format_context& context) const - { - return std::format_to(context.out(), "{}", object._object.get()); - } -}; diff --git a/cppjson/src/cppjson.cpp b/cppjson/src/cppjson.cpp index b35c1a8..e69de29 100644 --- a/cppjson/src/cppjson.cpp +++ b/cppjson/src/cppjson.cpp @@ -1,4 +0,0 @@ -#include -#include - -void cppjson::hello_world() { std::println("Hewwo wowld"); } From a1da784312d046c363eb2de024864f9049218639 Mon Sep 17 00:00:00 2001 From: TymianekPL Date: Fri, 23 May 2025 17:33:04 +0200 Subject: [PATCH 18/18] Formatting issues Signed-off-by: TymianekPL --- cppjson/include/cppjson/cppjson.hpp | 2 +- cppjson/include/cppjson/formatter.hpp | 2 +- cppjson/src/object.cpp | 34 ++++++++++++--------------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/cppjson/include/cppjson/cppjson.hpp b/cppjson/include/cppjson/cppjson.hpp index 817f431..6a0cebf 100644 --- a/cppjson/include/cppjson/cppjson.hpp +++ b/cppjson/include/cppjson/cppjson.hpp @@ -1,4 +1,4 @@ #pragma once -#include "object.hpp" #include "formatter.hpp" +#include "object.hpp" diff --git a/cppjson/include/cppjson/formatter.hpp b/cppjson/include/cppjson/formatter.hpp index 800f038..6e5da70 100644 --- a/cppjson/include/cppjson/formatter.hpp +++ b/cppjson/include/cppjson/formatter.hpp @@ -1,5 +1,5 @@ -#include "object.hpp" #include +#include "object.hpp" template <> struct std::formatter diff --git a/cppjson/src/object.cpp b/cppjson/src/object.cpp index feb29e1..955950d 100644 --- a/cppjson/src/object.cpp +++ b/cppjson/src/object.cpp @@ -1,7 +1,7 @@ #include "cppjson/object.hpp" +#include #include #include -#include #include constexpr std::size_t DataStorageSize = std::max({sizeof(std::string), sizeof(cppjson::Object), sizeof(double), sizeof(bool)}); @@ -11,34 +11,34 @@ cppjson::JsonObject::JsonObject() : _dataStorage(static_cast(::opera cppjson::JsonObject::JsonObject(const cppjson::JsonObject& other) { if (other._dataStorage == nullptr) return; - + this->_dataType = other._dataType; this->_dataStorage = static_cast(::operator new(DataStorageSize)); std::memcpy(this->_dataStorage, other._dataStorage, DataStorageSize); } cppjson::JsonObject::JsonObject(JsonObject&& other) noexcept { - this->_dataType = std::exchange(other._dataType, cppjson::JsonType::Null); - this->_dataStorage = std::exchange(other._dataStorage, static_cast(::operator new(DataStorageSize))); + this->_dataType = std::exchange(other._dataType, cppjson::JsonType::Null); + this->_dataStorage = std::exchange(other._dataStorage, static_cast(::operator new(DataStorageSize))); } cppjson::JsonObject& cppjson::JsonObject::operator=(const cppjson::JsonObject& other) { - if (&other != this) - { + if (&other != this) + { this->_dataType = other._dataType; this->_dataStorage = static_cast(::operator new(DataStorageSize)); - std::memcpy(this->_dataStorage, other._dataStorage, DataStorageSize); + std::memcpy(this->_dataStorage, other._dataStorage, DataStorageSize); } - return *this; + return *this; } cppjson::JsonObject& cppjson::JsonObject::operator=(cppjson::JsonObject&& other) noexcept { - if (&other != this) - { + if (&other != this) + { this->_dataType = std::exchange(other._dataType, cppjson::JsonType::Null); this->_dataStorage = std::exchange(other._dataStorage, static_cast(::operator new(DataStorageSize))); } - return *this; + return *this; } cppjson::JsonObject::~JsonObject() { @@ -48,9 +48,9 @@ cppjson::JsonObject::~JsonObject() void cppjson::JsonObject::Destroy(void) { - using std::string; - using cppjson::Object; using cppjson::Array; + using cppjson::Object; + using std::string; switch (std::exchange(this->_dataType, JsonType::Null)) { @@ -179,13 +179,9 @@ const std::nullptr_t& cppjson::JsonObject::As() const noexcept(f return DangerousAs(); } -cppjson::Object::ObjectProxy cppjson::Object::ObjectProxy::operator[](const std::string& key) -{ - return ObjectProxy{ this->_object.get().As()[key] }; -} - +cppjson::Object::ObjectProxy cppjson::Object::ObjectProxy::operator[](const std::string& key) { return ObjectProxy{this->_object.get().As()[key]}; } cppjson::Object::ConstObjectProxy cppjson::Object::ConstObjectProxy::operator[](const std::string& key) const { - return ConstObjectProxy{ this->_object.get().As()[key] }; + return ConstObjectProxy{this->_object.get().As()[key]}; }