diff --git a/include/gkit/core/reflect/serdebase.hpp b/include/gkit/core/reflect/serdebase.hpp index a429633..e87ba8d 100644 --- a/include/gkit/core/reflect/serdebase.hpp +++ b/include/gkit/core/reflect/serdebase.hpp @@ -3,6 +3,7 @@ #include "gkit/core/object_id.hpp" #include "gkit/core/value.hpp" +#include #include #include @@ -16,35 +17,45 @@ namespace gkit::core::reflect { * (Json / Xml / Toml ...) by overriding the wrapper / gap / leaf hooks. */ class SerdeBase { + struct SerdeData; + std::unique_ptr data; + public: using WrapperPair = std::pair; - virtual ~SerdeBase() = default; + SerdeBase(); + virtual ~SerdeBase(); + + /** + * @brief Serialize an object to serdedata + * @throw @ref std::invalid_argument, if objectid is invalid + */ + auto from(const ObjectId& id) -> void; /** - * @brief Serialize an object (by id) into the target format. - * @throws std::invalid_argument if the id is not available. - * @return Empty string when the object tree cannot be built. + * @brief Serialize a value to serdedata + * @throw @ref std::invalid_argument, if value is null. */ - [[nodiscard]] auto seralize(const ObjectId& id) -> std::string; + auto from(const Value& v) -> void; /** - * @brief Serialize a single value node (with optional key). + * @brief Serialize serdedata to string. + * @return Empty string when the SerdeBase cannot be built. */ - [[nodiscard]] auto seralize(const std::string& key, const Value& v) -> std::string; + [[nodiscard]] auto to_string() const noexcept -> std::string; // ===================================================================== // Format hooks (pure virtual) // ===================================================================== /// @brief Begin/end delimiters around a node (keyed or unkeyed). - virtual auto wrapper(Type type, const std::string& key) -> WrapperPair = 0; + virtual auto wrapper(Type type, const std::string& key) const -> WrapperPair = 0; /// @brief Separator between sibling elements of a container. - virtual auto element_gap(Type type) -> std::string = 0; + virtual auto element_gap(Type type) const -> std::string = 0; /// @brief Encode a leaf value into the target format. - virtual auto leaf_value(const Value& v) -> std::string = 0; + virtual auto leaf_value(const Value& v) const -> std::string = 0; }; } // namespace gkit::core::reflect diff --git a/src/core/reflect/seralize.cpp b/src/core/reflect/seralize.cpp index 28fa482..2a7c40a 100644 --- a/src/core/reflect/seralize.cpp +++ b/src/core/reflect/seralize.cpp @@ -1,10 +1,12 @@ #include "core/reflect/seralize.hpp" #include "core/object_pool.hpp" +#include "gkit/core/object_id.hpp" #include "gkit/core/reflect/registry.hpp" #include "gkit/core/value.hpp" #include +#include #include #include @@ -34,11 +36,29 @@ namespace gkit::core::reflect { * SerdeStruct */ SerdeStruct::SerdeStruct(Value v) noexcept { // NOLINT(performance-unnecessary-value-param) + this->from(std::move(v)); + } + + SerdeStruct::SerdeStruct(const ObjectId v) noexcept { + this->from(v); + } + + auto SerdeStruct::operator[](const std::string& key) -> Value& { + for (const auto& node : this->serde_root->get_children()) { + if (node->get_key() == key) { + return node->get_value(); + } + } + + throw std::invalid_argument("element not found"); + } + + auto SerdeStruct::from(Value v) noexcept -> void { // NOLINT(performance-unnecessary-value-param) this->serde_root = std::make_unique("", std::move(v)); this->available_flag = true; } - SerdeStruct::SerdeStruct(const ObjectId v) noexcept { + auto SerdeStruct::from(ObjectId v) noexcept -> void { if (!v.available()) return; auto* obj_ptr = ObjectPool::instance().deref_from(v); diff --git a/src/core/reflect/seralize.hpp b/src/core/reflect/seralize.hpp index d286784..76b06f2 100644 --- a/src/core/reflect/seralize.hpp +++ b/src/core/reflect/seralize.hpp @@ -25,6 +25,7 @@ namespace gkit::core::reflect { [[nodiscard]] inline auto get_key() const -> const std::string& { return this->key; } [[nodiscard]] inline auto get_type() const -> Type { return this->type; } + [[nodiscard]] inline auto get_value() -> Value& { return this->value; } [[nodiscard]] inline auto get_value() const -> const Value& { return this->value; } [[nodiscard]] inline auto get_children() const -> const std::vector>& { return this->children; @@ -33,13 +34,18 @@ namespace gkit::core::reflect { class SerdeStruct final { bool available_flag = false; - std::unique_ptr serde_root{}; + std::unique_ptr serde_root = nullptr; public: + SerdeStruct() = default; explicit SerdeStruct(Value v) noexcept; explicit SerdeStruct(const ObjectId v) noexcept; ~SerdeStruct() = default; + auto operator[](const std::string& key) -> Value&; + + auto from(Value v) noexcept -> void; + auto from(const ObjectId v) noexcept -> void; [[nodiscard]] inline auto available() -> bool { return this->available_flag; } [[nodiscard]] inline auto root() const -> const SerdeNode& { return *this->serde_root; } }; diff --git a/src/core/reflect/serdebase.cpp b/src/core/reflect/serdebase.cpp index 990fdb7..2b58079 100644 --- a/src/core/reflect/serdebase.cpp +++ b/src/core/reflect/serdebase.cpp @@ -1,15 +1,19 @@ #include "gkit/core/reflect/serdebase.hpp" #include "core/reflect/seralize.hpp" +#include "gkit/core/value.hpp" +#include "seralize.hpp" +#include #include #include +#include namespace gkit::core::reflect { namespace { - auto render_node(const SerdeNode& node, SerdeBase& base) -> std::string { + auto render_node(const SerdeNode& node, const SerdeBase& base) -> std::string { // ObjectId values do not participate in (de)serialization for now. if (node.get_type() == Type::ObjectId) { return {}; @@ -46,12 +50,31 @@ namespace gkit::core::reflect { } // namespace - auto SerdeBase::seralize(const ObjectId& id) -> std::string { + struct SerdeBase::SerdeData { + SerdeStruct ss {}; + }; + + SerdeBase::SerdeBase() : data(std::make_unique()) {} + SerdeBase::~SerdeBase() {} + + auto SerdeBase::from(const ObjectId& id) -> void { if (!id.available()) { throw std::invalid_argument("id is not available"); } - SerdeStruct ss(id); + this->data->ss.from(id); + } + + auto SerdeBase::from(const Value& v) -> void { + if (v.type() == Type::Null) { + throw std::invalid_argument("id is not available"); + } + + this->data->ss.from(v); + } + + auto SerdeBase::to_string() const noexcept -> std::string { + auto& ss = this->data->ss; if (!ss.available()) { return {}; } @@ -59,8 +82,4 @@ namespace gkit::core::reflect { return render_node(ss.root(), *this); } - auto SerdeBase::seralize(const std::string& key, const Value& v) -> std::string { - return render_node(SerdeNode(key, v), *this); - } - } // namespace gkit::core::reflect diff --git a/test/core/reflect/test_serdebase.cpp b/test/core/reflect/test_serdebase.cpp index e36ecb8..e161fba 100644 --- a/test/core/reflect/test_serdebase.cpp +++ b/test/core/reflect/test_serdebase.cpp @@ -15,7 +15,7 @@ class SeralizeObject : public gkit::core::Object { std::string str = "Hello my girl"; gkit::core::Array arr = {Value(100), Value(200.f), Value("300")}; - gkit::core::Map map = {{"map_num", Value(100)}, {"map_fnum", Value(200.f)}, {"map_str", Value("")}}; + gkit::core::Map map = {{"map_num", Value(100)}, {"map_fnum", Value(200.f)}, {"map_str", Value("str")}}; public: static const char* c_name; @@ -33,7 +33,7 @@ class SeralizeObject : public gkit::core::Object { const char* SeralizeObject::c_name = "SeralizeObject"; class Json final : public SerdeBase { - auto wrapper(gkit::core::Type type, const std::string& key) -> WrapperPair override { + auto wrapper(gkit::core::Type type, const std::string& key) const -> WrapperPair override { std::string begin; if (!key.empty()) { begin += "\"" + key + "\":"; @@ -51,9 +51,9 @@ class Json final : public SerdeBase { return {begin, end}; } - auto element_gap(gkit::core::Type /*type*/) -> std::string override { return ","; } + auto element_gap(gkit::core::Type /*type*/) const -> std::string override { return ","; } - auto leaf_value(const Value& v) -> std::string override { + auto leaf_value(const Value& v) const -> std::string override { switch (v.type()) { case gkit::core::Type::String: { return "\"" + v.as_string() + "\""; @@ -76,13 +76,14 @@ class Json final : public SerdeBase { auto main() -> int { SeralizeObject::regist_method(); - auto json = Json(); - auto obj = gkit::core::UniqueObject::create(); - auto serde_str = json.seralize(obj.get_id()); + auto json = Json(); + auto obj = gkit::core::UniqueObject::create(); + json.from(obj.get_id()); + auto serde_str = json.to_string(); std::cout << serde_str << '\n'; const auto expect = - R"({"num":10,"f_num":20.000000,"str":"Hello my girl","arr":[100,200.000000,"300"],"map":{"map_fnum":200.000000,"map_num":100,"map_str":""}})"; + R"({"num":10,"f_num":20.000000,"str":"Hello my girl","arr":[100,200.000000,"300"],"map":{"map_fnum":200.000000,"map_num":100,"map_str":"str"}})"; if (serde_str != expect) { std::cerr << "unexpected output:\n got: " << serde_str << '\n' << " expected: " << expect << '\n'; return 1; @@ -90,16 +91,17 @@ auto main() -> int { // ObjectId values do not participate in (de)serialization for now. auto obj2 = gkit::core::UniqueObject::create(); - - const auto ref_str = json.seralize("ref", Value(obj2.get_id())); + json.from(Value(obj2.get_id())); + const auto ref_str = json.to_string(); if (!ref_str.empty()) { std::cerr << "expected ObjectId to be skipped, got: " << ref_str << '\n'; return 1; } const gkit::core::Array mixed = {Value(1), Value(obj2.get_id()), Value(2)}; - const auto mixed_str = json.seralize("mixed", Value(mixed)); - if (mixed_str != "\"mixed\":[1,2]") { + json.from(Value(mixed)); + const auto mixed_str = json.to_string(); + if (mixed_str != "[1,2]") { std::cerr << "unexpected mixed array output: " << mixed_str << '\n'; return 1; }