diff --git a/autogen/src/data/analysis.cpp b/autogen/src/data/analysis.cpp index 40107e5..c2f5971 100644 --- a/autogen/src/data/analysis.cpp +++ b/autogen/src/data/analysis.cpp @@ -174,6 +174,25 @@ static bool hasMethod(CXCursor cur, std::string methodName) { return found; } +static bool hasGenericMethod(CXCursor cur, std::string methodName) { + bool found = false; + x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { + CXCursorKind kind = clang_getCursorKind(cur); + if (kind != CXCursor_CXXMethod) return CXChildVisit_Continue; + + if (x_clang_toString(clang_getCursorSpelling(cur)) != methodName) return CXChildVisit_Continue; + + int numArgs = clang_Cursor_getNumArguments(cur); + CXCursor lastParam = clang_Cursor_getArgument(cur, numArgs - 1); + std::string lastParamType = x_clang_toString(clang_getTypeSpelling(clang_getCursorType(lastParam))); + if (lastParamType != "const TypeMeta") return CXChildVisit_Continue; + + found = true; + return CXChildVisit_Break; + }); + return found; +} + static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) { ClassAnalysis anly; @@ -184,6 +203,8 @@ static void processClass(CXCursor cur, AnalysisState* state, std::string classNa anly.serializedName = result["name"]; anly.hasFromString = hasMethod(cur, "FromString"); anly.isSerializable = hasMethod(cur, "Serialize") && hasMethod(cur, "Deserialize"); + anly.hasGenericDeserializer = hasGenericMethod(cur, "Deserialize"); + anly.hasGenericFromString = hasGenericMethod(cur, "FromString"); if (anly.serializedName == "") anly.serializedName = className; diff --git a/autogen/src/data/analysis.h b/autogen/src/data/analysis.h index a8e55c3..40b7c01 100644 --- a/autogen/src/data/analysis.h +++ b/autogen/src/data/analysis.h @@ -37,6 +37,8 @@ struct ClassAnalysis { std::string headerPath; bool hasFromString; bool isSerializable; + bool hasGenericDeserializer; + bool hasGenericFromString; std::vector properties; std::vector methods; std::vector staticProperties; diff --git a/autogen/src/data/codegen.cpp b/autogen/src/data/codegen.cpp index ec5f9c4..250a23a 100644 --- a/autogen/src/data/codegen.cpp +++ b/autogen/src/data/codegen.cpp @@ -238,13 +238,13 @@ static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) { static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { std::string fqn = state.name; - out << "static int data_gc(lua_State*);\n" - "static int data_index(lua_State*);\n" - "static int data_tostring(lua_State*);\n" - "static const struct luaL_Reg metatable [] = {\n" - " {\"__gc\", data_gc},\n" - " {\"__index\", data_index},\n" - " {\"__tostring\", data_tostring},\n" + out << "static int data_" << state.name << "_gc(lua_State*);\n" + "static int data_" << state.name << "_index(lua_State*);\n" + "static int data_" << state.name << "_tostring(lua_State*);\n" + "static const struct luaL_Reg " << state.name << "_metatable [] = {\n" + " {\"__gc\", data_" << state.name << "_gc},\n" + " {\"__index\", data_" << state.name << "_index},\n" + " {\"__tostring\", data_" << state.name << "_tostring},\n" " {NULL, NULL} /* end of array */\n" "};\n\n"; @@ -256,7 +256,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { " // Create the library's metatable\n" " luaL_newmetatable(L, \"__mt_" << state.name << "\");\n" - " luaL_register(L, NULL, metatable);\n" + " luaL_register(L, NULL, " << state.name << "_metatable);\n" " lua_setmetatable(L, n+1);\n" "}\n\n"; @@ -271,7 +271,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { // Indexing methods and properties - out << "static int data_index(lua_State* L) {\n" + out << "static int data_" << state.name << "_index(lua_State* L) {\n" " " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n" "\n" " std::string key(lua_tostring(L, 2));\n" @@ -323,7 +323,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { // ToString - out << "\nint data_tostring(lua_State* L) {\n" + out << "\nint data_" << state.name << "_tostring(lua_State* L) {\n" " " << fqn << "* this_ = *(" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n" " lua_pushstring(L, std::string(this_->ToString()).c_str());\n" " return 1;\n" @@ -331,7 +331,7 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { // Destructor - out << "\nint data_gc(lua_State* L) {\n" + out << "\nint data_" << state.name << "_gc(lua_State* L) {\n" " " << fqn << "** userdata = (" << fqn << "**)luaL_checkudata(L, 1, \"__mt_" << state.name << "\");\n" " delete *userdata;\n" " return 0;\n" @@ -341,9 +341,12 @@ static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) { static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) { std::string fqn = state.name; - out << "static int lib_index(lua_State*);\n" - "static const struct luaL_Reg lib_metatable [] = {\n" - " {\"__index\", lib_index},\n" + // If there are no static methods or properties, no need to create a library + if (state.staticMethods.size() == 0 && state.staticProperties.size() == 0) return; + + out << "static int lib_" << state.name << "_index(lua_State*);\n" + "static const struct luaL_Reg lib_" << state.name << "_metatable [] = {\n" + " {\"__index\", lib_" << state.name << "_index},\n" " {NULL, NULL} /* end of array */\n" "};\n\n"; @@ -355,7 +358,7 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) { "\n" " // Create the library's metatable\n" " luaL_newmetatable(L, \"__mt_lib_" << state.name << "\");\n" - " luaL_register(L, NULL, lib_metatable);\n" + " luaL_register(L, NULL, lib_" << state.name << "_metatable);\n" " lua_setmetatable(L, -2);\n" "\n" " lua_rawset(L, -3);\n" @@ -364,7 +367,7 @@ static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) { // Indexing methods and properties - out << "static int lib_index(lua_State* L) {\n" + out << "static int lib_" << state.name << "_index(lua_State* L) {\n" " std::string key(lua_tostring(L, 2));\n" " lua_pop(L, 2);\n" "\n"; @@ -418,14 +421,24 @@ void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAn out << "#include \"datatypes/variant.h\"\n"; out << "#include \n"; out << "#include \"lua.h\"\n\n"; - out << "const TypeDescriptor " << state.name << "::TYPE = {\n" - << " .name = \"" << state.serializedName << "\",\n" - << " .serializer = toVariantFunction(&" << state.name << "::Serialize)," - << " .deserializer = toVariantGenerator(&" << state.name << "::Deserialize),\n" - << " .toString = toVariantFunction(&" << state.name << "::ToString),"; - if (state.hasFromString) out << " .fromString = &" << state.name << "::FromString,\n"; + out << "const TypeDesc " << state.name << "::TYPE = {\n" + << " .name = \"" << state.serializedName << "\",\n"; + if (state.isSerializable) { + out << " .serialize = toVariantFunction(&" << state.name << "::Serialize),"; + if (state.hasGenericDeserializer) + out << " .deserialize = toVariantGenerator(&" << state.name << "::Deserialize),\n"; + else + out << " .deserialize = toVariantGeneratorNoMeta(&" << state.name << "::Deserialize),\n"; + } + out << " .toString = toVariantFunction(&" << state.name << "::ToString),"; + if (state.hasFromString) { + if (state.hasGenericFromString) + out << " .fromString = toVariantGenerator(&" << state.name << "::FromString),\n"; + else + out << " .fromString = toVariantGeneratorNoMeta(&" << state.name << "::FromString),\n"; + } out << " .pushLuaValue = toVariantFunction(&" << state.name << "::PushLuaValue)," - << " .fromLuaValue = &" << state.name << "::FromLuaValue,\n" + << " .fromLuaValue = toVariantGenerator(&" << state.name << "::FromLuaValue),\n" << "};\n\n"; writeLuaMethodImpls(out, state); diff --git a/core/src/datatypes/annotation.h b/core/src/datatypes/annotation.h index 617c954..bb90a43 100644 --- a/core/src/datatypes/annotation.h +++ b/core/src/datatypes/annotation.h @@ -26,7 +26,7 @@ #define AUTOGEN_PREAMBLE_DATA \ public: \ -static const TypeDescriptor TYPE; \ +static const TypeDesc TYPE; \ virtual void PushLuaValue(lua_State*) const; \ static result FromLuaValue(lua_State*, int idx); \ private: diff --git a/core/src/datatypes/base.h b/core/src/datatypes/base.h index e40a474..b61999a 100644 --- a/core/src/datatypes/base.h +++ b/core/src/datatypes/base.h @@ -11,17 +11,20 @@ extern "C" { typedef struct lua_State lua_State; } namespace pugi { class xml_node; }; class Variant; -typedef std::function Serializer; -typedef std::function Deserializer; +struct TypeMeta; + +typedef std::function Serialize; +typedef std::function(pugi::xml_node, const TypeMeta)> Deserialize; typedef std::function ToString; -typedef std::function(std::string)> FromString; +typedef std::function(std::string, const TypeMeta)> FromString; typedef std::function(lua_State*, int idx)> FromLuaValue; typedef std::function PushLuaValue; -struct TypeDescriptor { +// Describes a concrete type +struct TypeDesc { std::string name; - Serializer serializer; - Deserializer deserializer; + Serialize serialize; + Deserialize deserialize; ToString toString; FromString fromString; PushLuaValue pushLuaValue; @@ -29,11 +32,17 @@ struct TypeDescriptor { }; class Enum; +struct InstanceType; -struct TypeInfo { - const TypeDescriptor* descriptor; - Enum* enum_; +// Describes a meta-type, which consists of a concrete type, and some generic argument. +struct TypeMeta { + const TypeDesc* descriptor; + union { + Enum* enum_; // Applicable for EnumItem + InstanceType* instType; // Applicable for InstanceRef + }; - inline TypeInfo(const TypeDescriptor* descriptor) : descriptor(descriptor) {} - TypeInfo(Enum*); + inline TypeMeta(const TypeDesc* descriptor) : descriptor(descriptor) {} + TypeMeta(Enum*); + TypeMeta(InstanceType*); }; \ No newline at end of file diff --git a/core/src/datatypes/cframe.cpp b/core/src/datatypes/cframe.cpp index 917b1c8..74c6db9 100644 --- a/core/src/datatypes/cframe.cpp +++ b/core/src/datatypes/cframe.cpp @@ -1,5 +1,6 @@ #include "cframe.h" #include "datatypes/vector.h" +#include "error/data.h" #include "physics/util.h" #include #include @@ -134,7 +135,7 @@ void CFrame::Serialize(pugi::xml_node node) const { } -CFrame CFrame::Deserialize(pugi::xml_node node) { +result CFrame::Deserialize(pugi::xml_node node) { return CFrame( node.child("X").text().as_float(), node.child("Y").text().as_float(), diff --git a/core/src/datatypes/cframe.h b/core/src/datatypes/cframe.h index 7a1b996..f9684f0 100644 --- a/core/src/datatypes/cframe.h +++ b/core/src/datatypes/cframe.h @@ -3,6 +3,7 @@ #include "base.h" #include "datatypes/annotation.h" #include "datatypes/vector.h" +#include "error/data.h" #include #include #include @@ -40,7 +41,7 @@ public: virtual const std::string ToString() const; virtual void Serialize(pugi::xml_node parent) const; - static CFrame Deserialize(pugi::xml_node node); + static result Deserialize(pugi::xml_node node); static void PushLuaLibrary(lua_State*); diff --git a/core/src/datatypes/color3.cpp b/core/src/datatypes/color3.cpp index 24dc01d..b8e142b 100644 --- a/core/src/datatypes/color3.cpp +++ b/core/src/datatypes/color3.cpp @@ -1,5 +1,6 @@ #include "color3.h" #include "datatypes/variant.h" +#include "error/data.h" #include #include #include @@ -39,6 +40,6 @@ void Color3::Serialize(pugi::xml_node node) const { node.text().set(this->ToHex()); } -Color3 Color3::Deserialize(pugi::xml_node node) { +result Color3::Deserialize(pugi::xml_node node) { return Color3::FromHex(node.text().get()); } \ No newline at end of file diff --git a/core/src/datatypes/color3.h b/core/src/datatypes/color3.h index ba5e837..34128d4 100644 --- a/core/src/datatypes/color3.h +++ b/core/src/datatypes/color3.h @@ -2,6 +2,7 @@ #include "base.h" #include "datatypes/annotation.h" +#include "error/data.h" #include class DEF_DATA Color3 { @@ -21,7 +22,7 @@ public: virtual const std::string ToString() const; DEF_DATA_METHOD std::string ToHex() const; virtual void Serialize(pugi::xml_node node) const; - static Color3 Deserialize(pugi::xml_node node); + static result Deserialize(pugi::xml_node node); static void PushLuaLibrary(lua_State*); diff --git a/core/src/datatypes/enum.cpp b/core/src/datatypes/enum.cpp index 2ad5ce7..bf74c3f 100644 --- a/core/src/datatypes/enum.cpp +++ b/core/src/datatypes/enum.cpp @@ -1,8 +1,10 @@ #include "enum.h" #include "datatypes/base.h" #include "datatypes/variant.h" +#include "error/data.h" +#include -TypeInfo::TypeInfo(Enum* enum_) : enum_(enum_), descriptor(&EnumItem::TYPE) {} +TypeMeta::TypeMeta(Enum* enum_) : enum_(enum_), descriptor(&EnumItem::TYPE) {} Enum::Enum(_EnumData* data) : data(data) {} @@ -36,24 +38,29 @@ EnumItem::EnumItem(_EnumData* parentData, std::string name, int value) : parentD // -std::string Enum::ToString() { +std::string Enum::ToString() const { return "Enum." + this->data->name; } -void Enum::PushLuaValue(lua_State*) { +// +std::string EnumItem::ToString() const { + return "Enum." + parentData->name + "." + name; } -Variant Enum::FromLuaValue(lua_State*, int) { - +void EnumItem::Serialize(pugi::xml_node node) const { + node.set_name("token"); + node.text().set(value); } -const TypeDescriptor Enum::TYPE { - .name = "Enum", - .toString = toVariantFunction(&Enum::ToString), - // .fromString = Enum_FromString, - // .pushLuaValue = &Enum_PushLuaValue, - .fromLuaValue = toVariantGenerator(Enum::FromLuaValue), - // .serializer = Enum_Serialize, - // .deserializer = Enum_Deserialize, -}; \ No newline at end of file +result EnumItem::Deserialize(pugi::xml_node node, const TypeMeta info) { + auto result = info.enum_->FromValue(node.text().as_int()); + if (result.has_value()) return result.value(); + return DataParseError(node.text().as_string(), "EnumItem"); +} + +result EnumItem::FromString(std::string string, const TypeMeta info) { + auto result = info.enum_->FromName(string); + if (result.has_value()) return result.value(); + return DataParseError(string, "EnumItem"); +} \ No newline at end of file diff --git a/core/src/datatypes/enum.h b/core/src/datatypes/enum.h index b8c6fa0..1e976cc 100644 --- a/core/src/datatypes/enum.h +++ b/core/src/datatypes/enum.h @@ -4,6 +4,8 @@ #include #include #include +#include "datatypes/annotation.h" +#include "error/data.h" #include "lua.h" struct _EnumData { @@ -14,33 +16,40 @@ struct _EnumData { class EnumItem; -class Enum { +class DEF_DATA Enum { _EnumData* data; public: Enum(_EnumData*); - static const TypeDescriptor TYPE; + static const TypeDesc TYPE; inline _EnumData* InternalType() const { return this->data; }; std::vector GetEnumItems() const; std::optional FromName(std::string) const; std::optional FromValue(int) const; - std::string ToString(); - void PushLuaValue(lua_State*); - static Variant FromLuaValue(lua_State*, int); + std::string ToString() const; + void PushLuaValue(lua_State*) const; + static result FromLuaValue(lua_State*, int); }; -class EnumItem { +class DEF_DATA EnumItem { _EnumData* parentData; std::string name; int value; public: EnumItem(_EnumData*, std::string, int); - static const TypeDescriptor TYPE; + static const TypeDesc TYPE; inline std::string Name() const { return this->name; } inline int Value() const { return this->value; } inline Enum EnumType() const { return Enum(this->parentData); } + + static result FromString(std::string, const TypeMeta); + std::string ToString() const; + void Serialize(pugi::xml_node) const; + static result Deserialize(pugi::xml_node, const TypeMeta); + void PushLuaValue(lua_State*) const; + static result FromLuaValue(lua_State*, int); }; \ No newline at end of file diff --git a/core/src/datatypes/primitives.cpp b/core/src/datatypes/primitives.cpp index 27572e7..eef91d2 100644 --- a/core/src/datatypes/primitives.cpp +++ b/core/src/datatypes/primitives.cpp @@ -1,4 +1,5 @@ #include "primitives.h" +#include "error/data.h" #include "variant.h" #include #include "lua.h" @@ -10,7 +11,7 @@ void Null_Serialize(Variant self, pugi::xml_node node) { node.text().set("null"); } -Variant Null_Deserialize(pugi::xml_node node) { +result Null_Deserialize(pugi::xml_node node, const TypeMeta) { return std::monostate(); } @@ -18,7 +19,7 @@ const std::string Null_ToString(Variant self) { return "null"; } -const std::optional Null_FromString(std::string str) { +result Null_FromString(std::string string, const TypeMeta) { return std::monostate(); } @@ -30,7 +31,7 @@ result Null_FromLuaValue(lua_State* L, int idx) { return Variant(std::monostate()); } -const TypeDescriptor NULL_TYPE { +const TypeDesc NULL_TYPE { "null", Null_Serialize, Null_Deserialize, @@ -48,7 +49,7 @@ void Bool_Serialize(Variant self, pugi::xml_node node) { node.text().set(self.get() ? "true" : "false"); } -Variant Bool_Deserialize(pugi::xml_node node) { +result Bool_Deserialize(pugi::xml_node node, const TypeMeta) { return node.text().as_bool(); } @@ -56,7 +57,7 @@ const std::string Bool_ToString(Variant self) { return self.get() ? "true" : "false"; } -const std::optional Bool_FromString(std::string string) { +result Bool_FromString(std::string string, const TypeMeta) { return string[0] == 't' || string[0] == 'T' || string[0] == '1' || string[0] == 'y' || string[0] == 'Y'; } @@ -70,7 +71,7 @@ result Bool_FromLuaValue(lua_State* L, int idx) { return Variant(lua_toboolean(L, idx)); } -const TypeDescriptor BOOL_TYPE { +const TypeDesc BOOL_TYPE { "bool", Bool_Serialize, Bool_Deserialize, @@ -88,7 +89,7 @@ void Int_Serialize(Variant self, pugi::xml_node node) { node.text().set(self.get()); } -Variant Int_Deserialize(pugi::xml_node node) { +result Int_Deserialize(pugi::xml_node node, const TypeMeta) { return node.text().as_int(); } @@ -96,10 +97,10 @@ const std::string Int_ToString(Variant self) { return std::to_string(self.get()); } -const std::optional Int_FromString(std::string string) { +result Int_FromString(std::string string, const TypeMeta) { char* endPos; int value = (int)std::strtol(string.c_str(), &endPos, 10); - if (endPos == string.c_str()) return std::nullopt; + if (endPos == string.c_str()) return DataParseError(string, "int"); return value; } @@ -113,7 +114,7 @@ result Int_FromLuaValue(lua_State* L, int idx) { return Variant((int)lua_tonumber(L, idx)); } -const TypeDescriptor INT_TYPE { +const TypeDesc INT_TYPE { "int", Int_Serialize, Int_Deserialize, @@ -131,7 +132,7 @@ void Float_Serialize(Variant self, pugi::xml_node node) { node.text().set(self.get()); } -Variant Float_Deserialize(pugi::xml_node node) { +result Float_Deserialize(pugi::xml_node node, const TypeMeta) { return node.text().as_float(); } @@ -141,10 +142,10 @@ const std::string Float_ToString(Variant self) { return stream.str(); } -const std::optional Float_FromString(std::string string) { +result Float_FromString(std::string string, const TypeMeta) { char* endPos; float value = std::strtof(string.c_str(), &endPos); - if (endPos == string.c_str()) return std::nullopt; + if (endPos == string.c_str()) return DataParseError(string, "float"); return value; } @@ -158,7 +159,7 @@ result Float_FromLuaValue(lua_State* L, int idx) { return Variant((float)lua_tonumber(L, idx)); } -const TypeDescriptor FLOAT_TYPE { +const TypeDesc FLOAT_TYPE { "float", Float_Serialize, Float_Deserialize, @@ -176,7 +177,7 @@ void String_Serialize(Variant self, pugi::xml_node node) { node.text().set(self.get()); } -Variant String_Deserialize(pugi::xml_node node) { +result String_Deserialize(pugi::xml_node node, const TypeMeta) { return node.text().as_string(); } @@ -184,7 +185,7 @@ const std::string String_ToString(Variant self) { return self.get(); } -const std::optional String_FromString(std::string string) { +result String_FromString(std::string string, const TypeMeta) { return string; } @@ -198,7 +199,7 @@ result String_FromLuaValue(lua_State* L, int idx) { return Variant(lua_tostring(L, idx)); } -const TypeDescriptor STRING_TYPE { +const TypeDesc STRING_TYPE { "string", String_Serialize, String_Deserialize, diff --git a/core/src/datatypes/primitives.h b/core/src/datatypes/primitives.h index 5ef8406..71eb752 100644 --- a/core/src/datatypes/primitives.h +++ b/core/src/datatypes/primitives.h @@ -2,8 +2,8 @@ #include "base.h" -extern const TypeDescriptor NULL_TYPE; -extern const TypeDescriptor BOOL_TYPE; -extern const TypeDescriptor INT_TYPE; -extern const TypeDescriptor FLOAT_TYPE; -extern const TypeDescriptor STRING_TYPE; \ No newline at end of file +extern const TypeDesc NULL_TYPE; +extern const TypeDesc BOOL_TYPE; +extern const TypeDesc INT_TYPE; +extern const TypeDesc FLOAT_TYPE; +extern const TypeDesc STRING_TYPE; \ No newline at end of file diff --git a/core/src/datatypes/ref.cpp b/core/src/datatypes/ref.cpp index ddfe121..8173e03 100644 --- a/core/src/datatypes/ref.cpp +++ b/core/src/datatypes/ref.cpp @@ -10,14 +10,16 @@ #include "objects/base/member.h" #include +TypeMeta::TypeMeta(InstanceType* instType) : instType(instType), descriptor(&InstanceRef::TYPE) {} + InstanceRef::InstanceRef() {}; InstanceRef::InstanceRef(std::weak_ptr instance) : ref(instance) {}; InstanceRef::~InstanceRef() = default; -const TypeDescriptor InstanceRef::TYPE = { +const TypeDesc InstanceRef::TYPE = { .name = "Ref", - .serializer = toVariantFunction(&InstanceRef::Serialize), - .deserializer = &InstanceRef::Deserialize, + .serialize = toVariantFunction(&InstanceRef::Serialize), + .deserialize = toVariantGeneratorNoMeta(&InstanceRef::Deserialize), .toString = toVariantFunction(&InstanceRef::ToString), .fromString = nullptr, .pushLuaValue = toVariantFunction(&InstanceRef::PushLuaValue), @@ -39,7 +41,7 @@ void InstanceRef::Serialize(pugi::xml_node node) const { panic(); } -InstanceRef InstanceRef::Deserialize(pugi::xml_node node) { +result InstanceRef::Deserialize(pugi::xml_node node) { // Handled by Instance panic(); } diff --git a/core/src/datatypes/ref.h b/core/src/datatypes/ref.h index 15f5c19..6de2865 100644 --- a/core/src/datatypes/ref.h +++ b/core/src/datatypes/ref.h @@ -13,13 +13,13 @@ public: InstanceRef(std::weak_ptr); ~InstanceRef(); - static const TypeDescriptor TYPE; + static const TypeDesc TYPE; operator std::weak_ptr(); virtual const std::string ToString() const; virtual void Serialize(pugi::xml_node node) const; virtual void PushLuaValue(lua_State*) const; - static InstanceRef Deserialize(pugi::xml_node node); + static result Deserialize(pugi::xml_node node); static result FromLuaValue(lua_State*, int idx); }; \ No newline at end of file diff --git a/core/src/datatypes/signal.cpp b/core/src/datatypes/signal.cpp index a349630..c83654b 100644 --- a/core/src/datatypes/signal.cpp +++ b/core/src/datatypes/signal.cpp @@ -225,7 +225,7 @@ static const struct luaL_Reg signal_metatable [] = { SignalRef::SignalRef(std::weak_ptr ref) : signal(ref) {} SignalRef::~SignalRef() = default; -const TypeDescriptor SignalRef::TYPE = { +const TypeDesc SignalRef::TYPE = { .name = "Signal", .toString = toVariantFunction(&SignalRef::ToString), .pushLuaValue = toVariantFunction(&SignalRef::PushLuaValue), @@ -346,7 +346,7 @@ static const struct luaL_Reg signalconnection_metatable [] = { SignalConnectionRef::SignalConnectionRef(std::weak_ptr ref) : signalConnection(ref) {} SignalConnectionRef::~SignalConnectionRef() = default; -const TypeDescriptor SignalConnectionRef::TYPE = { +const TypeDesc SignalConnectionRef::TYPE = { .name = "Signal", .toString = toVariantFunction(&SignalConnectionRef::ToString), .pushLuaValue = toVariantFunction(&SignalConnectionRef::PushLuaValue), diff --git a/core/src/datatypes/signal.h b/core/src/datatypes/signal.h index dcc80b8..594f3d5 100644 --- a/core/src/datatypes/signal.h +++ b/core/src/datatypes/signal.h @@ -112,7 +112,7 @@ public: SignalRef(std::weak_ptr); ~SignalRef(); - static const TypeDescriptor TYPE; + static const TypeDesc TYPE; operator std::weak_ptr(); @@ -129,7 +129,7 @@ public: SignalConnectionRef(std::weak_ptr); ~SignalConnectionRef(); - static const TypeDescriptor TYPE; + static const TypeDesc TYPE; operator std::weak_ptr(); diff --git a/core/src/datatypes/variant.cpp b/core/src/datatypes/variant.cpp index c9915bc..257fd71 100644 --- a/core/src/datatypes/variant.cpp +++ b/core/src/datatypes/variant.cpp @@ -6,6 +6,7 @@ #include "datatypes/ref.h" #include "datatypes/signal.h" #include "datatypes/vector.h" +#include "error/data.h" #include "logger.h" #include "panic.h" #include @@ -20,7 +21,7 @@ #endif } -const TypeDescriptor* VARIANT_TYPES[] { +const TypeDesc* VARIANT_TYPES[] { &NULL_TYPE, &BOOL_TYPE, &INT_TYPE, @@ -32,15 +33,15 @@ const TypeDescriptor* VARIANT_TYPES[] { &InstanceRef::TYPE, &SignalRef::TYPE, &SignalConnectionRef::TYPE, - // &Enum::TYPE, - // &EnumItem::TYPE, + &Enum::TYPE, + &EnumItem::TYPE, }; -const TypeInfo Variant::GetTypeInfo() const { +const TypeMeta Variant::GetTypeMeta() const { return VARIANT_TYPES[wrapped.index()]; } -const TypeDescriptor* Variant::GetType() const { +const TypeDesc* Variant::GetType() const { return VARIANT_TYPES[wrapped.index()]; } @@ -55,7 +56,7 @@ void Variant::Serialize(pugi::xml_node node) const { if (!VARIANT_TYPES[wrapped.index()]->pushLuaValue) { Logger::fatalErrorf("Data type %s does not implement serializer", VARIANT_TYPES[wrapped.index()]->name.c_str()); } - VARIANT_TYPES[wrapped.index()]->serializer(*this, node); + VARIANT_TYPES[wrapped.index()]->serialize(*this, node); } void Variant::PushLuaValue(lua_State* state) const { @@ -65,14 +66,15 @@ void Variant::PushLuaValue(lua_State* state) const { VARIANT_TYPES[wrapped.index()]->pushLuaValue(*this, state); } -Variant Variant::Deserialize(pugi::xml_node node, const TypeInfo type) { - if (!type.descriptor->deserializer) { +result Variant::Deserialize(pugi::xml_node node, const TypeMeta type) { + if (!type.descriptor->deserialize) { Logger::fatalErrorf("Data type %s does not implement deserialize", type.descriptor->name.c_str()); + return DataParseError(node.text().as_string(), type.descriptor->name); } - return type.descriptor->deserializer(node); + return type.descriptor->deserialize(node, type); } -std::map TYPE_MAP = { +std::map TYPE_MAP = { { "null", &NULL_TYPE }, { "bool", &BOOL_TYPE }, { "int", &INT_TYPE }, @@ -82,4 +84,5 @@ std::map TYPE_MAP = { { "CoordinateFrame", &CFrame::TYPE }, { "Color3", &Color3::TYPE }, { "Ref", &InstanceRef::TYPE }, + { "token", &EnumItem::TYPE }, }; \ No newline at end of file diff --git a/core/src/datatypes/variant.h b/core/src/datatypes/variant.h index d5fbba0..56c45ea 100644 --- a/core/src/datatypes/variant.h +++ b/core/src/datatypes/variant.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include "base.h" @@ -7,6 +8,7 @@ #include "datatypes/enum.h" #include "datatypes/ref.h" #include "datatypes/signal.h" +#include "error/data.h" #include "vector.h" #include "cframe.h" @@ -37,16 +39,16 @@ typedef std::variant< class Variant { __VARIANT_TYPE wrapped; public: - template Variant(T obj) : wrapped(obj) {} - template T get() { return std::get(wrapped); } + template , int> = 0> Variant(T obj) : wrapped(obj) {} + template , int> = 0> T get() { return std::get(wrapped); } std::string ToString() const; - const TypeInfo GetTypeInfo() const; - const TypeDescriptor* GetType() const; + const TypeMeta GetTypeMeta() const; + const TypeDesc* GetType() const; void Serialize(pugi::xml_node node) const; void PushLuaValue(lua_State* state) const; - static Variant Deserialize(pugi::xml_node node, const TypeInfo); + static result Deserialize(pugi::xml_node node, const TypeMeta); }; template @@ -70,5 +72,23 @@ std::function toVariantGenerator(T(f)(Args...)) { }; } +template +std::function(Args...)> toVariantGenerator(result(f)(Args...)) { + return [f](Args... args) -> result { + auto result = f(args...); + if (result.isSuccess()) return (Variant)(result.success().value()); + return result.error().value(); + }; +} + +template +std::function(Args..., const TypeMeta)> toVariantGeneratorNoMeta(result(f)(Args...)) { + return [f](Args... args, const TypeMeta) -> result { + auto result = f(args...); + if (result.isSuccess()) return (Variant)(result.success().value()); + return result.error().value(); + }; +} + // Map of all data types to their type names -extern std::map TYPE_MAP; \ No newline at end of file +extern std::map TYPE_MAP; \ No newline at end of file diff --git a/core/src/datatypes/vector.cpp b/core/src/datatypes/vector.cpp index 39c58fe..d9fd608 100644 --- a/core/src/datatypes/vector.cpp +++ b/core/src/datatypes/vector.cpp @@ -8,6 +8,7 @@ #include #include "datatypes/base.h" #include "datatypes/variant.h" +#include "error/data.h" #include namespace rp = reactphysics3d; @@ -87,15 +88,15 @@ void Vector3::Serialize(pugi::xml_node node) const { node.append_child("Z").text().set(std::to_string(this->Z())); } -Vector3 Vector3::Deserialize(pugi::xml_node node) { +result Vector3::Deserialize(pugi::xml_node node) { return Vector3(node.child("X").text().as_float(), node.child("Y").text().as_float(), node.child("Z").text().as_float()); } -std::optional Vector3::FromString(std::string string) { +result Vector3::FromString(std::string string) { float components[3]; for (int i = 0; i < 3; i++) { - if (string.length() == 0) return std::nullopt; + if (string.length() == 0) return DataParseError(string, "Vector3"); while (string[0] == ' ' && string.length() > 0) string.erase(0, 1); size_t nextPos = string.find(","); if (nextPos == -1) nextPos = string.length(); @@ -104,7 +105,7 @@ std::optional Vector3::FromString(std::string string) { char* cpos; float value = std::strtof(term.c_str(), &cpos); - if (cpos == term.c_str()) return std::nullopt; + if (cpos == term.c_str()) return DataParseError(string, "Vector3"); components[i] = value; } diff --git a/core/src/datatypes/vector.h b/core/src/datatypes/vector.h index 3f2a3d3..846ed90 100644 --- a/core/src/datatypes/vector.h +++ b/core/src/datatypes/vector.h @@ -2,6 +2,7 @@ #include "base.h" #include "datatypes/annotation.h" +#include "error/data.h" #include #include @@ -24,8 +25,8 @@ public: virtual const std::string ToString() const; virtual void Serialize(pugi::xml_node node) const; - static Vector3 Deserialize(pugi::xml_node node); - static std::optional FromString(std::string); + static result Deserialize(pugi::xml_node node); + static result FromString(std::string); static void PushLuaLibrary(lua_State*); diff --git a/core/src/error/data.h b/core/src/error/data.h index c2a58e1..f390be8 100644 --- a/core/src/error/data.h +++ b/core/src/error/data.h @@ -4,5 +4,10 @@ class LuaCastError : public Error { public: - inline LuaCastError(std::string sourceType, std::string targetType) : Error("InstanceCastError", "Attempt to cast " + sourceType + " to " + targetType) {} + inline LuaCastError(std::string sourceType, std::string targetType) : Error("LuaCastError", "Attempt to cast " + sourceType + " to " + targetType) {} +}; + +class DataParseError : public Error { + public: + inline DataParseError(std::string parsedString, std::string targetType) : Error("DataParseError", "Failed to parse '" + parsedString + "' into value of type " + targetType) {} }; \ No newline at end of file diff --git a/core/src/error/result.h b/core/src/error/result.h index 034dbbd..47ca47a 100644 --- a/core/src/error/result.h +++ b/core/src/error/result.h @@ -24,6 +24,8 @@ class [[nodiscard]] result { std::variant value; public: + // Helper for std::variant, etc. + template , int> = 0> result(T_Args... args) : value(success_state { T_Result(args...) }) {} result(T_Result success) : value(success_state { success }) {} result(std::variant error) : value(error_state { error }) {} template ...>, int> = 0> @@ -61,7 +63,7 @@ public: // Equivalent to .success operator std::optional() { return success(); } - operator bool() { return isSuccess(); } + explicit operator bool() const { return isSuccess(); } // Explicity is necessary to prevent auto-casting from result to Variant, for instance bool operator !() { return isError(); } }; diff --git a/core/src/objects/base/instance.cpp b/core/src/objects/base/instance.cpp index b5aabfd..d28801d 100644 --- a/core/src/objects/base/instance.cpp +++ b/core/src/objects/base/instance.cpp @@ -354,7 +354,12 @@ result, NoSuchInstance> Instance::Deserialize(pugi::xm object->SetPropertyValue(propertyName, InstanceRef()).expect(); } } else { - Variant value = Variant::Deserialize(propertyNode, meta.type); + auto valueResult = Variant::Deserialize(propertyNode, meta.type); + if (valueResult.isError()) { + valueResult.logError(); + continue; + } + auto value = valueResult.expect(); object->SetPropertyValue(propertyName, value).expect("Declared property was missing"); } } diff --git a/core/src/objects/base/member.h b/core/src/objects/base/member.h index e6d8f7a..dcc523a 100644 --- a/core/src/objects/base/member.h +++ b/core/src/objects/base/member.h @@ -24,7 +24,7 @@ enum PropertyCategory { const int PROPERTY_CATEGORY_MAX = PROP_CATEGORY_SURFACE_INPUT; struct PropertyMeta { - const TypeInfo type; + const TypeMeta type; PropertyFlags flags; PropertyCategory category = PROP_CATEGORY_DATA; }; diff --git a/editor/main.cpp b/editor/main.cpp index 08d2743..054cd06 100644 --- a/editor/main.cpp +++ b/editor/main.cpp @@ -1,3 +1,4 @@ +#include "error/data.h" #include "mainwindow.h" #include "logger.h" diff --git a/editor/panes/propertiesview.cpp b/editor/panes/propertiesview.cpp index 2ac45cb..e8def62 100644 --- a/editor/panes/propertiesview.cpp +++ b/editor/panes/propertiesview.cpp @@ -3,6 +3,7 @@ #include "datatypes/base.h" #include "datatypes/variant.h" #include "datatypes/primitives.h" +#include "error/data.h" #include "objects/base/member.h" #include @@ -213,11 +214,12 @@ public: } else if (meta.type.descriptor->fromString) { QLineEdit* lineEdit = dynamic_cast(editor); - std::optional parsedResult = meta.type.descriptor->fromString(lineEdit->text().toStdString()); + result parsedResult = meta.type.descriptor->fromString(lineEdit->text().toStdString(), meta.type); if (!parsedResult) return; - inst->SetPropertyValue(propertyName, parsedResult.value()).expect(); - model->setData(index, QString::fromStdString(parsedResult.value().ToString())); - view->rebuildCompositeProperty(view->itemFromIndex(index), meta.type.descriptor, parsedResult.value()); + Variant parsedValue = parsedResult.expect(); + inst->SetPropertyValue(propertyName, parsedValue).expect(); + model->setData(index, QString::fromStdString(parsedValue.ToString())); + view->rebuildCompositeProperty(view->itemFromIndex(index), meta.type.descriptor, parsedValue); } } }; @@ -358,7 +360,7 @@ void PropertiesView::propertyChanged(QTreeWidgetItem *item, int column) { } } -void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const TypeDescriptor* type, Variant value) { +void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const TypeDesc* type, Variant value) { if (type == &Vector3::TYPE) { // https://forum.qt.io/post/266837 foreach(auto i, item->takeChildren()) delete i; diff --git a/editor/panes/propertiesview.h b/editor/panes/propertiesview.h index 60b9450..10ff10f 100644 --- a/editor/panes/propertiesview.h +++ b/editor/panes/propertiesview.h @@ -15,7 +15,7 @@ class PropertiesView : public QTreeWidget { std::weak_ptr currentInstance; void propertyChanged(QTreeWidgetItem *item, int column); void activateProperty(QTreeWidgetItem *item, int column); - void rebuildCompositeProperty(QTreeWidgetItem *item, const TypeDescriptor*, Variant); + void rebuildCompositeProperty(QTreeWidgetItem *item, const TypeDesc*, Variant); void onPropertyUpdated(std::shared_ptr instance, std::string property, Variant newValue); friend PropertiesItemDelegate;