cpp-reflect
C++ Reflection and Annotations Library
Loading...
Searching...
No Matches
equality.cppm
Go to the documentation of this file.
1// Copyright (c) 2024-2025, Víctor Castillo Agüero.
2// SPDX-License-Identifier: GPL-3.0-or-later
3
8
9export module reflect:equality;
10
11import std;
12
13import packtl;
14
15import :types;
16import :accessors;
17
18export namespace refl::eq_policy {
24}
25
26export namespace refl {
27 template<Reflected R>
28 bool deep_eq(const R &lhs, const R &rhs);
29}
30
32 template<typename T>
33 bool ref_eq(const T &lhs, const T &rhs);
34
35 template<typename I>
36 bool std_iterable_eq(const I &lhs, const I &rhs) {
37 using T = typename I::value_type;
38
39 if (lhs.size() != rhs.size()) {
40 return false;
41 }
42 for (auto it1 = lhs.begin(), it2 = rhs.begin(); it1 != lhs.end() && it2 != rhs.end();
43 ++it1, ++it2) {
44 if (not ref_eq<T>(*it1, *it2)) {
45 return false;
46 }
47 }
48 return true;
49 }
50
51 template<typename P>
52 bool std_pair_eq(const P &lhs, const P &rhs) {
53 using T1 = typename P::first_type;
54 using T2 = typename P::second_type;
55
56 if (not ref_eq<T1>(lhs.first, rhs.first)) {
57 return false;
58 }
59 if (not ref_eq<T2>(lhs.second, rhs.second)) {
60 return false;
61 }
62
63 return true;
64 }
65
66 template<typename I>
67 bool std_map_eq(const I &lhs, const I &rhs) {
68 using T = typename I::value_type;
69
70 if (lhs.size() != rhs.size()) {
71 return false;
72 }
73 for (auto it1 = lhs.begin(), it2 = rhs.begin(); it1 != lhs.end() && it2 != rhs.end();
74 ++it1, ++it2) {
75 if (not std_pair_eq<T>(*it1, *it2)) {
76 return false;
77 }
78 }
79 return true;
80 }
81
82 template<typename F>
83 bool std_function_eq(const F &lhs, const F &rhs) {
84 // I know this isn't correct, but properly comparing an std::function is impossible in C++
85 return lhs.target_type() == rhs.target_type();
86 }
87
88 template<typename... Ts>
89 bool std_tuple_eq(const std::tuple<Ts...> &lhs, const std::tuple<Ts...> &rhs) {
90 return [&]<std::size_t ... I>(std::index_sequence<I...>) -> bool {
91 return (ref_eq(std::get<I>(lhs), std::get<I>(rhs)) and ...);
92 }(std::make_index_sequence<sizeof...(Ts)> { });
93 }
94
95 template<typename T>
96 bool ref_eq(const T &lhs, const T &rhs) {
97 if constexpr (packtl::is_type<std::vector, T>::value) {
98 return std_iterable_eq(lhs, rhs);
99 } else if constexpr (packtl::is_type<std::list, T>::value) {
100 return std_iterable_eq(lhs, rhs);
101 } else if constexpr (packtl::is_type<std::deque, T>::value) {
102 return std_iterable_eq(lhs, rhs);
103 } else if constexpr (packtl::is_type<std::queue, T>::value) {
104 return std_iterable_eq(lhs.__get_container(), rhs.__get_container());
105 } else if constexpr (packtl::is_type<std::stack, T>::value) {
106 return std_iterable_eq(lhs, rhs);
107 } else if constexpr (packtl::is_type<std::map, T>::value) {
108 return std_map_eq(lhs, rhs);
109 } else if constexpr (packtl::is_type<std::unordered_map, T>::value) {
110 return std_map_eq(lhs, rhs);
111 } else if constexpr (packtl::is_type<std::set, T>::value) {
112 return std_iterable_eq(lhs, rhs);
113 } else if constexpr (packtl::is_type<std::unordered_set, T>::value) {
114 return std_iterable_eq(lhs, rhs);
115 } else if constexpr (packtl::is_type<std::pair, T>::value) {
116 return std_pair_eq(lhs, rhs);
117 } else if constexpr (packtl::is_type<std::function, T>::value) {
118 return std_function_eq(lhs, rhs);
119 } else if constexpr (packtl::is_type<std::tuple, T>::value) {
120 return std_tuple_eq(lhs, rhs);
121 } else if constexpr (std::equality_comparable<T>) {
122 return lhs == rhs;
123 } else if constexpr (Reflected<T>) {
124 return deep_eq(lhs, rhs);
125 } else {
126 return false;
127 }
128 }
129
130 template<Reflected R, typename field_data>
131 bool field_eq(const R &lhs, const R &rhs) {
132 const auto &field1 = field_data::from_instance(lhs);
133 const auto &field2 = field_data::from_instance(rhs);
134
136 if constexpr (field_data::template has_metadata<eq_policy::policy_e>) {
137 policy = field_data::template get_metadata<eq_policy::policy_e>;
138 }
139
140 if (policy == eq_policy::skip) {
141 return true;
142 }
143
144 if constexpr (field_data::is_reference) {
145 if (policy == eq_policy::deep) {
146 if (&field1 == &field2) {
147 return true;
148 }
149
150 using field_type = std::remove_reference_t<typename field_data::type>;
151 return ref_eq<field_type>(field1, field2);
152 } else if (policy == eq_policy::shallow) {
153 return &field1 == &field2;
154 }
155 } else if constexpr (field_data::is_pointer) {
156 if (policy == eq_policy::deep) {
157 if (field1 == field2) {
158 return true;
159 }
160
161 using field_type = std::remove_pointer_t<typename field_data::type>;
162 if constexpr (std::is_void_v<field_type>) {
163 return false;
164 } else {
165 return ref_eq<field_type>(*field1, *field2);
166 }
167 } else if (policy == eq_policy::shallow) {
168 return field1 == field2;
169 }
170 } else {
171 using field_type = typename field_data::type;
172 return ref_eq<field_type>(field1, field2);
173 }
174
175 return false;
176 }
177} // namespace refl::deep_eq_impl
178
179namespace refl {
180 template<Reflected R>
181 bool deep_eq(const R &lhs, const R &rhs) {
182 static constexpr auto count = field_count<R>;
183
184 auto impl = [&]<std::size_t... I>(std::index_sequence<I...>) {
185 return ((deep_eq_impl::field_eq<R, field<R, I>>(lhs, rhs)) && ...);
186 };
187
188 return impl(std::make_index_sequence<count> { });
189 }
190}
bool std_iterable_eq(const I &lhs, const I &rhs)
Definition equality.cppm:36
bool field_eq(const R &lhs, const R &rhs)
bool std_function_eq(const F &lhs, const F &rhs)
Definition equality.cppm:83
bool std_pair_eq(const P &lhs, const P &rhs)
Definition equality.cppm:52
bool std_tuple_eq(const std::tuple< Ts... > &lhs, const std::tuple< Ts... > &rhs)
Definition equality.cppm:89
bool std_map_eq(const I &lhs, const I &rhs)
Definition equality.cppm:67
bool ref_eq(const T &lhs, const T &rhs)
Definition equality.cppm:96
bool deep_eq(const R &lhs, const R &rhs)
constexpr std::size_t field_count