feat: properties and datatypes galore
This commit is contained in:
parent
4b29e99a2e
commit
49066db8fb
|
@ -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)
|
||||
|
|
|
@ -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
41
src/datatypes/base.cpp
Normal 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
48
src/datatypes/base.h
Normal 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
8
src/datatypes/meta.cpp
Normal 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
30
src/datatypes/meta.h
Normal 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;
|
||||
};
|
||||
}
|
|
@ -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)
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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...
|
||||
|
|
Loading…
Reference in a new issue