feat: properties and datatypes galore

This commit is contained in:
maelstrom 2025-01-29 22:41:24 +01:00
parent 4b29e99a2e
commit 49066db8fb
12 changed files with 190 additions and 51 deletions

View file

@ -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)

View file

@ -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());
}

41
src/datatypes/base.cpp Normal file
View file

@ -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;
}

48
src/datatypes/base.h Normal file
View file

@ -0,0 +1,48 @@
#pragma once
#include <string>
#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

8
src/datatypes/meta.cpp Normal file
View file

@ -0,0 +1,8 @@
#include "meta.h"
#include <variant>
Data::String Data::Variant::ToString() const {
return std::visit([](auto&& it) {
return it.ToString();
}, this->wrapped);
}

30
src/datatypes/meta.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include "base.h"
#include <variant>
// #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 <typename T> Variant(T obj) : wrapped(obj) {}
template <typename T> T get() { return std::get<T>(wrapped); }
Data::String ToString() const;
};
}

View file

@ -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)

View file

@ -1,25 +0,0 @@
#pragma once
#include <string>
#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

View file

@ -1,5 +1,8 @@
#include "instance.h"
#include "../../common.h"
#include "../../datatypes/meta.h"
#include "datatypes/base.h"
#include "objects/base/member.h"
#include <algorithm>
#include <cstddef>
#include <cstdio>
@ -24,12 +27,12 @@ InstanceType* Instance::TYPE = &TYPE_;
Instance::Instance(InstanceType* type) {
this->name = type->className;
this->memberMap = {
this->memberMap = std::make_unique<MemberMap>( MemberMap {
.super = std::nullopt,
.members = {
{ "Name", { .backingField = &name } }
{ "Name", { .backingField = &name, .codec = fieldCodecOf<Data::String, std::string>() } }
}
};
});
}
Instance::~Instance () {
@ -68,24 +71,24 @@ void Instance::OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParen
// Properties
tl::expected<std::string, MemberNotFound> Instance::GetPropertyValue(std::string name) {
tl::expected<Data::Variant, MemberNotFound> 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<void, MemberNotFound> Instance::SetPropertyValue(std::string name, std::string value) {
tl::expected<void, MemberNotFound> 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<PropertyMeta, MemberNotFound> 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<std::string> Instance::GetProperties() {
std::vector<std::string> 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;

View file

@ -36,7 +36,7 @@ private:
std::optional<std::vector<std::string>> cachedMemberList;
protected:
MemberMap memberMap;
std::unique_ptr<MemberMap> memberMap;
Instance(InstanceType*);
virtual ~Instance();
@ -57,8 +57,8 @@ public:
// Properties
// Do I like using expected?
tl::expected<std::string, MemberNotFound> GetPropertyValue(std::string name);
tl::expected<void, MemberNotFound> SetPropertyValue(std::string name, std::string value);
tl::expected<Data::Variant, MemberNotFound> GetPropertyValue(std::string name);
tl::expected<void, MemberNotFound> SetPropertyValue(std::string name, Data::Variant value);
tl::expected<PropertyMeta, MemberNotFound> GetPropertyMeta(std::string name);
// Returning a list of property names feels kinda janky. Is this really the way to go?
std::vector<std::string> GetProperties();

View file

@ -1,5 +1,7 @@
#pragma once
#include "../../datatypes/base.h"
#include "datatypes/meta.h"
#include <map>
#include <memory>
#include <optional>
@ -7,8 +9,33 @@
class Instance;
struct FieldCodec {
void (*write)(Data::Variant source, void* destination);
Data::Variant (*read)(void* source);
};
template <typename T, typename U>
void _writeCodec(Data::Variant source, void* destination) {
*(U*)destination = source.get<T>().value;
}
template <typename T, typename U>
Data::Variant _readCodec(void* source) {
return T(*(U*)source);
}
template <typename T, typename U>
constexpr FieldCodec fieldCodecOf() {
return FieldCodec {
.write = &_writeCodec<T, U>,
.read = &_readCodec<T, U>,
};
}
struct PropertyMeta {
void* backingField;
Data::TypeInfo type;
FieldCodec codec;
};
typedef std::variant<PropertyMeta> MemberMeta;

View file

@ -1,5 +1,9 @@
#include "part.h"
#include "base/instance.h"
#include "datatypes/base.h"
#include "objects/base/member.h"
#include <memory>
#include <optional>
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>(MemberMap {
.super = std::move(this->memberMap),
.members = {
{ "Anchored", { .backingField = &anchored, .codec = fieldCodecOf<Data::Bool, bool>() } }
}
});
}
// This feels wrong. Get access to PhysicsWorld somehow else? Part will need access to this often though, most likely...