]> git.tdb.fi Git - libs/game.git/blob - source/game/reflection.h
Add reflection infrastructure
[libs/game.git] / source / game / reflection.h
1 #ifndef MSP_GAME_REFLECTION_H_
2 #define MSP_GAME_REFLECTION_H_
3
4 #include <algorithm>
5 #include <memory>
6 #include <string>
7 #include <typeindex>
8 #include <vector>
9
10 namespace Msp::Game {
11 namespace Reflection {
12
13 class ClassBase;
14 class Reflector;
15
16 class PolymorphismBase
17 {
18 public:
19         virtual ~PolymorphismBase() = default;
20
21         virtual bool is_instance_of(const void *, const PolymorphismBase &) const = 0;
22 };
23
24
25 template<typename B>
26 class RootedPolymorphism: public PolymorphismBase
27 {
28 public:
29         virtual bool is_instance(const B &) const = 0;
30 };
31
32
33 template<typename T, typename B>
34         requires std::is_base_of_v<B, T>
35 class Polymorphism: public RootedPolymorphism<B>
36 {
37 public:
38         bool is_instance(const B &obj) const override { return std::is_same_v<B, T> || dynamic_cast<const T *>(&obj); }
39         bool is_instance_of(const void *, const PolymorphismBase &) const override;
40 };
41
42
43 class ClassBase
44 {
45 protected:
46         Reflector &reflector;
47         std::type_index type;
48         std::string name;
49         std::vector<const ClassBase *> bases;
50         std::unique_ptr<PolymorphismBase> polymorphism;
51
52         ClassBase(Reflector &, std::type_index);
53 public:
54         virtual ~ClassBase() = default;
55
56         const std::type_index &get_type() const { return type; }
57         const std::string &get_name() const { return name; }
58
59         bool is_direct_base_of(const ClassBase &) const;
60         bool is_base_of(const ClassBase &) const;
61
62         template<typename T>
63         bool is_instance(const T &) const;
64
65         template<typename T>
66         bool has_polymorphic_base() const { return dynamic_cast<const RootedPolymorphism<T> *>(polymorphism.get()); }
67 };
68
69
70 template<typename T>
71 class Class: public ClassBase
72 {
73 public:
74         Class(Reflector &r): ClassBase(r, typeid(T)) { }
75
76         template<typename B>
77         void set_polymorphic_base();
78
79         template<typename B>
80         void set_polymorphic_base(const T &obj) { set_polymorphic_base<B>(); check_bases<B>(obj); }
81
82 private:
83         template<typename B>
84         void check_bases(const T &obj);
85 };
86
87
88 class Reflector
89 {
90 private:
91         std::vector<std::unique_ptr<ClassBase>> classes;
92
93         std::vector<std::unique_ptr<ClassBase>>::const_iterator lower_bound(const std::type_index &) const;
94
95 public:
96         template<typename T>
97         Class<T> *find_class() const;
98
99         template<typename T>
100         Class<T> &get_or_create_class();
101
102         template<typename F>
103         std::vector<ClassBase *> find_classes_if(F &&) const;
104 };
105
106
107 inline std::vector<std::unique_ptr<ClassBase>>::const_iterator Reflector::lower_bound(const std::type_index &type) const
108 {
109         return std::ranges::lower_bound(classes, type, {}, [](auto &c){ return c->get_type(); });
110 }
111
112 template<typename T>
113 inline Class<T> *Reflector::find_class() const
114 {
115         std::type_index type = typeid(T);
116         auto i = lower_bound(type);
117         return (i!=classes.end() && (*i)->get_type()==type ? static_cast<Class<T> *>(i->get()) : nullptr);
118 }
119
120 template<typename T>
121 inline Class<T> &Reflector::get_or_create_class()
122 {
123         std::type_index type = typeid(T);
124         auto i = lower_bound(type);
125         if(i==classes.end() || (*i)->get_type()!=type)
126                 i = classes.emplace(i, std::make_unique<Class<T>>(std::ref(*this)));
127         return static_cast<Class<T> &>(*i->get());
128 }
129
130 template<typename F>
131 inline std::vector<ClassBase *> Reflector::find_classes_if(F &&pred) const
132 {
133         std::vector<ClassBase *> result;
134         for(auto &c: classes)
135                 if(pred(*c))
136                         result.push_back(&*c);
137         return result;
138 }
139
140
141 template<typename T, typename B>
142 inline bool Polymorphism<T, B>::is_instance_of(const void *obj, const PolymorphismBase &other) const
143 {
144         if(const RootedPolymorphism<B> *rooted = dynamic_cast<const RootedPolymorphism<B> *>(&other))
145                 return rooted->is_instance(*static_cast<const T *>(obj));
146         return false;
147 }
148
149
150 template<typename T>
151 inline bool ClassBase::is_instance(const T &obj) const
152 {
153         if(const RootedPolymorphism<T> *p = dynamic_cast<const RootedPolymorphism<T> *>(polymorphism.get()))
154                 return p->is_instance(obj);
155         else if(ClassBase *c = reflector.find_class<T>(); c->polymorphism)
156                 return c->polymorphism->is_instance_of(&obj, *polymorphism);
157         return false;
158 }
159
160
161 template<typename T>
162 template<typename B>
163 inline void Class<T>::set_polymorphic_base()
164 {
165         if(!polymorphism)
166                 polymorphism = std::make_unique<Polymorphism<T, B>>();
167         else if(!dynamic_cast<const Polymorphism<T, B> *>(polymorphism.get()))
168                 throw std::logic_error("conflicting polymorphism");
169 }
170
171 template<typename T>
172 template<typename B>
173 inline void Class<T>::check_bases(const T &obj)
174 {
175         std::vector<const ClassBase *> candidate_bases;
176         for(const ClassBase *b: reflector.find_classes_if([](const ClassBase &c){ return c.has_polymorphic_base<B>(); }))
177                 if(b!=this && b->is_instance<B>(obj))
178                         candidate_bases.push_back(b);
179
180         for(auto i=candidate_bases.begin(); i!=candidate_bases.end(); )
181         {
182                 if(std::ranges::any_of(candidate_bases, [i](const ClassBase *c){ return (c!=*i && (*i)->is_direct_base_of(*c)); }))
183                         i = candidate_bases.erase(i);
184                 else
185                         ++i;
186         }
187
188         bases = std::move(candidate_bases);
189 }
190
191 } // namespace Reflection
192 } // namespace Msp::Game
193
194 #endif