From 49066db8fbf62922fca599518fd5b21656ba9fac Mon Sep 17 00:00:00 2001 From: maelstrom Date: Wed, 29 Jan 2025 22:41:24 +0100 Subject: [PATCH] feat: properties and datatypes galore --- CMakeLists.txt | 4 +++ editor/panes/propertiesmodel.cpp | 2 +- src/datatypes/base.cpp | 41 +++++++++++++++++++++++++++ src/datatypes/base.h | 48 ++++++++++++++++++++++++++++++++ src/datatypes/meta.cpp | 8 ++++++ src/datatypes/meta.h | 30 ++++++++++++++++++++ src/datatypes/primitives.cpp | 11 -------- src/datatypes/primitives.h | 25 ----------------- src/objects/base/instance.cpp | 26 ++++++++++------- src/objects/base/instance.h | 6 ++-- src/objects/base/member.h | 27 ++++++++++++++++++ src/objects/part.cpp | 13 ++++++++- 12 files changed, 190 insertions(+), 51 deletions(-) create mode 100644 src/datatypes/base.cpp create mode 100644 src/datatypes/base.h create mode 100644 src/datatypes/meta.cpp create mode 100644 src/datatypes/meta.h delete mode 100644 src/datatypes/primitives.cpp delete mode 100644 src/datatypes/primitives.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 496a745..9fef269 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,10 @@ set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib ) +SET(BASEPATH "${CMAKE_SOURCE_DIR}") +include_directories("${BASEPATH}") +include_directories("src") + find_package(OpenGL REQUIRED COMPONENTS OpenGL) find_package(SDL2 REQUIRED) diff --git a/editor/panes/propertiesmodel.cpp b/editor/panes/propertiesmodel.cpp index e038994..b26125c 100644 --- a/editor/panes/propertiesmodel.cpp +++ b/editor/panes/propertiesmodel.cpp @@ -22,7 +22,7 @@ QVariant PropertiesModel::data(const QModelIndex &index, int role) const { if (index.column() == 0) return QString::fromStdString(propertyName); else if (index.column() == 1) - return QString::fromStdString(selectedItem->GetPropertyValue(propertyName).value()); + return QString::fromStdString(selectedItem->GetPropertyValue(propertyName).value().ToString()); // case Qt::DecorationRole: // return iconOf(item->GetClass()); } diff --git a/src/datatypes/base.cpp b/src/datatypes/base.cpp new file mode 100644 index 0000000..e28e759 --- /dev/null +++ b/src/datatypes/base.cpp @@ -0,0 +1,41 @@ +#include "base.h" + +#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE, TYPE_NAME) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : value(in) {} \ +Data::CLASS_NAME::~CLASS_NAME() = default; \ +Data::CLASS_NAME::operator WRAPPED_TYPE() { return value; } \ +const Data::TypeInfo Data::CLASS_NAME::TYPE = { .name = TYPE_NAME, }; \ +const Data::TypeInfo& Data::CLASS_NAME::GetType() const { return Data::CLASS_NAME::TYPE; }; + +Data::Base::~Base() {}; + +Data::Null::Null() {}; +Data::Null::~Null() = default; +const Data::TypeInfo Data::Null::TYPE = { + .name = "null", +}; +const Data::TypeInfo& Data::Null::GetType() const { return Data::Null::TYPE; }; + +IMPL_WRAPPER_CLASS(Bool, bool, "bool") +IMPL_WRAPPER_CLASS(Int, int, "int") +IMPL_WRAPPER_CLASS(Float, float, "float") +IMPL_WRAPPER_CLASS(String, std::string, "string") + +const Data::String Data::Null::ToString() const { + return Data::String("null"); +} + +const Data::String Data::Bool::ToString() const { + return Data::String(value ? "true" : "false"); +} + +const Data::String Data::Int::ToString() const { + return Data::String(std::to_string(value)); +} + +const Data::String Data::Float::ToString() const { + return Data::String(std::to_string(value)); +} + +const Data::String Data::String::ToString() const { + return *this; +} \ No newline at end of file diff --git a/src/datatypes/base.h b/src/datatypes/base.h new file mode 100644 index 0000000..84db758 --- /dev/null +++ b/src/datatypes/base.h @@ -0,0 +1,48 @@ +#pragma once + +#include + + +#define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \ +public: \ + const WRAPPED_TYPE value; \ + CLASS_NAME(WRAPPED_TYPE); \ + ~CLASS_NAME(); \ + operator WRAPPED_TYPE(); \ + virtual const TypeInfo& GetType() const override; \ + static const TypeInfo TYPE; \ + \ + virtual const Data::String ToString() const override; \ +}; + +namespace Data { + struct TypeInfo { + std::string name; + }; + + class String; + class Base { + public: + virtual ~Base(); + virtual const TypeInfo& GetType() const = 0; + virtual const Data::String ToString() const = 0; + }; + + class Null : Base { + public: + Null(); + ~Null(); + virtual const TypeInfo& GetType() const override; + static const TypeInfo TYPE; + + virtual const Data::String ToString() const override; + }; + + DEF_WRAPPER_CLASS(Bool, bool) + DEF_WRAPPER_CLASS(Int, int) + DEF_WRAPPER_CLASS(Float, float) + DEF_WRAPPER_CLASS(String, std::string) +}; + + +#undef DEF_WRAPPER_CLASS \ No newline at end of file diff --git a/src/datatypes/meta.cpp b/src/datatypes/meta.cpp new file mode 100644 index 0000000..06b1223 --- /dev/null +++ b/src/datatypes/meta.cpp @@ -0,0 +1,8 @@ +#include "meta.h" +#include + +Data::String Data::Variant::ToString() const { + return std::visit([](auto&& it) { + return it.ToString(); + }, this->wrapped); +} \ No newline at end of file diff --git a/src/datatypes/meta.h b/src/datatypes/meta.h new file mode 100644 index 0000000..a28f842 --- /dev/null +++ b/src/datatypes/meta.h @@ -0,0 +1,30 @@ +#pragma once + +#include "base.h" +#include + +// #define __VARIANT_TYPE std::variant< \ +// Null, \ +// Bool, \ +// Int, \ +// Float, \ +// String \ +// > + +namespace Data { + typedef std::variant< + Null, + Bool, + Int, + Float, + String + > __VARIANT_TYPE; + + class Variant { + __VARIANT_TYPE wrapped; + public: + template Variant(T obj) : wrapped(obj) {} + template T get() { return std::get(wrapped); } + Data::String ToString() const; + }; +} \ No newline at end of file diff --git a/src/datatypes/primitives.cpp b/src/datatypes/primitives.cpp deleted file mode 100644 index 3176327..0000000 --- a/src/datatypes/primitives.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "primitives.h" - -#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : wrapped(in) {}\ -Data::CLASS_NAME::operator WRAPPED_TYPE() { return wrapped; } - -Data::Void::Void() {}; - -IMPL_WRAPPER_CLASS(Bool, bool) -IMPL_WRAPPER_CLASS(Int, int) -IMPL_WRAPPER_CLASS(Float, float) -IMPL_WRAPPER_CLASS(String, std::string) \ No newline at end of file diff --git a/src/datatypes/primitives.h b/src/datatypes/primitives.h deleted file mode 100644 index 698668b..0000000 --- a/src/datatypes/primitives.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -#define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME {\ - WRAPPED_TYPE wrapped;\ -public:\ - CLASS_NAME(WRAPPED_TYPE); \ - operator WRAPPED_TYPE(); \ -}; - -namespace Data { - class Void { - public: - Void(); - }; - - DEF_WRAPPER_CLASS(Bool, bool) - DEF_WRAPPER_CLASS(Int, int) - DEF_WRAPPER_CLASS(Float, float) - DEF_WRAPPER_CLASS(String, std::string) -}; - - -#undef DEF_WRAPPER_CLASS \ No newline at end of file diff --git a/src/objects/base/instance.cpp b/src/objects/base/instance.cpp index 6204191..72c3f1a 100644 --- a/src/objects/base/instance.cpp +++ b/src/objects/base/instance.cpp @@ -1,5 +1,8 @@ #include "instance.h" #include "../../common.h" +#include "../../datatypes/meta.h" +#include "datatypes/base.h" +#include "objects/base/member.h" #include #include #include @@ -24,12 +27,12 @@ InstanceType* Instance::TYPE = &TYPE_; Instance::Instance(InstanceType* type) { this->name = type->className; - this->memberMap = { + this->memberMap = std::make_unique( MemberMap { .super = std::nullopt, .members = { - { "Name", { .backingField = &name } } + { "Name", { .backingField = &name, .codec = fieldCodecOf() } } } - }; + }); } Instance::~Instance () { @@ -68,24 +71,24 @@ void Instance::OnParentUpdated(std::optional> oldParen // Properties -tl::expected Instance::GetPropertyValue(std::string name) { +tl::expected Instance::GetPropertyValue(std::string name) { auto meta = GetPropertyMeta(name); if (!meta) return tl::make_unexpected(MemberNotFound()); - return *(std::string*)meta->backingField; + return meta->codec.read(meta->backingField); } -tl::expected Instance::SetPropertyValue(std::string name, std::string value) { +tl::expected Instance::SetPropertyValue(std::string name, Data::Variant value) { auto meta = GetPropertyMeta(name); if (!meta) return tl::make_unexpected(MemberNotFound()); - *(std::string*)meta->backingField = value; + meta->codec.write(value, meta->backingField); return {}; } tl::expected Instance::GetPropertyMeta(std::string name) { - MemberMap* current = &memberMap; + MemberMap* current = &*memberMap; while (true) { // We look for the property in current member map auto it = current->members.find(name); @@ -108,14 +111,17 @@ std::vector Instance::GetProperties() { std::vector memberList; - MemberMap* current = &memberMap; + MemberMap* current = &*memberMap; do { for (auto const& [key, _] : current->members) { // Don't add it if it's already in the list if (std::find(memberList.begin(), memberList.end(), key) == memberList.end()) memberList.push_back(key); } - } while (current->super.has_value()); + if (!current->super.has_value()) + break; + current = &*current->super.value(); + } while (true); cachedMemberList = memberList; return memberList; diff --git a/src/objects/base/instance.h b/src/objects/base/instance.h index 2f85efe..d39b146 100644 --- a/src/objects/base/instance.h +++ b/src/objects/base/instance.h @@ -36,7 +36,7 @@ private: std::optional> cachedMemberList; protected: - MemberMap memberMap; + std::unique_ptr memberMap; Instance(InstanceType*); virtual ~Instance(); @@ -57,8 +57,8 @@ public: // Properties // Do I like using expected? - tl::expected GetPropertyValue(std::string name); - tl::expected SetPropertyValue(std::string name, std::string value); + tl::expected GetPropertyValue(std::string name); + tl::expected SetPropertyValue(std::string name, Data::Variant value); tl::expected GetPropertyMeta(std::string name); // Returning a list of property names feels kinda janky. Is this really the way to go? std::vector GetProperties(); diff --git a/src/objects/base/member.h b/src/objects/base/member.h index 615c72b..3859afe 100644 --- a/src/objects/base/member.h +++ b/src/objects/base/member.h @@ -1,5 +1,7 @@ #pragma once +#include "../../datatypes/base.h" +#include "datatypes/meta.h" #include #include #include @@ -7,8 +9,33 @@ class Instance; +struct FieldCodec { + void (*write)(Data::Variant source, void* destination); + Data::Variant (*read)(void* source); +}; + +template +void _writeCodec(Data::Variant source, void* destination) { + *(U*)destination = source.get().value; +} + +template +Data::Variant _readCodec(void* source) { + return T(*(U*)source); +} + +template +constexpr FieldCodec fieldCodecOf() { + return FieldCodec { + .write = &_writeCodec, + .read = &_readCodec, + }; +} + struct PropertyMeta { void* backingField; + Data::TypeInfo type; + FieldCodec codec; }; typedef std::variant MemberMeta; diff --git a/src/objects/part.cpp b/src/objects/part.cpp index b365f42..b8c7570 100644 --- a/src/objects/part.cpp +++ b/src/objects/part.cpp @@ -1,5 +1,9 @@ #include "part.h" #include "base/instance.h" +#include "datatypes/base.h" +#include "objects/base/member.h" +#include +#include static InstanceType TYPE_ { .super = Instance::TYPE, @@ -14,11 +18,18 @@ InstanceType* Part::GetClass() { return &TYPE_; } -Part::Part(): Instance(&TYPE_) { +Part::Part(): Part(PartConstructParams {}) { } Part::Part(PartConstructParams params): Instance(&TYPE_), position(params.position), rotation(params.rotation), scale(params.scale), material(params.material), anchored(params.anchored) { + + this->memberMap = std::make_unique(MemberMap { + .super = std::move(this->memberMap), + .members = { + { "Anchored", { .backingField = &anchored, .codec = fieldCodecOf() } } + } + }); } // This feels wrong. Get access to PhysicsWorld somehow else? Part will need access to this often though, most likely...