summaryrefslogtreecommitdiff
path: root/include/serialize.h
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2016-07-28 21:29:35 -0400
committerAdam <Adam@anope.org>2016-07-28 21:29:35 -0400
commit0e758a2ac23dc4a001e8e126cec14588da9a9769 (patch)
tree45df813323e023c5c89db7279426c4ad0943b4a9 /include/serialize.h
parenta3c8afae00c54d5b95c620248b51f90679d7d53f (diff)
Allow serializable fields to use storage in the respective objects.
Split service management code nito a proper servicemanager. Make service references managed instead of lazy lookup. Also made events and serializable use service manager instead of their respective systems for management
Diffstat (limited to 'include/serialize.h')
-rw-r--r--include/serialize.h426
1 files changed, 301 insertions, 125 deletions
diff --git a/include/serialize.h b/include/serialize.h
index 1dc4d4e26..3ee9ce376 100644
--- a/include/serialize.h
+++ b/include/serialize.h
@@ -1,13 +1,10 @@
/*
*
- * (C) 2003-2014 Anope Team
+ * (C) 2003-2016 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- *
*/
#pragma once
@@ -22,7 +19,6 @@ namespace Serialize
class Object;
class TypeBase;
- class AbstractType;
class FieldBase;
template<typename> class FieldTypeBase;
@@ -32,9 +28,7 @@ namespace Serialize
template<typename T, typename> class Type;
template<typename T> class Reference;
- template<typename T> class TypeReference;
- extern std::map<Anope::string, Serialize::TypeBase *> Types;
// by id
extern std::unordered_map<ID, Object *> objects;
extern std::vector<FieldBase *> serializableFields;
@@ -42,7 +36,16 @@ namespace Serialize
extern Object *GetID(ID id);
template<typename T>
- inline std::vector<T> GetObjects(Serialize::TypeBase *type);
+ inline T GetObject();
+
+ template<typename T>
+ inline std::vector<T> GetObjects(const Anope::string &name);
+
+ template<typename T>
+ inline std::vector<T> GetObjects();
+
+ template<typename T>
+ inline T New();
extern void Clear();
@@ -61,6 +64,12 @@ namespace Serialize
return other == e.other && field == e.field && direction == e.direction;
}
};
+
+ extern std::multimap<Anope::string, Anope::string> child_types;
+
+ extern void SetParent(const Anope::string &child, const Anope::string &parent);
+
+ extern std::vector<Serialize::TypeBase *> GetTypes(const Anope::string &name);
}
class CoreExport Serialize::Object : public Extensible, public virtual Base
@@ -73,13 +82,12 @@ class CoreExport Serialize::Object : public Extensible, public virtual Base
std::map<TypeBase *, std::vector<Edge>> edges;
- std::vector<Serialize::Edge> GetRefs(TypeBase *);
+ std::vector<Edge> GetRefs(TypeBase *);
- protected:
+ public:
Object(TypeBase *type);
Object(TypeBase *type, ID);
- public:
virtual ~Object();
virtual void Delete();
@@ -91,38 +99,58 @@ class CoreExport Serialize::Object : public Extensible, public virtual Base
void RemoveEdge(Object *other, FieldBase *field);
+ /**
+ * Get an object of type T that this object references.
+ */
template<typename T>
- T GetRef(TypeBase *type)
+ T GetRef()
{
- std::vector<T> t = GetRefs<T>(type);
+ std::vector<T> t = GetRefs<T>();
return !t.empty() ? t[0] : nullptr;
}
-
+
+ /**
+ * Gets all objects of type T that this object references
+ */
template<typename T>
- std::vector<T> GetRefs(TypeBase *type);
+ std::vector<T> GetRefs();
+ /**
+ * Get the value of a field on this object.
+ */
template<
- typename Type, // inherits from Serialize::Type
- template<typename, typename> class Field,
- typename TypeImpl, // implementation of Type
+ typename Type,
+ template<typename, typename> class Field, // Field type being read
+ typename TypeImpl,
typename T // type of the Extensible
>
T Get(Field<TypeImpl, T> Type::*field)
{
+ static_assert(std::is_base_of<Object, TypeImpl>::value, "");
+ static_assert(std::is_base_of<Serialize::TypeBase, Type>::value, "");
+
Type *t = static_cast<Type *>(s_type);
Field<TypeImpl, T>& f = t->*field;
return f.GetField(f.Upcast(this));
}
- /* the return value can't be inferred by the compiler so we supply this function to allow us to specify it */
+ /**
+ * Get the value of a field on this object. Allows specifying return
+ * type if the return type can't be inferred.
+ */
template<typename Ret, typename Field, typename Type>
Ret Get(Field Type::*field)
{
+ static_assert(std::is_base_of<Serialize::TypeBase, Type>::value, "");
+
Type *t = static_cast<Type *>(s_type);
Field& f = t->*field;
return f.GetField(f.Upcast(this));
}
+ /**
+ * Set the value of a field on this object
+ */
template<
typename Type,
template<typename, typename> class Field,
@@ -131,14 +159,22 @@ class CoreExport Serialize::Object : public Extensible, public virtual Base
>
void Set(Field<TypeImpl, T> Type::*field, const T& value)
{
+ static_assert(std::is_base_of<Object, TypeImpl>::value, "");
+ static_assert(std::is_base_of<Serialize::TypeBase, Type>::value, "");
+
Type *t = static_cast<Type *>(s_type);
Field<TypeImpl, T>& f = t->*field;
f.SetField(f.Upcast(this), value);
}
+ /**
+ * Set the value of a field on this object
+ */
template<typename Field, typename Type, typename T>
void Set(Field Type::*field, const T& value)
{
+ static_assert(std::is_base_of<Serialize::TypeBase, Type>::value, "");
+
Type *t = static_cast<Type *>(s_type);
Field& f = t->*field;
f.SetField(f.Upcast(this), value);
@@ -149,12 +185,19 @@ class CoreExport Serialize::Object : public Extensible, public virtual Base
*/
TypeBase* GetSerializableType() const { return this->s_type; }
+ /** Set the value of a field on this object, by field name
+ */
template<typename T>
void SetS(const Anope::string &name, const T &what);
+ /** Unset a field on this object, by field name
+ */
template<typename T>
void UnsetS(const Anope::string &name);
+ /** Test if a field is set. Only useful with extensible fields,
+ * which can unset (vs set to the default value)
+ */
bool HasFieldS(const Anope::string &name);
};
@@ -162,16 +205,14 @@ class CoreExport Serialize::TypeBase : public Service
{
Anope::string name;
- TypeBase *parent = nullptr;
- std::vector<TypeBase *> children;
-
/* Owner of this type. Used for placing objects of this type in separate databases
* based on what module, if any, owns it.
*/
Module *owner;
public:
- std::vector<FieldBase *> fields;
+ static constexpr const char *NAME = "typebase";
+
std::set<Object *> objects;
TypeBase(Module *owner, const Anope::string &n);
@@ -179,20 +220,19 @@ class CoreExport Serialize::TypeBase : public Service
void Unregister();
- protected:
- void SetParent(TypeBase *other);
-
- public:
/** Gets the name for this type
* @return The name, eg "NickAlias"
*/
const Anope::string &GetName() { return this->name; }
+ /**
+ * Get all objects of the given type
+ */
template<typename T>
std::vector<T> List()
{
std::vector<ID> ids;
- EventReturn result = Event::OnSerialize(&Event::SerializeEvents::OnSerializeList, this, ids);
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeList, this, ids);
if (result == EVENT_ALLOW)
{
std::vector<T> o;
@@ -205,44 +245,39 @@ class CoreExport Serialize::TypeBase : public Service
return o;
}
- return GetObjects<T>(this);
+ return GetObjects<T>();
}
+ /** Create a new object of this type.
+ */
virtual Object *Create() anope_abstract;
+
+ /** Get or otherwise create an object of this type
+ * with the given ID.
+ */
virtual Object *Require(Serialize::ID) anope_abstract;
+ /** Find a field on this type
+ */
FieldBase *GetField(const Anope::string &name);
- Module* GetOwner() const { return this->owner; }
+ /** Get all fields of this type
+ */
+ std::vector<FieldBase *> GetFields();
- std::vector<TypeBase *> GetSubTypes();
+ Module* GetOwner() const { return this->owner; }
static TypeBase *Find(const Anope::string &name);
static const std::map<Anope::string, TypeBase *>& GetTypes();
};
-class Serialize::AbstractType : public Serialize::TypeBase
-{
- public:
- using Serialize::TypeBase::TypeBase;
-
- Object *Create() override
- {
- return nullptr;
- }
-
- Object *Require(ID id) override
- {
- return nullptr;
- }
-};
-
template<typename T, typename Base = Serialize::TypeBase>
class Serialize::Type : public Base
{
public:
- using Base::Base;
+ Type(Module *module) : Base(module, T::NAME) { }
+ Type(Module *module, const Anope::string &name) : Base(module, name) { }
Object *Create() override
{
@@ -267,25 +302,12 @@ class Serialize::Type : public Base
}
};
-template<typename T>
-class Serialize::TypeReference : public ServiceReference<Serialize::TypeBase>
-{
- public:
- TypeReference(const Anope::string &name) : ServiceReference<Serialize::TypeBase>("Serialize::Type", name) { }
-
- T* Create()
- {
- TypeBase *t = *this;
- return static_cast<T *>(t->Create());
- }
-
- TypeBase *ToType()
- {
- TypeBase *t = *this;
- return t;
- }
-};
-
+/** A reference to a serializable object. Serializable objects may not always
+ * exist in memory at all times, like if they exist in an external database,
+ * so you can't hold pointers to them. Instead, hold a Serialize::Reference
+ * which will properly fetch the object on demand from the underlying database
+ * system.
+ */
template<typename T>
class Serialize::Reference
{
@@ -331,7 +353,7 @@ class Serialize::Reference
if (targ != nullptr && targ->GetSerializableType() == type)
return anope_dynamic_static_cast<T*>(targ);
- EventReturn result = Event::OnSerialize(&Event::SerializeEvents::OnSerializeDeref, id, type);
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeDeref, id, type);
if (result == EVENT_ALLOW)
return anope_dynamic_static_cast<T *>(type->Require(id));
@@ -339,33 +361,40 @@ class Serialize::Reference
}
};
-class Serialize::FieldBase
+/** A field, associated with a type.
+ */
+class Serialize::FieldBase : public Service
{
public:
- struct Listener : ServiceReference<TypeBase>
- {
- FieldBase *base;
+ static constexpr const char *NAME = "fieldbase";
- Listener(FieldBase *b, const ServiceReference<TypeBase> &t) : ServiceReference<TypeBase>(t), base(b) { }
+ Anope::string serialize_type; // type the field is on
+ Anope::string serialize_name; // field name
- void OnAcquire() override;
- } type;
-
- Module *creator;
- Anope::string name;
+ /** For fields which reference other Objects. If true, when
+ * the object the field references gets deleted, this object
+ * gets deleted too.
+ */
bool depends;
- bool unregister = false;
- FieldBase(Module *, const Anope::string &, const ServiceReference<Serialize::TypeBase> &, bool);
+ FieldBase(Module *, const Anope::string &, const Anope::string &, bool);
virtual ~FieldBase();
void Unregister();
- const Anope::string &GetName() { return name; }
+ /** Serialize value of this field on the given object to string form
+ */
virtual Anope::string SerializeToString(Object *s) anope_abstract;
+
+ /** Unserialize value of this field on the given object from string form
+ */
virtual void UnserializeFromString(Object *s, const Anope::string &) anope_abstract;
- virtual void CacheMiss(Object *) anope_abstract;
+ /** Test if the given object has the given field, only usefil for extensible fields
+ */
virtual bool HasFieldS(Object *) anope_abstract;
+
+ /** Unset this field on the given object
+ */
virtual void UnsetS(Object *) anope_abstract;
};
@@ -375,33 +404,115 @@ class Serialize::FieldTypeBase : public FieldBase
public:
using FieldBase::FieldBase;
+ /** Set this field to the given value on the given object.
+ */
virtual void SetFieldS(Object *, const T &) anope_abstract;
};
-template<typename TypeImpl, typename T>
+/** Base class for serializable fields and serializable object fields.
+ */
+template<
+ typename TypeImpl, // Serializable type
+ typename T // actual type of field
+>
class Serialize::CommonFieldBase : public FieldTypeBase<T>
{
+ static_assert(std::is_base_of<Object, TypeImpl>::value, "");
+
+ /** Extensible storage for value of fields. Only used if field
+ * isn't set. Note extensible fields can be "unset", where field
+ * pointers are never unset, but are T().
+ */
+ ExtensibleItem<T> *ext = nullptr;
+
+ /** Field pointer to storage in the TypeImpl object for
+ * this field.
+ */
+ T TypeImpl::*field = nullptr;
+
protected:
- ExtensibleItem<T> ext;
+ /** Set the value of a field in storage
+ */
+ void Set_(TypeImpl *object, const T &value)
+ {
+ if (field != nullptr)
+ object->*field = value;
+ else if (ext != nullptr)
+ ext->Set(object, value);
+ else
+ throw CoreException("No field or ext");
+ }
+
+ /* Get the value of a field from storage
+ */
+ T* Get_(TypeImpl *object)
+ {
+ if (field != nullptr)
+ return &(object->*field);
+ else if (ext != nullptr)
+ return ext->Get(object);
+ else
+ throw CoreException("No field or ext");
+ }
+
+ /** Unset a field from storage
+ */
+ void Unset_(TypeImpl *object)
+ {
+ if (field != nullptr)
+ object->*field = T();
+ else if (ext != nullptr)
+ ext->Unset(object);
+ else
+ throw CoreException("No field or ext");
+ }
+
+ /** Check is a field is set. Only useful for
+ * extensible storage. Returns true for field storage.
+ */
+ bool HasField_(TypeImpl *object)
+ {
+ if (field != nullptr)
+ return true;
+ else if (ext != nullptr)
+ return ext->HasExt(object);
+ else
+ throw CoreException("No field or ext");
+ }
public:
- CommonFieldBase(Serialize::TypeBase *t, const Anope::string &n, bool depends)
- : FieldTypeBase<T>(t->GetOwner(), n, ServiceReference<Serialize::TypeBase>("Serialize::Type", t->GetName()), depends)
- , ext(t->GetOwner(), t->GetName(), n)
+ CommonFieldBase(Serialize::TypeBase *t, const Anope::string &n, bool d)
+ : FieldTypeBase<T>(t->GetOwner(), n, t->GetName(), d)
+ {
+ ext = new ExtensibleItem<T>(t->GetOwner(), t->GetName(), n);
+ }
+
+ CommonFieldBase(Module *creator, const Anope::string &n, bool d)
+ : FieldTypeBase<T>(creator, n, TypeImpl::NAME, d)
{
+ ext = new ExtensibleItem<T>(creator, TypeImpl::NAME, n);
}
- CommonFieldBase(Module *creator, Serialize::TypeReference<TypeImpl> &typeref, const Anope::string &n, bool depends)
- : FieldTypeBase<T>(creator, n, typeref, depends)
- , ext(creator, typeref.GetName(), n)
+ CommonFieldBase(Serialize::TypeBase *t,
+ const Anope::string &n,
+ T TypeImpl::*f,
+ bool d)
+ : FieldTypeBase<T>(t->GetOwner(), n, t->GetName(), d)
+ , field(f)
{
}
- void CacheMiss(Object *s) override
+ ~CommonFieldBase()
{
- ext.Set(s, T());
+ delete ext;
}
+ /** Get the value of this field on the given object
+ */
+ virtual T GetField(TypeImpl *) anope_abstract;
+
+ /** Unset this field on the given object
+ */
virtual void UnsetField(TypeImpl *) anope_abstract;
void UnsetS(Object *s) override
@@ -409,26 +520,36 @@ class Serialize::CommonFieldBase : public FieldTypeBase<T>
UnsetField(Upcast(s));
}
+ bool HasFieldS(Object *s) override
+ {
+ return HasField(Upcast(s));
+ }
+
+ /** Cast a serializable object of type Object to type TypeImpl,
+ * if appropriate
+ */
TypeImpl* Upcast(Object *s)
{
- if (this->type != s->GetSerializableType())
+ if (this->serialize_type != s->GetSerializableType()->GetName())
+ {
return nullptr;
+ }
return anope_dynamic_static_cast<TypeImpl *>(s);
}
- bool HasFieldS(Object *s) override
- {
- return HasField(Upcast(s));
- }
-
bool HasField(TypeImpl *s)
{
- EventReturn result = Event::OnSerialize(&Event::SerializeEvents::OnSerializeHasField, s, this);
- return result != EVENT_CONTINUE;
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeHasField, s, this);
+ if (result != EVENT_CONTINUE)
+ return true;
+
+ return this->HasField_(s);
}
};
+/** Class for all fields that aren't to other serializable objects
+ */
template<typename TypeImpl, typename T>
class Serialize::Field : public CommonFieldBase<TypeImpl, T>
{
@@ -437,28 +558,39 @@ class Serialize::Field : public CommonFieldBase<TypeImpl, T>
{
}
- Field(Module *creator, TypeReference<TypeImpl> &typeref, const Anope::string &n) : CommonFieldBase<TypeImpl, T>(creator, typeref, n, false)
+ Field(Module *creator, const Anope::string &n) : CommonFieldBase<TypeImpl, T>(creator, n, false)
+ {
+ }
+
+ Field(TypeBase *t, const Anope::string &n, T TypeImpl::*f) : CommonFieldBase<TypeImpl, T>(t, n, f, false)
{
}
T GetField(TypeImpl *s)
{
- T* t = this->ext.Get(s);
- if (t)
+ T* t = this->Get_(s);
+
+ // If we have a non-default value for this field it is up to date and cached
+ if (t && *t != T())
return *t;
+ // Query modules
Anope::string value;
- EventReturn result = Event::OnSerialize(&Event::SerializeEvents::OnSerializeGet, s, this, value);
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeGet, s, this, value);
if (result == EVENT_ALLOW)
{
// module returned us data, so we unserialize it
T t2 = this->Unserialize(value);
- this->ext.Set(s, t2);
+ // Cache
+ this->Set_(s, t2);
return t2;
}
+ if (t)
+ return *t;
+
return T();
}
@@ -470,16 +602,16 @@ class Serialize::Field : public CommonFieldBase<TypeImpl, T>
virtual void SetField(TypeImpl *s, const T &value)
{
Anope::string strvalue = this->Serialize(value);
- Event::OnSerialize(&Event::SerializeEvents::OnSerializeSet, s, this, strvalue);
+ EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeSet, s, this, strvalue);
- this->ext.Set(s, value);
+ this->Set_(s, value);
}
void UnsetField(TypeImpl *s) override
{
- Event::OnSerialize(&Event::SerializeEvents::OnSerializeUnset, s, this);
+ EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeUnset, s, this);
- this->ext.Unset(s);
+ this->Unset_(s);
}
Anope::string Serialize(const T& t)
@@ -490,13 +622,14 @@ class Serialize::Field : public CommonFieldBase<TypeImpl, T>
}
catch (const ConvertException &)
{
+ Log(LOG_DEBUG) << "Unable to stringify " << t;
return "";
}
}
T Unserialize(const Anope::string &str)
{
- if (str.empty()) // for caching not set
+ if (str.empty())
return T();
try
@@ -546,6 +679,8 @@ class Serialize::Field : public CommonFieldBase<TypeImpl, T>
}
};
+/** Class for all fields that contain a reference to another serializable object.
+ */
template<typename TypeImpl, typename T>
class Serialize::ObjectField : public CommonFieldBase<TypeImpl, T>
{
@@ -554,28 +689,38 @@ class Serialize::ObjectField : public CommonFieldBase<TypeImpl, T>
{
}
+ ObjectField(TypeBase *t, const Anope::string &n, T TypeImpl::*field, bool d = false) : CommonFieldBase<TypeImpl, T>(t, n, field, d)
+ {
+ }
+
T GetField(TypeImpl *s)
{
- T *t = this->ext.Get(s);
- if (t)
+ T *t = this->Get_(s);
+ if (t && *t != nullptr)
return *t;
Anope::string type;
ID sid;
- EventReturn result = Event::OnSerialize(&Event::SerializeEvents::OnSerializeGetSerializable, s, this, type, sid);
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeGetSerializable, s, this, type, sid);
if (result != EVENT_CONTINUE)
{
Serialize::TypeBase *base = Serialize::TypeBase::Find(type);
if (base == nullptr)
+ {
+ Log(LOG_DEBUG_2) << "OnSerializeGetSerializable returned unknown type " << type;
return nullptr;
+ }
T t2 = result == EVENT_ALLOW ? static_cast<T>(base->Require(sid)) : nullptr;
- this->ext.Set(s, t2);
+ this->Set_(s, t2);
return t2;
}
+ if (t)
+ return *t;
+
return T();
}
@@ -586,13 +731,13 @@ class Serialize::ObjectField : public CommonFieldBase<TypeImpl, T>
virtual void SetField(TypeImpl *s, T value)
{
- Event::OnSerialize(&Event::SerializeEvents::OnSerializeSetSerializable, s, this, value);
+ EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeSetSerializable, s, this, value);
- T *old = this->ext.Get(s);
+ T *old = this->Get_(s);
if (old != nullptr && *old != nullptr)
s->RemoveEdge(*old, this);
- this->ext.Set(s, value);
+ this->Set_(s, value);
if (value != nullptr)
s->AddEdge(value, this);
@@ -600,9 +745,9 @@ class Serialize::ObjectField : public CommonFieldBase<TypeImpl, T>
void UnsetField(TypeImpl *s) override
{
- Event::OnSerialize(&Event::SerializeEvents::OnSerializeUnsetSerializable, s, this);
+ EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeUnsetSerializable, s, this);
- this->ext.Unset(s);
+ this->Unset_(s);
}
Anope::string SerializeToString(Object *s) override
@@ -650,11 +795,18 @@ class Serialize::ObjectField : public CommonFieldBase<TypeImpl, T>
};
template<typename T>
-std::vector<T> Serialize::GetObjects(Serialize::TypeBase *type)
+T Serialize::GetObject()
+{
+ std::vector<T> v = GetObjects<T>();
+ return v.empty() ? nullptr : v[0];
+}
+
+template<typename T>
+std::vector<T> Serialize::GetObjects(const Anope::string &name)
{
std::vector<T> objs;
- for (TypeBase *t : type->GetSubTypes())
+ for (TypeBase *t : GetTypes(name))
for (Object *s : t->objects)
objs.push_back(anope_dynamic_static_cast<T>(s));
@@ -662,17 +814,41 @@ std::vector<T> Serialize::GetObjects(Serialize::TypeBase *type)
}
template<typename T>
-std::vector<T> Serialize::Object::GetRefs(Serialize::TypeBase *type)
+std::vector<T> Serialize::GetObjects()
{
- std::vector<T> objs;
+ const char* const name = std::remove_pointer<T>::type::NAME;
+ return GetObjects<T>(name);
+}
+
+template<typename T>
+T Serialize::New()
+{
+ const char* const name = std::remove_pointer<T>::type::NAME;
+ Serialize::TypeBase *type = TypeBase::Find(name);
if (type == nullptr)
{
- Log(LOG_DEBUG) << "GetRefs for unknown type on #" << this->id << " type " << s_type->GetName();
+ Log(LOG_DEBUG) << "Serialize::New with unknown type " << name;
+ return nullptr;
+ }
+
+ return static_cast<T>(type->Create());
+}
+
+template<typename T>
+std::vector<T> Serialize::Object::GetRefs()
+{
+ const char* const name = std::remove_pointer<T>::type::NAME;
+ std::vector<Serialize::TypeBase *> types = GetTypes(name);
+ std::vector<T> objs;
+
+ if (types.empty())
+ {
+ Log(LOG_DEBUG) << "GetRefs for unknown type on #" << this->id << " type " << s_type->GetName() << " named " << name;
return objs;
}
- for (Serialize::TypeBase *t : type->GetSubTypes())
+ for (Serialize::TypeBase *t : types)
for (const Serialize::Edge &edge : GetRefs(t))
if (!edge.direction)
objs.push_back(anope_dynamic_static_cast<T>(edge.other));