feat: runtime property enumeration
This commit is contained in:
parent
9f42b9815f
commit
ce1e4d4f39
8 changed files with 2604 additions and 76 deletions
2476
include/expected.hpp
Normal file
2476
include/expected.hpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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)
|
|
@ -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
|
|
@ -1,4 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/metadata.h"
|
||||
#include "base/instance.h"
|
|
@ -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;
|
||||
}
|
|
@ -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
21
src/objects/base/member.h
Normal 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 {};
|
|
@ -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;
|
Loading…
Add table
Reference in a new issue