feat: runtime property enumeration

This commit is contained in:
maelstrom 2025-01-28 20:14:12 +01:00
parent 9f42b9815f
commit ce1e4d4f39
8 changed files with 2604 additions and 76 deletions

2476
include/expected.hpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
#include "primitives.h"
#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : wrapped(in) {}\
CLASS_NAME::operator WRAPPED_TYPE() { return wrapped; }
#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; }
VoidData::VoidData() {};
Data::Void::Void() {};
IMPL_WRAPPER_CLASS(BoolData, bool)
IMPL_WRAPPER_CLASS(IntData, int)
IMPL_WRAPPER_CLASS(FloatData, float)
IMPL_WRAPPER_CLASS(StringData, std::string)
IMPL_WRAPPER_CLASS(Bool, bool)
IMPL_WRAPPER_CLASS(Int, int)
IMPL_WRAPPER_CLASS(Float, float)
IMPL_WRAPPER_CLASS(String, std::string)

View file

@ -9,14 +9,17 @@ public:\
operator WRAPPED_TYPE(); \
};
class VoidData {
public:
VoidData();
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)
};
DEF_WRAPPER_CLASS(BoolData, bool)
DEF_WRAPPER_CLASS(IntData, int)
DEF_WRAPPER_CLASS(FloatData, float)
DEF_WRAPPER_CLASS(StringData, std::string)
#undef DEF_WRAPPER_CLASS

View file

@ -1,4 +1,3 @@
#pragma once
#include "base/metadata.h"
#include "base/instance.h"

View file

@ -1,6 +1,9 @@
#include "instance.h"
#include "../../common.h"
#include "objects/base/member.h"
#include <algorithm>
#include <cstddef>
#include <cstdio>
#include <memory>
#include <optional>
@ -21,6 +24,13 @@ InstanceType* Instance::TYPE = &TYPE_;
Instance::Instance(InstanceType* type) {
this->name = type->className;
this->memberMap = {
.super = std::nullopt,
.members = {
{ "Name", { .backingField = &name } }
}
};
}
Instance::~Instance () {
@ -55,4 +65,59 @@ std::optional<std::shared_ptr<Instance>> Instance::GetParent() {
void Instance::OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) {
// Empty stub
}
// Properties
tl::expected<std::string, MemberNotFound> Instance::GetPropertyValue(std::string name) {
auto meta = GetPropertyMeta(name);
if (!meta) return tl::make_unexpected(MemberNotFound());
return *(std::string*)meta->backingField;
}
tl::expected<void, MemberNotFound> Instance::SetPropertyValue(std::string name, std::string value) {
auto meta = GetPropertyMeta(name);
if (!meta) return tl::make_unexpected(MemberNotFound());
*(std::string*)meta->backingField = value;
return {};
}
tl::expected<PropertyMeta, MemberNotFound> Instance::GetPropertyMeta(std::string name) {
MemberMap* current = &memberMap;
while (true) {
// We look for the property in current member map
auto it = current->members.find(name);
// It is found, return it
if (it != current->members.end())
return it->second;
// It is not found, If there are no other maps to search, return null
if (!current->super.has_value())
return tl::make_unexpected(MemberNotFound());
// Search in the parent
current = current->super->get();
}
}
std::vector<std::string> Instance::GetProperties() {
if (cachedMemberList.has_value()) return cachedMemberList.value();
std::vector<std::string> memberList;
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());
cachedMemberList = memberList;
return memberList;
}

View file

@ -1,9 +1,21 @@
#pragma once
#include "metadata.h"
#include <vector>
#include <memory>
#include <optional>
#include <string>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <map>
#include <vector>
#include <../include/expected.hpp>
#include "objects/base/member.h"
class Instance;
typedef std::shared_ptr<Instance>(*InstanceConstructor)();
// Struct describing information about an instance
struct InstanceType {
@ -21,7 +33,11 @@ class Instance : public std::enable_shared_from_this<Instance> {
private:
std::optional<std::weak_ptr<Instance>> parent;
std::vector<std::shared_ptr<Instance>> children;
std::optional<std::vector<std::string>> cachedMemberList;
protected:
MemberMap memberMap;
Instance(InstanceType*);
virtual ~Instance();
@ -38,6 +54,14 @@ public:
// Utility functions
inline void AddChild(std::shared_ptr<Instance> object) { object->SetParent(this->shared_from_this()); }
// 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<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();
};
typedef std::shared_ptr<Instance> InstanceRef;

21
src/objects/base/member.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <map>
#include <memory>
#include <optional>
#include <variant>
class Instance;
struct PropertyMeta {
void* backingField;
};
typedef std::variant<PropertyMeta> MemberMeta;
struct MemberMap {
std::optional<std::unique_ptr<MemberMap>> super;
std::map<std::string, PropertyMeta> members;
};
struct MemberNotFound {};

View file

@ -1,60 +0,0 @@
#pragma once
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <map>
#include <vector>
#include "../../datatype.h"
class Instance;
typedef std::shared_ptr<Instance>(*InstanceConstructor)();
const uint INST_NOT_CREATABLE = 1;
// const uint INST_SINGLETON = 2;
typedef uint InstanceClassFlags;
struct InstanceClassDescriptor {
std::string className;
InstanceConstructor constructor;
InstanceClassFlags flags;
};
//
const uint PROP_READONLY = 1;
const uint PROP_NOSCRIPT = 2; // Cannot be read or written to by unpriveleged scripts
const uint PROP_NOSAVE = 4; // Property should not be serialized by the engine
const uint PROP_CLIENTONLY = 8; // Only accessible by the client
typedef uint PropertyFlags;
typedef DataValue(*InstancePropertyGetter)();
typedef void(*InstancePropertySetter)(DataValue);
// Properties may either have a backing field directly accessed by Instance,
// or, for more granular control may define a getter (and setter if writable).
struct MemberPropertyDescriptor {
DataType dataType;
PropertyFlags flags;
std::optional<DataValue*> backingField;
std::optional<InstancePropertyGetter> getter;
std::optional<InstancePropertySetter> setter;
};
//
typedef DataValue(*InstanceMethodHandler)(std::vector<DataValue>);
struct MemberMethodDescriptor {
DataType returnType;
// This may need to be converted into a vector in the future for overloaded methods
std::vector<DataType> parameters;
InstanceMethodHandler handler;
};
// TODO: Add MemberCallbackDescriptor
typedef std::map<std::string, std::variant<MemberPropertyDescriptor, MemberMethodDescriptor /*, MemberEventDescriptor */ /* , MemberCallbackDescriptor */>> InstanceMemberTable;