/
Team8
/
kittycad
Обзор
Документация
Войти
/
Team8
/
kittycad
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/data/serialization.h
628 строк
18 KB
levovix
fix: icons are now embeded into binary
07 янв 2026, 19:47
07 янв 2026, 19:47
48fb22d
Код
Авторство
О чём код?
#pragma once #include <cstdint> #include <iostream> #include <map> #include <sstream> #include "typedefs.h" struct ISerializable; struct RttiDb { std::map<std::string, ISerializable*(*)()> constructors{}; }; struct TraceContext { std::set<ISerializable*>& instances; }; struct DumpContext { int indent{0}; std::ostream& stream; char const* styleNormal = "\033[0m"; char const* styleSyntax = "\033[90m"; char const* styleId = "\033[94m"; char const* styleType = "\033[32m"; char const* styleString = "\033[93m"; char const* styleNumber = "\033[38;2;187;220;170m"; char const* stylePragma = "\033[95m"; char const* indentStr = "⎸ "; char const* declareStr = "- "; }; struct Serializator { // todo: разделить на JsonSerializeContext и JsonDeserializeContext std::iostream& s; // файл std::set<ISerializable*> instances; // для write std::map<int32_t, ref<ISerializable>> instances_map; // для read RttiDb* rttiDb{}; // используется для вызова конструкторов типа по его имени inline Serializator(std::iostream& s) : s(s) {} }; struct ParseError : std::exception { // ParseError(std::string_view message, std::stacktrace stacktrace); ParseError(std::string_view message); ~ParseError() {} std::string msg; const char* what() const noexcept override; }; struct ISerializable : std::enable_shared_from_this<ISerializable> { int32_t m_id{0}; int32_t m_version{0}; ISerializable() = default; virtual ~ISerializable() = default; // добавляет в s.instances все (сохраняемые) объекты которыми владеет этот объект. // (в том числи и сам этот объект). virtual void trace(TraceContext& s) const {} // добавляет в s.instances все (временные, обычно несохраняемые) объекты которыми владеет этот объект. // временные объекты это те которые условно существуют (и с ними может взаимодействовать пользователь), но на самом деле нет. // такие создаются при запуске любого создающего объект инструмента, // и остаются временными до конца работы с инструментом (нажатия ОК). // todo: возможно, если реализовать ctrl+z, временные объекты станут не нужны и можно будет забыть о них virtual void traceTemporary(TraceContext& s) const {} // записывает (только) поля объекта в формате json. virtual void serialize(Serializator& s) const; // считывает одно поле (с названием fieldName) объекта в формате json, ничего не делает если такого поля нет. virtual void deserialize(Serializator& s, std::string_view fieldName); // полное имя типа, так как оно записано в c++ (например, "data::SolidRadialArray"). virtual std::string_view typeName() const; // человекочитаемое имя типа, с локализацией, как оно будет отображаться в дереве построения (например, "Круговой массив"). // перевод берётся как QObject::tr("имя_типа_как_в_c++"). // см. translations/maints_ru.ts и translations/maints_en.ts (да, в данном случае английское название тоже надо локализовывать). virtual std::string typeNameHumanReadable() const; // пишет в человекочитаемом виде только id и тип объекта (без \n в конце). virtual void dumpType(DumpContext& c) const; // пишет в человекочитаемом виде только поля объекта (с \n в конце). virtual void dumpFields(DumpContext& c) const; // пишет в человекочитаемом виде только дочерние объекты (с \n в конце). virtual void dumpTree(DumpContext& c) const; int32_t id() const { return m_id; } void setId(const int32_t &id) { m_id = id; } int32_t version() const { return m_version; } void setVersion(const int32_t &version) { m_version = version; } }; #define IMPLEMENTS_ISerializable(...) \ public: \ void trace(TraceContext& s) const override; \ void traceTemporary(TraceContext& s) const override; \ void serialize(Serializator& s) const override; \ void deserialize(Serializator& s, std::string_view fieldName) override; \ std::string_view typeName() const override; \ std::string typeNameHumanReadable() const override; \ static void rtti_init(RttiDb& db); \ void dumpFields(DumpContext& c) const override; \ void dumpTree(DumpContext& c) const override; ; #define DONT_SERIALIZE // excludes field from serialization #define TRACE_AS_TEMPORARY // includes field for traceTemporary (excludes from trace, works with DONT_SERIALIZE) #define DONT_DUMP // excludes field from dump #define CHILD // use this object as a direct owned child, instead of just reference #define AUTO(...) // automatically generates the body of the function #define SERGEN_INCLUDE(...) // include a header in generated source file #define SERGEN_IGNORE #define SERGEN_IGNORE_END template<typename T> ref<T> asRef(T* v) requires std::derived_from<T, ISerializable> { return std::static_pointer_cast<T>(v->shared_from_this()); } void eatSpaces(Serializator& s); void eat(Serializator& s, char c); bool eatOptional(Serializator& s, char c); void writeQuoted(std::ostream& s, std::string_view x); void write(Serializator& s, std::string_view x); void read(Serializator& s, std::string& x); void skipUnknownObject(Serializator& s); void trace(TraceContext& s, ISerializable const* v); template<typename T> void trace(TraceContext& s, ref<T> const& v) requires std::derived_from<T, ISerializable> { trace(s, (ISerializable const*)v.get()); } template <typename T> void trace(TraceContext& s, std::vector<T> const& v) { for (auto const& elem : v) { traceField(s, elem); } } template <typename T> void traceField(TraceContext& s, T const& v) requires requires (TraceContext& s, T const& v) { trace(s, v); } { trace(s, v); } template <typename T> void traceField(TraceContext& s, T const& v) requires (!requires (TraceContext& s, T const& v) { trace(s, v); }) {} template<typename T> void traceTemporary(TraceContext& s, ref<T> const& v) requires std::derived_from<T, ISerializable> { trace(s, (ISerializable const*)v.get()); } template<typename T> void traceTemporary(TraceContext& s, ptr<T> const& v) requires std::derived_from<T, ISerializable> { if (!v.expired()) { traceTemporary(s, v.lock()); } } template <typename T> void traceTemporary(TraceContext& s, std::vector<T> const& v) { for (auto const& elem : v) { traceTemporaryField(s, elem); } } template <typename T> void traceTemporaryField(TraceContext& s, T const& v) requires requires (TraceContext& s, T const& v) { traceTemporary(s, v); } { traceTemporary(s, v); } template <typename T> void traceTemporaryField(TraceContext& s, T const& v) requires (!requires (TraceContext& s, T const& v) { traceTemporary(s, v); }) {} void write(Serializator &s, ISerializable const& v); void read(Serializator &s, ISerializable& v); void write(Serializator &s, ISerializable const* v); void read(Serializator &s, ISerializable*& v); template<typename T> void write(Serializator &s, T const& v) requires std::derived_from<T, ISerializable> { write(s, (ISerializable const&)v); } template<typename T> void read(Serializator &s, T& v) requires std::derived_from<T, ISerializable> { read(s, (ISerializable&)v); } template<typename T> void write(Serializator &s, T const* v) requires std::derived_from<T, ISerializable> { write(s, (ISerializable const*)v); } template<typename T> void read(Serializator &s, T* v) requires std::derived_from<T, ISerializable> { read(s, (ISerializable*)v); } template<typename T> void write(Serializator &s, ref<T> const& v) requires std::derived_from<T, ISerializable> { write(s, (ISerializable*)v.get()); } template<typename T> void read(Serializator &s, ref<T>& v) requires std::derived_from<T, ISerializable> { ISerializable* v_raw; read(s, v_raw); if (v_raw != nullptr) { v = std::dynamic_pointer_cast<T>(v_raw->shared_from_this()); } else { v.reset(); } } template<typename T> void write(Serializator &s, ptr<T> const& v) requires std::derived_from<T, ISerializable> { write(s, (ISerializable*)v.lock().get()); } template<typename T> void read(Serializator &s, ptr<T>& v) requires std::derived_from<T, ISerializable> { ISerializable* v_raw; read(s, v_raw); if (v_raw != nullptr) { v = std::dynamic_pointer_cast<T>(v_raw->shared_from_this()); } else { v.reset(); } } void write(Serializator& s, Vec2 v); void read(Serializator& s, Vec2& v); void write(Serializator& s, Vec3 v); void read(Serializator& s, Vec3& v); void write(Serializator& s, bool v); void read(Serializator& s, bool& v); inline void write(Serializator& s, Point2 v) { write(s, *(Vec2*)&v); } inline void read(Serializator& s, Point2& v) { read(s, *(Vec2*)&v); } inline void write(Serializator& s, Point3 v) { write(s, *(Vec3*)&v); } inline void read(Serializator& s, Point3& v) { read(s, *(Vec3*)&v); } // Записывает целиком json часть файла .kcd, с коневым элементом `v` void writeRoot(Serializator& s, ref<ISerializable> const& v); // Читает целиком json часть файла .kcd, сохраняет указатель на корневой элемент в `v` void readRoot(Serializator& s, ref<ISerializable>& v); // Назначает всем объектам в иерархии с корнем `v`, у которых id == 0, последовательно сгенерированные id void assignMissingIds(Serializator& s, ISerializable* v); template<typename T> void writeField(Serializator& s, std::string_view name, T const& field) requires requires (Serializator& s, T const& x) { write(s, x); } { s.s << ','; write(s, name); s.s << ':'; write(s, field); } template<typename T> void readField(Serializator& s, std::string_view readName, std::string_view name, T& field) requires requires (Serializator& s, T& x) { read(s, x); } { if (readName == name) { read(s, field); } } void dump(DumpContext& c, ISerializable const& v); void dump(DumpContext& c, ISerializable const* v); void dump(DumpContext& c, ptr<ISerializable> const& v); void dumpTree(DumpContext& c, std::string_view name, ISerializable const& v); void addEmptyLine(DumpContext& c); void addDeclarationIndent(DumpContext& c); void dump(DumpContext& c, Vec2 v); void dump(DumpContext& c, Vec3 v); void dump(DumpContext& c, bool v); void dump(DumpContext& c, std::string const& v); inline void dump(DumpContext& c, Point2 v) { dump(c, *(Vec2*)&v); } inline void dump(DumpContext& c, Point3 v) { dump(c, *(Vec3*)&v); } template<typename T> void dump(DumpContext& c, ptr<T> const& v) requires std::derived_from<T, ISerializable> { if (v.expired()) { dump(c, ptr<ISerializable>()); } else { dump(c, v.lock().get()); } } template<typename Range> void dumpList(DumpContext& c, Range const& v) { c.stream << c.styleSyntax << '[' << c.styleNormal; bool needComma = false; for (auto const& elem : v) { if (needComma) { c.stream << c.styleSyntax << ", "; } else { needComma = true; } dump(c, elem); } c.stream << c.styleSyntax << ']' << c.styleNormal; } template<typename T> void dump(DumpContext& c, std::vector<T> const& v) { dumpList(c, v); } template<typename T, size_t N> void dump(DumpContext& c, std::array<T, N> const& v) { dumpList(c, v); } template<typename T> void dumpField(DumpContext& c, std::string_view name, T const& field) requires requires (DumpContext& c, T const& x) { dump(c, x); } { c.stream << c.styleSyntax; for (int ind = c.indent; ind > 0; --ind) { c.stream << c.indentStr; } c.stream << c.styleNormal << name << c.styleSyntax << ": "; dump(c, field); c.stream << c.styleNormal << "\n"; } template<typename T> void dumpField(DumpContext& c, std::string_view name, T const& field) requires (!requires (DumpContext& c, T const& x) { dump(c, x); }) { c.stream << c.styleSyntax; for (int ind = c.indent; ind > 0; --ind) { c.stream << c.indentStr; } c.stream << c.styleNormal << name << c.styleSyntax << ": " << c.stylePragma << "???"; c.stream << c.styleNormal << "\n"; } template<typename Range> void dumpTreeList(DumpContext& c, std::string_view name, Range const& v) { ++c.indent; int i = 0; bool needNewline = false; for (auto const& obj : v) { if (needNewline) { addEmptyLine(c); } needNewline = true; if (obj == nullptr) { addDeclarationIndent(c); c.stream << c.stylePragma << name << c.styleSyntax << "[" << c.styleNumber << i << c.styleSyntax << "] = " << c.styleId << "nullptr" << c.styleNormal << "\n"; } else { obj->dumpType(c); c.stream << " " << c.stylePragma << name << c.styleSyntax << "[" << c.styleNumber << i << c.styleSyntax << "]" << c.styleNormal << ":\n"; obj->dumpFields(c); obj->dumpTree(c); } ++i; } --c.indent; } template<typename T> void dumpTree(DumpContext& c, std::string_view name, std::vector<ref<T>> const& v) requires std::derived_from<T, ISerializable> { dumpTreeList(c, name, v); } template<typename T, size_t N> void dumpTree(DumpContext& c, std::string_view name, std::array<ref<T>, N> const& v) requires std::derived_from<T, ISerializable> { dumpTreeList(c, name, v); } template<typename Range> void dumpTreeListObjs(DumpContext& c, std::string_view name, Range const& v) { ++c.indent; int i = 0; bool needNewline = false; for (auto const& obj : v) { if (needNewline) { addEmptyLine(c); } needNewline = true; obj.dumpType(c); c.stream << " " << c.stylePragma << name << c.styleSyntax << "[" << c.styleNumber << i << c.styleSyntax << "]" << c.styleNormal << ":\n"; obj.dumpFields(c); obj.dumpTree(c); ++i; } --c.indent; } template<typename T> void dumpTree(DumpContext& c, std::string_view name, std::vector<T> const& v) requires std::derived_from<T, ISerializable> { dumpTreeListObjs(c, name, v); } template<typename T, size_t N> void dumpTree(DumpContext& c, std::string_view name, std::array<T, N> const& v) requires std::derived_from<T, ISerializable> { dumpTreeListObjs(c, name, v); } template<typename T> void dumpTree(DumpContext& c, std::string_view name, ref<T> const& v) requires std::derived_from<T, ISerializable> { if (v == nullptr) { addDeclarationIndent(c); c.stream << c.stylePragma << name << c.styleSyntax << " = " << c.styleId << "nullptr" << c.styleNormal << "\n"; } else { dumpTree(c, name, *v); } } #define make_serializer(typ) \ inline void write(Serializator& s, typ v) { s.s << v; } \ inline void read(Serializator& s, typ& v) { \ auto pos = s.s.tellg(); \ s.s >> v; \ if (!s.s) { \ s.s.clear(); \ s.s.seekg(pos); \ throw ParseError("unable to read number"/*, std::stacktrace::current()*/); \ } \ } \ inline void dump(DumpContext& c, typ v) { c.stream << c.styleNumber << v; } ; make_serializer(int64_t) make_serializer(int32_t) make_serializer(int16_t) make_serializer(int8_t) make_serializer(uint64_t) make_serializer(uint32_t) make_serializer(uint16_t) make_serializer(uint8_t) make_serializer(float) make_serializer(double) #undef make_serializer template<typename Range> void writeList(Serializator& s, Range const& v) { s.s << '['; bool needComma = false; for (auto const& elem : v) { if (needComma) { s.s << ','; } else { needComma = true; } write(s, elem); } s.s << ']'; } template<typename T> void write(Serializator& s, std::vector<T> const& v) { writeList(s, v); } template<typename T, size_t N> void write(Serializator& s, std::array<T, N> const& v) { writeList(s, v); } template<typename T> void read(Serializator& s, std::vector<T>& v) { eatSpaces(s); eat(s, '['); while (true) { eatSpaces(s); if (eatOptional(s, ']')) { break; } eatSpaces(s); v.emplace_back(); read(s, v.back()); eatSpaces(s); eatOptional(s, ','); } } template<typename Enum> void write(Serializator& s, Enum v) requires std::is_enum_v<Enum> { write(s, (int)v); // todo: использовать строки для записи enum'ов в json } template<typename Enum> void read(Serializator& s, Enum& v) requires std::is_enum_v<Enum> { int i; read(s, i); v = static_cast<Enum>(i); } template<typename T> void print(T const& v) { DumpContext c {0, std::cout}; dump(c, v); std::cout << "\n"; } template<typename T> void print(T const& v) requires std::derived_from<T, ISerializable> { DumpContext c {0, std::cout}; dump(c, static_cast<ISerializable const&>(v)); std::cout << "\n"; } template<typename T> void print(T const* v) { print(*v); } template<typename T> void print(ptr<T> const& v) { print(*v.lock()); } template<typename T> void print(ref<T> const& v) { print(*v); } template<typename T> std::string dumpTypeAsStr(T const& v) requires std::derived_from<T, ISerializable> { std::stringstream ss; DumpContext c {0, ss}; v.dumpType(c); return ss.str(); } template<typename T> std::string dumpTypeAsStr(T const* v) { return dumpTypeAsStr(*v); } template<typename T> std::string dumpTypeAsStr(ptr<T> const& v) { return dumpTypeAsStr(*v.lock()); } template<typename T> std::string dumpTypeAsStr(ref<T> const& v) { return dumpTypeAsStr(*v); } void throwWithLineInfo(Serializator& s, ParseError&& e, std::string_view filename); void initAllRtti(RttiDb& db); // реализация генерируется с помощью codegen/sergen.nim