qxLib
double_dispatch_matrix.h
Go to the documentation of this file.
1 /**
2 
3  @file double_dispatch_matrix.h
4  @author Khrapov
5  @date 17.01.2025
6  @copyright © Nick Khrapov, 2025. All right reserved.
7 
8 **/
9 #pragma once
10 
11 #include <qx/patterns/singleton.h>
12 #include <qx/rtti/rtti.h>
13 #include <qx/rtti/rtti_cast.h>
14 #include "qx/meta/type_traits.h"
15 
16 #include <algorithm>
17 #include <concepts>
18 #include <functional>
19 #include <optional>
20 
21 namespace qx
22 {
23 
24 template<auto method_ptr_t>
25 struct double_dispatch_class_method_ptr_traits;
26 
27 template<class class_t, class return_t, return_t (class_t::*method_ptr_t)(const class_t&) const>
28 struct double_dispatch_class_method_ptr_traits<method_ptr_t>
29 {
30  using class_type = class_t;
31  using return_type = return_t;
32 };
33 
34 /**
35 
36  @class double_dispatch_matrix
37  @brief A class that implements the double dispatch mechanism in C++.
38  @details This class allows users to add their classes dynamically,
39  so you must call check_matrix_completeness() after register_function()
40  @tparam method_ptr_t - a base class method that takes another object of this class.
41  the class must use QX_OBJECT, e.g. use qx rtti system.
42  @code
43  class shape : public base
44  {
45  QX_OBJECT(base, shape);
46 
47  public:
48  bool intersects(const shape& other) const;
49  };
50 
51  using shape_intersection_double_dispatch_t = qx::double_dispatch_matrix<&shape::intersects>;
52 
53  class circle : public shape
54  {
55  ...
56  };
57 
58  class square : public shape
59  {
60  ...
61  };
62 
63  QX_CALL_BEFORE_MAIN = []()
64  {
65  auto& matrix = shape_intersection_double_dispatch_t::get_instance();
66  matrix.register_function(...);
67  matrix.register_function(...);
68  ...
69  QXE_EXPECT(matrix.check_matrix_completeness());
70  };
71  @author Khrapov
72  @date 16.02.2025
73 
74 **/
75 template<auto method_ptr_t>
76  requires is_specialization_exist_v<double_dispatch_class_method_ptr_traits<method_ptr_t>>
77 class double_dispatch_matrix
78 {
79  QX_SINGLETON(double_dispatch_matrix);
80 
81  using class_method_ptr_traits_type = double_dispatch_class_method_ptr_traits<method_ptr_t>;
82 
83 public:
84  using base_type = typename class_method_ptr_traits_type::class_type;
85  using return_type = typename class_method_ptr_traits_type::return_type;
86  using function_type = std::function<return_type(const base_type& first, const base_type& second)>;
87 
88  static_assert(std::derived_from<base_type, rtti_pure_base>);
89 
90  /**
91  @brief Add a function to a matrix
92  @details If there was a function it will be overriden
93  @tparam first_derived_t - the first type for a double dispatching
94  @tparam second_derived_t - the second type for a double dispatching, can be same as first_derived_t
95  @param function - a function that takes objects of types
96  first_derived_t and second_derived_t and returns return_type
97  **/
98  template<class first_derived_t, class second_derived_t>
99  requires(
100  std::is_base_of_v<typename double_dispatch_matrix<method_ptr_t>::base_type, first_derived_t>
101  && std::is_base_of_v<typename double_dispatch_matrix<method_ptr_t>::base_type, second_derived_t>)
102  void register_function(
103  std::function<return_type(const first_derived_t&, const second_derived_t&)> function) noexcept;
104 
105  /**
106  @brief Call a function if there is any
107  @param first - first object
108  @param second - second object
109  @retval - a return value if a function for these types found, std::nullopt otherwise
110  **/
111  std::optional<return_type> invoke(const base_type& first, const base_type& second);
112 
113  /**
114  @brief Check that for each registered type there is a match to all other types,
115  including the type itself, and that all these functions are valid.
116  @retval - true if this symmetric matrix is complete
117  **/
118  bool check_matrix_completeness() const noexcept;
119 
120 private:
121  /**
122  @brief Get or create a function for two types
123  @param first - first type
124  @param second - second type (can be same as the first type)
125  @retval - function
126  **/
127  function_type& get_function(class_id first, class_id second) noexcept;
128 
129 private:
130  std::unordered_map<class_id, std::unordered_map<class_id, function_type>> m_MatrixFunctions;
131 };
132 
133 } // namespace qx
134 
135 #include <qx/patterns/double_dispatch_matrix.inl>
RTTI system based on polymorphism.
#define QX_SINGLETON(T)
Simple Meyer's singleton.
Definition: singleton.h:93