throw std::invalid_argument("Owned::Owned");
Stage &stage = get_stage(*parent);
- /* TODO Add a flag to only do this when first_created in an outer call is
- true. */
- if(typeid(*parent)==typeid(P))
- {
- Reflection::Class<P> &parent_class = stage.get_reflector().get_or_create_class<P>();
- if(!parent_class.is_up_to_date())
- parent_class.template set_polymorphic_base<Entity>(*parent);
- }
-
Pool<T> &pool = stage.get_pools().get_pool<T>();
bool first_created = !pool.get_capacity();
this->ptr = pool.create(parent, std::forward<Args>(args)...);
if constexpr(std::is_base_of_v<Component, T>)
{
if(first_created)
- stage.get_reflector().get_or_create_class<T>().template set_polymorphic_base<Component>(**this);
+ stage.get_reflector().get_or_create_class<T>().discover_bases(this->ptr);
parent->add_component(*this);
stage.get_event_source().emit<Events::ComponentCreated>(*this);
}
else
{
if(first_created)
- stage.get_reflector().get_or_create_class<T>().template set_polymorphic_base<Entity>(**this);
+ stage.get_reflector().get_or_create_class<T>().discover_bases(this->ptr);
parent->add_child(*this);
stage.get_event_source().emit<Events::EntityCreated>(*this);
}
#include "reflection.h"
+#include <msp/core/algorithm.h>
#include <msp/debug/demangle.h>
using namespace std;
namespace Msp::Game {
namespace Reflection {
-ClassBase::ClassBase(Reflector &r, type_index t):
+struct SubObject
+{
+ ClassBase *type = nullptr;
+ std::size_t offset = 0;
+ std::size_t size = 0;
+};
+
+
+ClassBase::ClassBase(Reflector &r, type_index t, size_t s):
reflector(r),
type(t),
- name(Debug::demangle(t.name()))
+ name(Debug::demangle(t.name())),
+ size(s)
{ }
bool ClassBase::is_direct_base_of(const ClassBase &other) const
return ranges::any_of(other.bases, [this](const ClassBase *b){ return is_base_of(*b); });
}
+void ClassBase::discover_bases(const void *obj)
+{
+ if(!polymorphism)
+ return;
+
+ auto has_same_root = [this](const ClassBase &c){ return c.polymorphism && polymorphism->has_same_root(*c.polymorphism); };
+
+ std::vector<SubObject> hierarchy;
+ for(ClassBase *c: reflector.find_classes_if(has_same_root))
+ if(c!=this && polymorphism->is_instance_of(obj, *c->polymorphism))
+ hierarchy.emplace_back(c, polymorphism->get_offset_of_base(obj, *c->polymorphism), c->get_size());
+
+ sort_member(hierarchy, &SubObject::size);
+ hierarchy.emplace_back(this, 0, size);
+
+ bool any_changed = false;
+ for(auto i=hierarchy.begin(); i!=hierarchy.end(); ++i)
+ {
+ vector<const ClassBase *> detected_bases;
+ for(auto j=hierarchy.begin(); (j!=hierarchy.end() && j->size<=i->size); ++j)
+ {
+ bool enclosed = (j->offset>=i->offset && j->offset+j->size<i->offset+i->size);
+ if(j!=i && j->offset<size && (i->type==this || enclosed || j->type->is_direct_base_of(*i->type)))
+ {
+ detected_bases.push_back(j->type);
+ j->offset = size;
+ }
+ }
+
+ if(detected_bases!=i->type->bases)
+ {
+ i->type->bases = detected_bases;
+ any_changed = true;
+ }
+ }
+
+ if(any_changed)
+ ++reflector.generation;
+
+ for(const SubObject &o: hierarchy)
+ o.type->generation = reflector.generation;
+}
+
ClassBase *Reflector::find_class(const type_index &type) const
{
public:
virtual ~PolymorphismBase() = default;
+ virtual bool has_same_root(const PolymorphismBase &) const = 0;
virtual bool is_instance_of(const void *, const PolymorphismBase &) const = 0;
+ virtual std::size_t get_offset_of_base(const void *, const PolymorphismBase &) const = 0;
};
class RootedPolymorphism: public PolymorphismBase
{
public:
+ bool has_same_root(const PolymorphismBase &p) const override { return dynamic_cast<const RootedPolymorphism *>(&p); }
virtual bool is_instance(const B &) const = 0;
+ virtual const B &as_root(const void *) const = 0;
+ virtual const void *as_derived(const B &) const = 0;
};
public:
bool is_instance(const B &obj) const override { return std::is_same_v<B, T> || dynamic_cast<const T *>(&obj); }
bool is_instance_of(const void *, const PolymorphismBase &) const override;
+ const B &as_root(const void *p) const override { return *static_cast<const T *>(p); }
+ const void *as_derived(const B &obj) const override { return dynamic_cast<const T *>(&obj); }
+ std::size_t get_offset_of_base(const void *, const PolymorphismBase &) const override;
};
Reflector &reflector;
std::type_index type;
std::string name;
+ std::size_t size;
std::vector<const ClassBase *> bases;
std::unique_ptr<PolymorphismBase> polymorphism;
unsigned generation = 0;
- ClassBase(Reflector &, std::type_index);
+ ClassBase(Reflector &, std::type_index, std::size_t);
public:
virtual ~ClassBase() = default;
const std::type_index &get_type() const { return type; }
const std::string &get_name() const { return name; }
+ std::size_t get_size() const { return size; }
const std::vector<const ClassBase *> &get_direct_bases() const { return bases; }
bool is_direct_base_of(const ClassBase &) const;
template<typename T>
bool is_instance(const T &) const;
- template<typename T>
- bool has_polymorphic_base() const { return dynamic_cast<const RootedPolymorphism<T> *>(polymorphism.get()); }
+ virtual void discover_bases(const void *);
bool is_up_to_date() const;
-
-protected:
- void increment_generation() const;
};
template<typename B>
void set_polymorphic_base();
-
- template<typename B>
- void set_polymorphic_base(const T &obj) { set_polymorphic_base<B>(); check_bases<B>(obj); }
-
-private:
- template<typename B>
- void check_bases(const T &obj);
};
return false;
}
+template<typename T, typename B>
+ requires std::is_base_of_v<B, T>
+inline std::size_t Polymorphism<T, B>::get_offset_of_base(const void *obj, const PolymorphismBase &other) const
+{
+ const void *base_obj = dynamic_cast<const RootedPolymorphism<B> &>(other).as_derived(as_root(obj));
+ return reinterpret_cast<std::uintptr_t>(base_obj)-reinterpret_cast<std::uintptr_t>(obj);
+}
+
template<typename T>
inline bool ClassBase::is_instance(const T &obj) const
{
- if(const RootedPolymorphism<T> *p = dynamic_cast<const RootedPolymorphism<T> *>(polymorphism.get()))
+ if constexpr(requires { typename T::PolymorphicBase; })
+ {
+ if(auto p = dynamic_cast<const RootedPolymorphism<typename T::PolymorphicBase> *>(polymorphism.get()))
+ return p->is_instance(obj);
+ }
+ else if(auto p = dynamic_cast<const RootedPolymorphism<T> *>(polymorphism.get()))
return p->is_instance(obj);
else if(ClassBase *c = reflector.find_class<T>(); c->polymorphism)
return c->polymorphism->is_instance_of(&obj, *polymorphism);
return generation==reflector.get_generation();
}
-inline void ClassBase::increment_generation() const
-{
- ++reflector.generation;
-}
-
template<typename T>
Class<T>::Class(Reflector &r):
- ClassBase(r, typeid(T))
+ ClassBase(r, typeid(T), sizeof(T))
{
if constexpr(requires { typename T::PolymorphicBase; })
set_polymorphic_base<typename T::PolymorphicBase>();
throw std::logic_error("conflicting polymorphism");
}
-template<typename T>
-template<typename B>
-inline void Class<T>::check_bases(const T &obj)
-{
- if(typeid(obj)!=type)
- throw std::invalid_argument("Class::check_bases");
-
- std::vector<const ClassBase *> candidate_bases;
- for(const ClassBase *b: reflector.find_classes_if([](const ClassBase &c){ return c.has_polymorphic_base<B>(); }))
- if(b!=this && b->is_instance<B>(obj))
- candidate_bases.push_back(b);
-
- for(auto i=candidate_bases.begin(); i!=candidate_bases.end(); )
- {
- if(std::ranges::any_of(candidate_bases, [i](const ClassBase *c){ return (c!=*i && (*i)->is_direct_base_of(*c)); }))
- i = candidate_bases.erase(i);
- else
- ++i;
- }
-
- if(candidate_bases!=bases)
- {
- bases = std::move(candidate_bases);
- increment_generation();
- }
- generation = reflector.get_generation();
-}
-
} // namespace Reflection
} // namespace Msp::Game
inline T &Stage::add_system(Args &&... args)
{
auto &sys = static_cast<T &>(*systems.emplace_back(std::make_unique<T>(*this, std::forward<Args>(args)...)));
- reflector.get_or_create_class<T>().template set_polymorphic_base<System>(sys);
+ reflector.get_or_create_class<T>().discover_bases(&sys);
scheduler.add_system(sys);
return sys;
}
void polymorphism_conflict();
void deep_polymorphism();
void branching_polymorphism();
+ void empty_intermediate_base();
};
-struct A { virtual ~A() = default; };
-struct B: A { };
-struct C: B { };
-struct D: C { };
-struct C2: B { };
+
+namespace {
+
+struct A { using PolymorphicBase = A; void *a_member; virtual ~A() = default; };
+struct B: A { void *b_member; };
+struct C: B { void *c_member; };
+struct D: C { void *d_member; };
+struct C2: B { void *c2_member; };
+struct E: A { };
+struct F: E { void *f_member; };
+
+} // anonymous namespace
ReflectionTests::ReflectionTests()
add(&ReflectionTests::polymorphism_conflict, "Polymorphism conflict").expect_throw<std::logic_error>();
add(&ReflectionTests::deep_polymorphism, "Deep polymorphism");
add(&ReflectionTests::branching_polymorphism, "Branching polymorphism");
+ add(&ReflectionTests::empty_intermediate_base, "Empty intermediate base");
}
Game::Reflection::Reflector reflector;
Game::Reflection::Class<A> &a_class = reflector.get_or_create_class<A>();
- a_class.set_polymorphic_base<A>();
Game::Reflection::Class<B> &b_class = reflector.get_or_create_class<B>();
- b_class.set_polymorphic_base<A>(B());
+
+ B b_obj;
+ b_class.discover_bases(&b_obj);
EXPECT(a_class.is_direct_base_of(b_class));
EXPECT(!b_class.is_direct_base_of(a_class));
EXPECT(a_class.is_instance(a_obj));
EXPECT(!b_class.is_instance(a_obj));
- B b_obj;
EXPECT(a_class.is_instance(b_obj));
EXPECT(b_class.is_instance(b_obj));
Game::Reflection::Reflector reflector;
Game::Reflection::Class<A> &a_class = reflector.get_or_create_class<A>();
- a_class.set_polymorphic_base<A>();
Game::Reflection::Class<B> &b_class = reflector.get_or_create_class<B>();
- b_class.set_polymorphic_base<A>(B());
Game::Reflection::Class<C> &c_class = reflector.get_or_create_class<C>();
- c_class.set_polymorphic_base<A>(C());
Game::Reflection::Class<D> &d_class = reflector.get_or_create_class<D>();
- d_class.set_polymorphic_base<A>(D());
+
+ D d_obj;
+ d_class.discover_bases(&d_obj);
EXPECT(a_class.is_direct_base_of(b_class));
EXPECT(!a_class.is_direct_base_of(c_class));
EXPECT(b_class.is_base_of(d_class));
EXPECT(c_class.is_direct_base_of(d_class));
- D d_obj;
EXPECT(a_class.is_instance(d_obj));
EXPECT(b_class.is_instance(d_obj));
EXPECT(c_class.is_instance(d_obj));
Game::Reflection::Reflector reflector;
Game::Reflection::Class<A> &a_class = reflector.get_or_create_class<A>();
- a_class.set_polymorphic_base<A>();
Game::Reflection::Class<B> &b_class = reflector.get_or_create_class<B>();
- b_class.set_polymorphic_base<A>(B());
Game::Reflection::Class<C> &c_class = reflector.get_or_create_class<C>();
- c_class.set_polymorphic_base<A>(C());
Game::Reflection::Class<C2> &c2_class = reflector.get_or_create_class<C2>();
- c2_class.set_polymorphic_base<A>(C2());
+
+ C c_obj;
+ C2 c2_obj;
+ c_class.discover_bases(&c_obj);
+ c2_class.discover_bases(&c2_obj);
EXPECT(a_class.is_direct_base_of(b_class));
EXPECT(a_class.is_base_of(c_class));
EXPECT(!c_class.is_base_of(c2_class));
EXPECT(!c2_class.is_base_of(c_class));
- C c_obj;
- C2 c2_obj;
EXPECT(a_class.is_instance(c_obj));
EXPECT(a_class.is_instance(c2_obj));
EXPECT(b_class.is_instance(c_obj));
EXPECT(c2_class.is_instance(c2_obj));
EXPECT(!c2_class.is_instance(c_obj));
}
+
+void ReflectionTests::empty_intermediate_base()
+{
+ Game::Reflection::Reflector reflector;
+
+ Game::Reflection::Class<A> &a_class = reflector.get_or_create_class<A>();
+ Game::Reflection::Class<E> &e_class = reflector.get_or_create_class<E>();
+ Game::Reflection::Class<F> &f_class = reflector.get_or_create_class<F>();
+
+ E e_obj;
+ F f_obj;
+ e_class.discover_bases(&e_obj);
+ f_class.discover_bases(&f_obj);
+
+ EXPECT(a_class.is_direct_base_of(e_class));
+ EXPECT(!a_class.is_direct_base_of(f_class));
+ EXPECT(e_class.is_direct_base_of(f_class));
+ EXPECT(a_class.is_base_of(f_class));
+}