Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions include/gkit/core/reflect/serdebase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "gkit/core/object_id.hpp"
#include "gkit/core/value.hpp"

#include <memory>
#include <string>
#include <utility>

Expand All @@ -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<SerdeData> data;

public:
using WrapperPair = std::pair<std::string, std::string>;

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
22 changes: 21 additions & 1 deletion src/core/reflect/seralize.cpp
Original file line number Diff line number Diff line change
@@ -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 <memory>
#include <stdexcept>
#include <string>
#include <utility>

Expand Down Expand Up @@ -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<SerdeNode>("", 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);
Expand Down
8 changes: 7 additions & 1 deletion src/core/reflect/seralize.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::unique_ptr<SerdeNode>>& {
return this->children;
Expand All @@ -33,13 +34,18 @@ namespace gkit::core::reflect {

class SerdeStruct final {
bool available_flag = false;
std::unique_ptr<SerdeNode> serde_root{};
std::unique_ptr<SerdeNode> 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; }
};
Expand Down
33 changes: 26 additions & 7 deletions src/core/reflect/serdebase.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
#include "gkit/core/reflect/serdebase.hpp"

#include "core/reflect/seralize.hpp"
#include "gkit/core/value.hpp"
#include "seralize.hpp"

#include <memory>
#include <stdexcept>
#include <string>
#include <utility>

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 {};
Expand Down Expand Up @@ -46,21 +50,36 @@ namespace gkit::core::reflect {

} // namespace

auto SerdeBase::seralize(const ObjectId& id) -> std::string {
struct SerdeBase::SerdeData {
SerdeStruct ss {};
};

SerdeBase::SerdeBase() : data(std::make_unique<SerdeData>()) {}
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 {};
}

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
26 changes: 14 additions & 12 deletions test/core/reflect/test_serdebase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 + "\":";
Expand All @@ -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() + "\"";
Expand All @@ -76,30 +76,32 @@ class Json final : public SerdeBase {

auto main() -> int {
SeralizeObject::regist_method();
auto json = Json();
auto obj = gkit::core::UniqueObject::create<SeralizeObject>();
auto serde_str = json.seralize(obj.get_id());
auto json = Json();
auto obj = gkit::core::UniqueObject::create<SeralizeObject>();
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;
}

// ObjectId values do not participate in (de)serialization for now.
auto obj2 = gkit::core::UniqueObject::create<SeralizeObject>();

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;
}
Expand Down
Loading