#pragma once #include #include #include #include #include #include #include #include #include #include #include // #include <../../include/expected.hpp> #include #include #include "member.h" class Instance; typedef std::shared_ptr(*InstanceConstructor)(); class DataModel; class Workspace; typedef int InstanceFlags; // This instance should only be instantiated in special circumstances (i.e. by DataModel) and should be creatable directly via any API const InstanceFlags INSTANCE_NOTCREATABLE = (InstanceFlags)0x1; // This instance is a service const InstanceFlags INSTANCE_SERVICE = (InstanceFlags)0x2; // Struct describing information about an instance struct InstanceType { const InstanceType* super; // May be null std::string className; InstanceConstructor constructor; std::string explorerIcon = ""; InstanceFlags flags; }; class DescendantsIterator; // Base class for all instances in the data model // Note: enable_shared_from_this HAS to be public or else its field will not be populated // Maybe this could be replaced with a friendship? But that seems unnecessary. // https://stackoverflow.com/q/56415222/16255372 class Instance : public std::enable_shared_from_this { private: std::optional> parent; std::vector> children; std::optional> cachedMemberList; bool ancestryContinuityCheck(std::optional> newParent); protected: bool parentLocked = false; std::unique_ptr memberMap; Instance(const InstanceType*); virtual ~Instance(); virtual void OnParentUpdated(std::optional> oldParent, std::optional> newParent); // The root data model this object is a descendant of std::optional> dataModel(); // The root workspace this object is a descendant of // NOTE: This value is not necessarily present if dataModel is present // Objects under services other than workspace will NOT have this field set std::optional> workspace(); template inline std::shared_ptr shared() { return std::dynamic_pointer_cast(this->shared_from_this()); } public: const static InstanceType TYPE; std::string name; // Instance is abstract, so it should not implement GetClass directly virtual const InstanceType* GetClass() = 0; bool SetParent(std::optional> newParent); std::optional> GetParent(); bool IsParentLocked(); inline const std::vector> GetChildren() { return children; } DescendantsIterator GetDescendantsStart(); DescendantsIterator GetDescendantsEnd(); // Utility functions inline void AddChild(std::shared_ptr object) { object->SetParent(this->shared_from_this()); } // Properties // Do I like using expected? 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(); // Serialization void Serialize(pugi::xml_node parent); static std::shared_ptr Deserialize(pugi::xml_node node); }; typedef std::shared_ptr InstanceRef; typedef std::weak_ptr InstanceRefWeak; // https://gist.github.com/jeetsukumaran/307264 class DescendantsIterator { public: typedef DescendantsIterator self_type; typedef std::shared_ptr value_type; typedef std::shared_ptr& reference; typedef std::shared_ptr pointer; typedef std::forward_iterator_tag iterator_category; typedef int difference_type; DescendantsIterator(std::shared_ptr current); inline self_type operator++() { self_type i = *this; ++*this; return i; } inline std::shared_ptr operator*() { return current; } inline std::shared_ptr operator->() { return current; } inline bool operator==(const self_type& rhs) { return current == rhs.current; } inline bool operator!=(const self_type& rhs) { return current != rhs.current; } self_type operator++(int _); private: std::optional> root; std::shared_ptr current; std::vector siblingIndex; };