From 112c02f40806f550370b3928f3c1965b5bee5262 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 8 May 2021 20:16:32 +0100 Subject: Implement writing shared objects with @id --- lib/persistence.cpp | 56 ++++++++++++++++++++++++++++++++++++++++ lib/persistence.h | 66 +++++++++++++++++++---------------------------- test/test-persistence.cpp | 33 +++++++++++++++++------- 3 files changed, 105 insertions(+), 50 deletions(-) diff --git a/lib/persistence.cpp b/lib/persistence.cpp index 4f1be8f..b85bff2 100644 --- a/lib/persistence.cpp +++ b/lib/persistence.cpp @@ -1,5 +1,6 @@ #include "persistence.h" #include +#include namespace Persistence { using Factories @@ -26,6 +27,61 @@ namespace Persistence { return namedTypeFactories.at(t).second(); } + [[nodiscard]] std::string + Persistable::getId() const + { + std::stringstream ss; + ss << std::hex << this; + return ss.str(); + } + + PersistenceSelect::PersistenceSelect(const std::string & n) : name {n} { } + + PersistenceStore::NameAction + PersistenceSelect::setName(const std::string_view key) + { + return (key == name) ? NameAction::Push : NameAction::Ignore; + } + + void + PersistenceSelect::setType(const std::string_view, const Persistable *) + { + } + + PersistenceWrite::PersistenceWrite(const Writer & o, bool sh) : out {o}, shared {sh} { } + + PersistenceStore::NameAction + PersistenceWrite::setName(const std::string_view key) + { + if (!first) { + out.nextValue(); + } + else { + first = false; + } + out.pushKey(key); + return NameAction::HandleAndContinue; + } + + void + PersistenceWrite::selHandler() + { + this->sel->write(out); + }; + + void + PersistenceWrite::setType(const std::string_view tn, const Persistable * p) + { + out.pushKey("@typeid"); + out.pushValue(tn); + first = false; + if (shared) { + out.nextValue(); + out.pushKey("@id"); + out.pushValue(p->getId()); + } + } + void Selection::setValue(float) { diff --git a/lib/persistence.h b/lib/persistence.h index 9a143c4..0eba1ab 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -99,6 +99,7 @@ namespace Persistence { } }; + struct Persistable; struct PersistenceStore { template [[nodiscard]] inline bool persistType(const T * const, const std::type_info & ti); @@ -119,60 +120,33 @@ namespace Persistence { virtual NameAction setName(const std::string_view key) = 0; virtual void selHandler() {}; - virtual void setType(const std::string_view) = 0; + virtual void setType(const std::string_view, const Persistable *) = 0; SelectionPtr sel {}; }; struct PersistenceSelect : public PersistenceStore { - explicit PersistenceSelect(const std::string & n) : name {n} { } + explicit PersistenceSelect(const std::string & n); - NameAction - setName(const std::string_view key) override - { - return (key == name) ? NameAction::Push : NameAction::Ignore; - } + NameAction setName(const std::string_view key) override; - void - setType(const std::string_view) override - { - } + void setType(const std::string_view, const Persistable *) override; const std::string & name; }; struct PersistenceWrite : public PersistenceStore { - explicit PersistenceWrite(const Writer & o) : out {o} { } + explicit PersistenceWrite(const Writer & o, bool sh); - NameAction - setName(const std::string_view key) override - { - if (!first) { - out.nextValue(); - } - else { - first = false; - } - out.pushKey(key); - return NameAction::HandleAndContinue; - } + NameAction setName(const std::string_view key) override; - void - selHandler() override - { - this->sel->write(out); - }; + void selHandler() override; - void - setType(const std::string_view tn) override - { - out.pushKey("@typeid"); - out.pushValue(tn); - first = false; - } + void setType(const std::string_view tn, const Persistable * p) override; bool first {true}; const Writer & out; + bool shared; }; template @@ -267,6 +241,8 @@ namespace Persistence { virtual bool persist(PersistenceStore & store) = 0; + [[nodiscard]] virtual std::string getId() const; + template [[nodiscard]] constexpr static auto typeName() @@ -292,20 +268,22 @@ namespace Persistence { template inline bool - PersistenceStore::persistType(const T * const, const std::type_info & ti) + PersistenceStore::persistType(const T * const v, const std::type_info & ti) { if constexpr (!std::is_abstract_v) { [[maybe_unused]] constexpr auto f = &Persistable::addFactory; } if (typeid(std::decay_t) == ti) { - setType(Persistable::typeName()); + setType(Persistable::typeName(), v); } return true; } - // TODO Move this + // TODO Move these using SharedObjects = std::map>; inline SharedObjects sharedObjects; + using SeenSharedObjects = std::map; + inline SeenSharedObjects seenSharedObjects; template struct SelectionPtrBase : public SelectionV { using T = typename Ptr::element_type; @@ -380,7 +358,7 @@ namespace Persistence { write(const Writer & out) const override { out.beginObject(); - PersistenceWrite pw {out}; + PersistenceWrite pw {out, shared}; this->v->persist(pw); out.endObject(); } @@ -425,6 +403,14 @@ namespace Persistence { write(const Writer & out) const override { if (this->v) { + if constexpr (shared) { + if (const auto existing = seenSharedObjects.find(std::to_address(this->v)); + existing != seenSharedObjects.end()) { + out.pushValue(existing->second); + return; + } + seenSharedObjects.emplace(std::to_address(this->v), this->v->getId()); + } SelectionObj {this->v}.write(out); } else { diff --git a/test/test-persistence.cpp b/test/test-persistence.cpp index ab16166..e13cb7a 100644 --- a/test/test-persistence.cpp +++ b/test/test-persistence.cpp @@ -39,6 +39,12 @@ struct SubObject : public AbsObject { dummy() const override { } + + [[nodiscard]] std::string + getId() const override + { + return "someid"; + } }; struct SubObject2 : public AbsObject { @@ -313,9 +319,8 @@ BOOST_AUTO_TEST_CASE(write_test_dfl) auto to = std::make_unique(); std::stringstream ss; Persistence::JsonWritePersistence {ss}.saveState(to); - // TODO We can omit writing explicit nulls BOOST_CHECK_EQUAL(ss.str(), - R"({"@typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"ptr":null,"aptr":null,"vptr":[]})"); + R"({"@typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"vptr":[]})"); } BOOST_FIXTURE_TEST_CASE(write_test_loaded, JPP) @@ -323,9 +328,8 @@ BOOST_FIXTURE_TEST_CASE(write_test_loaded, JPP) auto to = load_json>(FIXTURESDIR "json/load_object.json"); std::stringstream ss; Persistence::JsonWritePersistence {ss}.saveState(to); - // TODO We can omit writing explicit nulls BOOST_CHECK_EQUAL(ss.str(), - R"({"@typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":true,"pos":[3.14,6.28,1.57],"flts":[3.14,6.28,1.57,0,-1,-3.14],"poss":[[3.14,6.28,1.57],[0,-1,-3.14]],"nest":[[["a","b"],["c","d","e"]],[["f"]],[]],"ptr":{"@typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"ptr":null,"aptr":null,"vptr":[]},"aptr":null,"vptr":[]})"); + R"({"@typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":true,"pos":[3.14,6.28,1.57],"flts":[3.14,6.28,1.57,0,-1,-3.14],"poss":[[3.14,6.28,1.57],[0,-1,-3.14]],"nest":[[["a","b"],["c","d","e"]],[["f"]],[]],"ptr":{"@typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"vptr":[]},"vptr":[]})"); } BOOST_FIXTURE_TEST_CASE(write_test_loaded_abs, JPP) @@ -333,20 +337,19 @@ BOOST_FIXTURE_TEST_CASE(write_test_loaded_abs, JPP) auto to = load_json>(FIXTURESDIR "json/abs.json"); std::stringstream ss; Persistence::JsonWritePersistence {ss}.saveState(to); - // TODO We can omit writing explicit nulls BOOST_CHECK_EQUAL(ss.str(), - R"({"@typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"ptr":null,"aptr":{"@typeid":"SubObject","base":"set base","sub":"set sub"},"vptr":[]})"); + R"({"@typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"aptr":{"@typeid":"SubObject","base":"set base","sub":"set sub"},"vptr":[]})"); } BOOST_FIXTURE_TEST_CASE(write_test_loaded_shared, JPP) { auto to = load_json>(FIXTURESDIR "json/shared_ptr_same.json"); std::stringstream ss; + Persistence::seenSharedObjects.clear(); Persistence::JsonWritePersistence {ss}.saveState(to); - // TODO We can omit writing explicit nulls - // TODO Also not really implemented, but it runs :) - // BOOST_CHECK_EQUAL(ss.str(), - // R"({"@typeid":"SharedTestObject","sptr":{"@typeid":"SubObject","@id":"someid"},"ssptr":"someid"})"); + BOOST_CHECK_EQUAL(Persistence::seenSharedObjects.size(), 1); + BOOST_CHECK_EQUAL(ss.str(), + R"({"@typeid":"SharedTestObject","sptr":{"@typeid":"SubObject","@id":"someid","base":"","sub":""},"ssptr":"someid"})"); } BOOST_DATA_TEST_CASE(write_special_strings, TEST_STRINGS, exp, in) @@ -356,3 +359,13 @@ BOOST_DATA_TEST_CASE(write_special_strings, TEST_STRINGS, exp, in) Persistence::JsonWritePersistence {ss}.saveState(copy); BOOST_CHECK_EQUAL(ss.str(), exp); } + +BOOST_AUTO_TEST_CASE(get_default_id) +{ + SubObject2 so; + const auto id {so.getId()}; + BOOST_TEST_CONTEXT(id) { + auto ptr = std::stoul(id, nullptr, 16); + BOOST_CHECK_EQUAL(ptr, reinterpret_cast(&so)); + } +} -- cgit v1.2.3