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