qxLib
rtti.h
Go to the documentation of this file.
1 /**
2 
3  @file rtti.h
4  @brief RTTI system based on polymorphism
5  @details Using qx::rtti_root and QX_RTTI_CLASS
6  will let you to use RTTI advantages without enabling
7  RTTI in compiler's flags
8  common logic is taken from
9  https://github.com/qualab/xakep-RTTI (Vladimir Kerimov, qualab)
10  @author Khrapov
11  @date 24.08.2020
12  @copyright (c) Nick Khrapov, 2021. All right reserved.
13 
14 **/
15 #pragma once
16 
18 #include <qx/macros/common.h>
20 #include <qx/meta/tuple_utils.h>
21 #include <qx/rtti/class_id.h>
23 
24 #include <array>
25 #include <span>
26 
27 namespace qx
28 {
29 
30 // utility class to let you use std::derived_from
32 {
33  QX_COPYMOVABLE(rtti_pure_base);
34 
35  template<class naming_strategy_t>
36  friend class rtti_root;
37 
38 public:
39  auto operator<=>(const rtti_pure_base&) const = default;
40 
41 private:
42  rtti_pure_base() = default;
43 };
44 
45 /**
46 
47  @class rtti_root
48  @brief Polymorphic base for qx class identity and inheritance queries.
49  @details Derive an RTTI hierarchy from this type and use QX_RTTI_CLASS in each
50  derived class to supply its identity and extend the inheritance chain.
51  This system does not require compiler RTTI.
52 
53  Static queries describe the type declaring them; virtual queries
54  describe the object's most-derived registered type. is<T>() checks
55  exact identity, while is_derived_from<T>() also accepts base types.
56 
57  The naming strategy affects class names, not class identifiers.
58  Names may be empty or shared by several types. The protected destructor
59  prevents deleting an object directly through an rtti_root pointer.
60  @tparam naming_strategy_t - strategy providing get_name(string_view) to transform
61  the compiler-generated type signature into a class name
62  @see rtti_naming_strategy.h
63  @author Khrapov
64  @date 10.09.2021
65 
66 **/
67 template<class naming_strategy_t = rtti_naming_strategy_class_name>
68 class QX_EMPTY_BASES rtti_root : public rtti_pure_base
69 {
71 
72 public:
73  using base_class_type = rtti_root;
75  using this_class_type = rtti_root;
76  using inheritance_tuple_type = std::tuple<this_class_type>;
77  using naming_strategy_type = naming_strategy_t;
78 
79 public:
80  rtti_root() noexcept = default;
81 
82  auto operator<=>(const rtti_root&) const = default;
83 
84  /**
85  @brief Test exact identity or membership in the object's RTTI base chain.
86  @tparam T - candidate type exposing QX_RTTI_CLASS()
87  @retval - true for the object's registered type and its registered bases
88  **/
89  template<class T>
90  bool is_derived_from() const noexcept;
91 
92  /**
93  @brief Compare the object's dynamic class identifier with T's identifier.
94  @tparam T - candidate type exposing QX_RTTI_CLASS()
95  @retval - true only when the identifiers match; base types are not accepted
96  **/
97  template<class T>
98  bool is() const noexcept;
99 
100  /**
101  @brief Compare against the class name selected by the naming strategy.
102  @note Name equality does not imply type identity when the naming strategy
103  produces duplicate names, such as rtti_naming_strategy_empty.
104  @param svClassName - expected result of get_class_name(), not a base-class name
105  @retval - true when the names match exactly
106  **/
107  bool is(string_view svClassName) const noexcept;
108 
109  /**
110  @brief Compare against the object's dynamic class identifier.
111  @param id - candidate for exact identity, not an inheritance query
112  @retval - true when id equals get_class_id()
113  **/
114  bool is(const class_id& id) const noexcept;
115 
116  /**
117  @brief Test an identifier against the object's dynamic RTTI hierarchy.
118  @param id - candidate class identifier, including the object's own identifier
119  @retval - true when id belongs to the registered type or one of its bases
120  **/
121  virtual bool is_derived_from_id(const class_id& id) const noexcept;
122 
123  /**
124  @brief Test an identifier against the declaring type's static RTTI hierarchy.
125  @details rtti_root matches only its own identifier; QX_RTTI_CLASS extends
126  this comparison recursively through the declared base class.
127  @param id - candidate class identifier to find in the static base chain
128  @retval - true when id belongs to the declaring type or one of its bases
129  **/
130  static constexpr bool is_derived_from_id_static(const class_id& id) noexcept;
131 
132  /**
133  @brief Apply the naming strategy to the declaring RTTI type's signature.
134  @retval - a class-name view that may be empty, depending on the strategy
135  **/
136  static constexpr string_view get_class_name_static() noexcept;
137 
138  /**
139  @brief Get the name of the object's most-derived registered RTTI type.
140  @retval - the naming strategy's result for the dynamic type; it may be empty
141  **/
142  virtual string_view get_class_name() const noexcept;
143 
144  /**
145  @brief Create the declaring RTTI type's identifier from its type signature.
146  @note The signature is compiler-dependent; identifiers are not a portable
147  format for persisting type identity across different builds.
148  @retval - identifier independent of the selected class-name representation
149  **/
150  static constexpr class_id get_class_id_static() noexcept;
151 
152  /**
153  @brief Get the identifier of the object's most-derived registered RTTI type.
154  @retval - identifier used by exact-type comparisons and RTTI casts
155  **/
156  virtual class_id get_class_id() const noexcept;
157 
158  /**
159  @brief Get the registered inheritance chain in base-to-derived order.
160  @retval - identifiers from the rtti_root specialization through the dynamic type
161  @details QX_RTTI_CLASS returns a span backed by class-specific static storage,
162  rather than storage owned by the object.
163  **/
164  virtual std::span<const class_id> get_inheritance_sequence() const = 0;
165 
166 protected:
167  virtual ~rtti_root() noexcept = default;
168 
169  /**
170  @brief Traverse identifiers treated as bases by derived RTTI classes.
171  @param idBase - candidate identifier from an inheritance query
172  @retval - true when an identifier matches during base-chain traversal
173  @details The root compares against its own identifier. Macro-generated
174  overrides check their immediate base and continue recursively.
175  **/
176  virtual bool _is_base_id(const class_id& idBase) const noexcept;
177 };
178 
179 namespace filters
180 {
181 
182 template<class T>
183 constexpr auto is_derived_from = [](const auto& value)
184 {
185  return value.template is_derived_from<T>();
186 };
187 
188 template<class T>
189 constexpr auto is = [](const auto& value)
190 {
191  return value.template is<T>();
192 };
193 
194 } // namespace filters
195 
196 /**
197  @def QX_RTTI_CLASS
198  @brief Add dynamic identity and inheritance queries to a class in a qx RTTI hierarchy.
199  @param thisClass - type being registered, used to generate its identifier and name
200  @param ... - immediate base type, either rtti_root or a type using QX_RTTI_CLASS
201 **/
202 #define QX_RTTI_CLASS(thisClass, ...) \
203  \
204 public: \
205  using this_class_type = thisClass; \
206  using super_class_type = __VA_ARGS__; \
207  using base_class_type = typename super_class_type::base_class_type; \
208  using inheritance_tuple_type = \
209  qx::tuple_utils::join_t<typename super_class_type::inheritance_tuple_type, this_class_type>; \
210  \
211 public: \
212  static constexpr bool is_derived_from_id_static(const qx::class_id& id) noexcept \
213  { \
214  return id == get_class_id_static() || super_class_type::is_derived_from_id_static(id); \
215  } \
216  virtual bool is_derived_from_id(const qx::class_id& id) const noexcept override \
217  { \
218  static_assert(std::is_same_v<std::remove_cvref_t<decltype(*this)>, this_class_type>); \
219  return is_derived_from_id_static(id); \
220  } \
221  static constexpr qx::string_view get_class_name_static() noexcept \
222  { \
223  return base_class_type::naming_strategy_type::get_name(qx::type_strings<this_class_type>::get_signature()); \
224  } \
225  virtual qx::class_id get_class_id() const noexcept override \
226  { \
227  return get_class_id_static(); \
228  } \
229  static constexpr qx::class_id get_class_id_static() noexcept \
230  { \
231  constexpr qx::class_id id = qx::class_id::create<this_class_type>(); \
232  return id; \
233  } \
234  virtual qx::string_view get_class_name() const noexcept override \
235  { \
236  return get_class_name_static(); \
237  } \
238  virtual std::span<const qx::class_id> get_inheritance_sequence() const override \
239  { \
240  static constexpr auto s_Ids = []() \
241  { \
242  std::array<qx::class_id, std::tuple_size_v<inheritance_tuple_type>> ids_; \
243  qx::tuple_utils::iterate<inheritance_tuple_type>( \
244  [&ids_]<class T, size_t I>() \
245  { \
246  ids_[I] = T::get_class_id_static(); \
247  }); \
248  return ids_; \
249  }(); \
250  return s_Ids; \
251  } \
252  \
253 protected: \
254  virtual bool _is_base_id(const qx::class_id& base_id) const noexcept override \
255  { \
256  return base_id == super_class_type::get_class_id_static() || super_class_type::_is_base_id(base_id); \
257  } \
258  \
259 private: \
260  QX_EMPTY_MACRO
261 
262 } // namespace qx
263 
264 #include <qx/rtti/rtti.inl>
Class id, unique for each class using qx rtti system.
Definition: class_id.h:27
Polymorphic base for qx class identity and inheritance queries.
Definition: rtti.h:69
virtual std::span< const class_id > get_inheritance_sequence() const =0
Get the registered inheritance chain in base-to-derived order.
#define QX_COPYMOVABLE(className)
Define class as default copyable and movable.
Contains naming strategies for qx::rtti_root class.
Contains utils for working with std::tuple.