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 final : public singleton<double_dispatch_matrix<method_ptr_t>>
78 {
79  using class_method_ptr_traits_type = double_dispatch_class_method_ptr_traits<method_ptr_t>;
80 
81 public:
82  using base_type = typename class_method_ptr_traits_type::class_type;
83  using return_type = typename class_method_ptr_traits_type::return_type;
84  using function_type = std::function<return_type(const base_type& first, const base_type& second)>;
85 
86  static_assert(std::derived_from<base_type, rtti_pure_base>);
87 
88  /**
89  @brief Add a function to a matrix
90  @details If there was a function it will be overriden
91  @tparam first_derived_t - the first type for a double dispatching
92  @tparam second_derived_t - the second type for a double dispatching, can be same as first_derived_t
93  @param function - a function that takes objects of types
94  first_derived_t and second_derived_t and returns return_type
95  **/
96  template<class first_derived_t, class second_derived_t>
97  requires(
98  std::is_base_of_v<typename double_dispatch_matrix<method_ptr_t>::base_type, first_derived_t>
99  && std::is_base_of_v<typename double_dispatch_matrix<method_ptr_t>::base_type, second_derived_t>)
100  void register_function(
101  std::function<return_type(const first_derived_t&, const second_derived_t&)> function) noexcept;
102 
103  /**
104  @brief Call a function if there is any
105  @param first - first object
106  @param second - second object
107  @retval - a return value if a function for these types found, std::nullopt otherwise
108  **/
109  std::optional<return_type> invoke(const base_type& first, const base_type& second);
110 
111  /**
112  @brief Check that for each registered type there is a match to all other types,
113  including the type itself, and that all these functions are valid.
114  @retval - true if this symmetric matrix is complete
115  **/
116  bool check_matrix_completeness() const noexcept;
117 
118 private:
119  /**
120  @brief Get or create a function for two types
121  @param first - first type
122  @param second - second type (can be same as the first type)
123  @retval - function
124  **/
125  function_type& get_function(class_id first, class_id second) noexcept;
126 
127 private:
128  std::unordered_map<class_id, std::unordered_map<class_id, function_type>> m_MatrixFunctions;
129 };
130 
131 } // namespace qx
132 
133 #include <qx/patterns/double_dispatch_matrix.inl>
Inherit the necessary singleton class from this.
requires(same_variadic_args_v< args_t... >) const expr auto coalesce(args_t &&... args)
Coalesce function, C# a ?? b analogue.
Definition: coalesce.inl:57
RTTI system based on polymorphism.