#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace glm { template struct vec; } namespace Persistence { struct Selection; using SelectionPtr = std::unique_ptr; using Stack = std::stack; struct Writer { Writer() = default; virtual ~Writer() = default; DEFAULT_MOVE_COPY(Writer); virtual void beginObject() const = 0; virtual void beginArray() const = 0; virtual void pushValue(bool value) const = 0; virtual void pushValue(float value) const = 0; virtual void pushValue(int value) const = 0; virtual void pushValue(std::nullptr_t) const = 0; virtual void pushValue(std::string_view value) const = 0; virtual void nextValue() const = 0; virtual void pushKey(std::string_view k) const = 0; virtual void endArray() const = 0; virtual void endObject() const = 0; }; struct Selection { Selection() = default; virtual ~Selection() = default; DEFAULT_MOVE_COPY(Selection); virtual void setValue(std::string_view); virtual void setValue(bool); virtual void setValue(std::nullptr_t); virtual void setValue(std::string &&); virtual void beginArray(Stack &); virtual void beginObject(Stack &); virtual void endObject(Stack &); virtual void beforeValue(Stack &); [[nodiscard]] virtual SelectionPtr select(const std::string &); [[nodiscard]] virtual bool needsWrite() const; virtual void write(const Writer &) const; }; template struct SelectionT; template struct SelectionV : public Selection { explicit SelectionV(T & value) : v {value} { } void beforeValue(Stack &) override { } [[nodiscard]] static SelectionPtr make(T & value) { return make_s>(value); } template [[nodiscard]] static SelectionPtr make_s(T & value, Extra &&... extra) { return std::make_unique(value, std::forward(extra)...); } T & v; }; template concept Arithmatic = std::is_arithmetic_v; template<> struct SelectionT : public SelectionV { using SelectionV::SelectionV; using Selection::setValue; void setValue(bool evalue) override { this->v = evalue; } void setValue(std::string && evalue) override { using namespace std::literals; if (!(this->v = evalue == "true"sv)) { if (evalue != "false"sv) { throw std::runtime_error("Value conversion failure"); } } } void write(const Writer & out) const override { out.pushValue(this->v); } }; template struct SelectionT : public SelectionV { using SelectionV::SelectionV; using Selection::setValue; void setValue(std::string_view evalue) override { if (auto res = std::from_chars(evalue.data(), evalue.data() + evalue.length(), this->v).ec; res != std::errc {}) { throw std::runtime_error("Value conversion failure"); } } void setValue(std::string && evalue) override { setValue(std::string_view {evalue}); } void write(const Writer & out) const override { out.pushValue(this->v); } }; template struct SelectionT : public SelectionV { using SelectionV::SelectionV; using Selection::setValue; void setValue(T && evalue) override { std::swap(this->v, evalue); } void write(const Writer & out) const override { out.pushValue(this->v); } }; template struct SelectionT> : public SelectionT { explicit SelectionT(std::optional & value) : SelectionT {value.emplace()} { } }; struct Persistable; struct PersistenceStore { using SelectionFactory = std::function; PersistenceStore() = default; virtual ~PersistenceStore() = default; DEFAULT_MOVE_NO_COPY(PersistenceStore); template [[nodiscard]] inline bool persistType(const T * const, const std::type_info & ti); enum class NameAction { Push, HandleAndContinue, Ignore }; using NameActionSelection = std::pair; template [[nodiscard]] inline bool persistValue(const std::string_view key, T & value) { auto [act, s] = setName(key, [&value]() { return std::make_unique(value); }); if (act != NameAction::Ignore) { sel = std::move(s); if (act == NameAction::HandleAndContinue) { selHandler(); } } return (act != NameAction::Push); } [[nodiscard]] virtual NameActionSelection setName(const std::string_view key, SelectionFactory &&) = 0; virtual void selHandler() {}; virtual void setType(const std::string_view, const Persistable *) = 0; SelectionPtr sel {}; }; struct PersistenceSelect : public PersistenceStore { explicit PersistenceSelect(const std::string & n); NameActionSelection setName(const std::string_view key, SelectionFactory &&) override; void setType(const std::string_view, const Persistable *) override; const std::string & name; }; struct PersistenceWrite : public PersistenceStore { explicit PersistenceWrite(const Writer & o, bool sh); NameActionSelection setName(const std::string_view key, SelectionFactory &&) override; void selHandler() override; void setType(const std::string_view tn, const Persistable * p) override; bool first {true}; const Writer & out; bool shared; }; template struct SelectionT> : public SelectionV> { using V = std::span; struct Members : public SelectionV { using SelectionV::SelectionV; void beforeValue(Stack & stk) override { stk.push(SelectionV::make(this->v[idx++])); } std::size_t idx {0}; void write(const Writer & out) const override { for (std::size_t n = 0; n < this->v.size(); n += 1) { if (n) { out.nextValue(); } SelectionT {this->v[n]}.write(out); } } }; using SelectionV::SelectionV; using SelectionV::setValue; void setValue(std::string && s) override { std::stringstream ss {std::move(s)}; for (std::size_t n = 0; n < this->v.size(); n += 1) { ss >> this->v[n]; ss.get(); } } void beginArray(Stack & stk) override { stk.push(this->template make_s(this->v)); } void write(const Writer & out) const override { out.beginArray(); Members {this->v}.write(out); out.endArray(); } }; template struct SelectionT> : public SelectionT> { SelectionT(glm::vec & v) : SelectionT> {spn}, spn {&v[0], L} { } std::span spn; }; template struct SelectionT> : public SelectionV> { using V = std::vector; struct Members : public SelectionV { using SelectionV::SelectionV; void beforeValue(Stack & stk) override { stk.push(SelectionV::make(this->v.emplace_back())); } void write(const Writer & out) const override { for (auto & member : this->v) { if (&member != &this->v.front()) { out.nextValue(); } SelectionT {member}.write(out); } } }; using SelectionV::SelectionV; void beginArray(Stack & stk) override { stk.push(this->template make_s(this->v)); } void write(const Writer & out) const override { out.beginArray(); Members {this->v}.write(out); out.endArray(); } }; template struct MapByMember : public Persistence::SelectionT { MapByMember(Map & m) : Persistence::SelectionT {s}, map {m} { } using Persistence::SelectionT::SelectionT; void endObject(Persistence::Stack & stk) override { // TODO test with unique_ptr map.emplace(std::invoke(Key, s), std::move(s)); Persistence::SelectionT::endObject(stk); } private: Type s; Map & map; }; template struct Appender : public Persistence::SelectionT { Appender(Container & c) : Persistence::SelectionT {s}, container {c} { } using Persistence::SelectionT::SelectionT; void endObject(Persistence::Stack & stk) override { // TODO test with unique_ptr container.emplace_back(std::move(s)); Persistence::SelectionT::endObject(stk); } private: Type s; Container & container; }; struct Persistable { Persistable() = default; virtual ~Persistable() = default; DEFAULT_MOVE_COPY(Persistable); virtual bool persist(PersistenceStore & store) = 0; virtual void postLoad(); [[nodiscard]] virtual std::string getId() const; template [[nodiscard]] constexpr static auto typeName() { constexpr std::string_view name {__PRETTY_FUNCTION__}; constexpr auto s {name.find("T = ") + 4}, e {name.rfind(']')}; return name.substr(s, e - s); } template static void addFactory() __attribute__((constructor)); static void addFactory(const std::string_view, std::function()>, std::function()>); [[nodiscard]] static std::unique_ptr callFactory(const std::string_view); [[nodiscard]] static std::shared_ptr callSharedFactory(const std::string_view); }; template void Persistable::addFactory() { addFactory(typeName(), std::make_unique, std::make_shared); } template inline bool 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(), v); } return true; } class ParseBase { public: using SharedObjects = std::map>; using SharedObjectsWPtr = std::weak_ptr; using SharedObjectsPtr = std::shared_ptr; ParseBase(); DEFAULT_MOVE_NO_COPY(ParseBase); template static auto getShared(auto && k) { return std::dynamic_pointer_cast(Persistence::ParseBase::sharedObjects.lock()->at(k)); } template static auto emplaceShared(T &&... v) { return sharedObjects.lock()->emplace(std::forward(v)...); } protected: Stack stk; private: inline static thread_local SharedObjectsWPtr sharedObjects; SharedObjectsPtr sharedObjectsInstance; }; // TODO Move these using SeenSharedObjects = std::map; inline SeenSharedObjects seenSharedObjects; template struct SelectionPtrBase : public SelectionV { static constexpr auto shared = std::is_copy_assignable_v; using T = typename Ptr::element_type; struct SelectionObj : public SelectionV { struct MakeObjectByTypeName : public SelectionV { using SelectionV::SelectionV; using Selection::setValue; void setValue(std::string && type) override { if constexpr (shared) { auto no = Persistable::callSharedFactory(type); if (auto tno = std::dynamic_pointer_cast(no)) { this->v = std::move(tno); return; } } else { auto no = Persistable::callFactory(type); if (dynamic_cast(no.get())) { this->v.reset(static_cast(no.release())); return; } } throw std::runtime_error("Named type doesn't cast to target type"); } }; struct RememberObjectById : public SelectionV { using SelectionV::SelectionV; using Selection::setValue; void setValue(std::string && id) override { ParseBase::emplaceShared(id, this->v); } }; using SelectionV::SelectionV; [[nodiscard]] SelectionPtr select(const std::string & mbr) override { using namespace std::literals; if (mbr == "p.typeid"sv) { if (this->v) { throw std::runtime_error("cannot set object type after creation"); } return this->template make_s(this->v); } if constexpr (shared) { if (mbr == "p.id"sv) { make_default_as_needed(this->v); return this->template make_s(this->v); } } make_default_as_needed(this->v); PersistenceSelect ps {mbr}; if (this->v->persist(ps)) { throw std::runtime_error("cannot find member: " + mbr); } return std::move(ps.sel); } void endObject(Stack & stk) override { make_default_as_needed(this->v); if (this->v) { this->v->postLoad(); } stk.pop(); } void write(const Writer & out) const override { out.beginObject(); PersistenceWrite pw {out, shared}; this->v->persist(pw); out.endObject(); } }; static inline void make_default_as_needed(Ptr & v) { if (!v) { if constexpr (std::is_abstract_v) { throw std::runtime_error("cannot select member of null abstract object"); } else if constexpr (shared) { v = std::make_shared(); } else { v = std::make_unique(); } } } using SelectionV::SelectionV; using Selection::setValue; void setValue(std::nullptr_t) override { this->v.reset(); } void beginObject(Stack & stk) override { stk.push(this->template make_s(this->v)); } void endObject(Stack & stk) override { stk.pop(); } [[nodiscard]] bool needsWrite() const override { return this->v != nullptr; } void 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 { out.pushValue(nullptr); } } }; template struct SelectionT> : public SelectionPtrBase> { using SelectionPtrBase>::SelectionPtrBase; }; template struct SelectionT> : public SelectionPtrBase> { using SelectionPtrBase>::SelectionPtrBase; using SelectionPtrBase>::setValue; void setValue(std::string && id) override { if (auto teo = ParseBase::getShared(id)) { this->v = std::move(teo); } else { throw std::runtime_error("Named type doesn't cast to target type"); } } }; } #define STORE_TYPE store.persistType(this, typeid(*this)) #define STORE_MEMBER(mbr) STORE_NAME_MEMBER(#mbr, mbr) #define STORE_NAME_MEMBER(name, mbr) store.persistValue>(name, mbr) #define STORE_HELPER(mbr, Helper) STORE_NAME_HELPER(#mbr, mbr, Helper) #define STORE_NAME_HELPER(name, mbr, Helper) store.persistValue(name, mbr)