#pragma once #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(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(float); 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) { return std::make_unique(value); } T & v; }; template struct SelectionT : public SelectionV { using SelectionV::SelectionV; using Selection::setValue; using P = std::conditional_t, T, T &&>; void setValue(P evalue) override { std::swap(this->v, evalue); } void write(const Writer & out) const override { out.pushValue(this->v); } }; struct Persistable; struct PersistenceStore { 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 }; template [[nodiscard]] inline bool persistValue(const std::string_view key, T & value) { auto s = std::make_unique(value); const auto act {setName(key, *s)}; if (act != NameAction::Ignore) { sel = std::move(s); if (act == NameAction::HandleAndContinue) { selHandler(); } } return (act != NameAction::Push); } virtual NameAction setName(const std::string_view key, const Selection &) = 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); NameAction setName(const std::string_view key, const Selection &) 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); NameAction setName(const std::string_view key, const Selection &) 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 = glm::vec; struct Members : public SelectionV { using SelectionV::SelectionV; void beforeValue(Stack & stk) override { stk.push(SelectionV::make(this->v[idx++])); } glm::length_t idx {0}; void write(const Writer & out) const override { for (glm::length_t n = 0; n < L; 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 (glm::length_t n = 0; n < L; 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 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 { map.emplace(std::invoke(Key, s), s); stk.pop(); } private: std::shared_ptr 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 { container.emplace_back(std::move(s)); stk.pop(); } private: Type s; Container & container; }; struct Persistable { Persistable() = default; virtual ~Persistable() = default; DEFAULT_MOVE_COPY(Persistable); virtual bool persist(PersistenceStore & store) = 0; [[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; } // 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; 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 { sharedObjects.emplace(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); 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, false> { using SelectionPtrBase, false>::SelectionPtrBase; }; template struct SelectionT> : public SelectionPtrBase, true> { using SelectionPtrBase, true>::SelectionPtrBase; using SelectionPtrBase, true>::setValue; void setValue(std::string && id) override { if (auto teo = std::dynamic_pointer_cast(sharedObjects.at(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)