GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
#ifndef ZSERIO_REFLECTABLE_UTIL_H_INC |
||
2 |
#define ZSERIO_REFLECTABLE_UTIL_H_INC |
||
3 |
|||
4 |
#include <algorithm> |
||
5 |
#include <functional> |
||
6 |
#include <cmath> |
||
7 |
#include <limits> |
||
8 |
|||
9 |
#include "zserio/CppRuntimeException.h" |
||
10 |
#include "zserio/IReflectable.h" |
||
11 |
#include "zserio/ITypeInfo.h" |
||
12 |
#include "zserio/StringView.h" |
||
13 |
#include "zserio/Traits.h" |
||
14 |
#include "zserio/TypeInfoUtil.h" |
||
15 |
|||
16 |
namespace zserio |
||
17 |
{ |
||
18 |
|||
19 |
namespace detail |
||
20 |
{ |
||
21 |
|||
22 |
template <typename T> |
||
23 |
struct gets_value_by_value : std::integral_constant<bool, |
||
24 |
std::is_arithmetic<T>::value || |
||
25 |
std::is_same<StringView, T>::value || |
||
26 |
std::is_enum<T>::value || |
||
27 |
is_bitmask<T>::value> |
||
28 |
{}; |
||
29 |
|||
30 |
} // namespace detail |
||
31 |
|||
32 |
/** |
||
33 |
* Utilities on zserio reflectable interface. |
||
34 |
*/ |
||
35 |
class ReflectableUtil |
||
36 |
{ |
||
37 |
public: |
||
38 |
/** |
||
39 |
* Makes "deep" comparison of given reflectables. |
||
40 |
* |
||
41 |
* \note Floating point values are compared using "almost equal" strategy. |
||
42 |
* |
||
43 |
* \param lhs Left-hand side reflectable. |
||
44 |
* \param rhs Right-hand side reflectable. |
||
45 |
* |
||
46 |
* \return True when the reflectables are equal, false otherwise. |
||
47 |
*/ |
||
48 |
template <typename ALLOC = std::allocator<uint8_t>> |
||
49 |
static bool equal(const IBasicReflectableConstPtr<ALLOC>& lhs, |
||
50 |
const IBasicReflectableConstPtr<ALLOC>& rhs); |
||
51 |
|||
52 |
/** |
||
53 |
* Gets native value from the given reflectable. |
||
54 |
* |
||
55 |
* Overload for types where the value is returned by value: |
||
56 |
* |
||
57 |
* - arithmetic types, enums, bitmasks and strings (via string view). |
||
58 |
* |
||
59 |
* \param reflectable Reflectable to use for value extraction. |
||
60 |
* |
||
61 |
* \return Value of the type T. |
||
62 |
*/ |
||
63 |
template <typename T, typename ALLOC = std::allocator<uint8_t>, |
||
64 |
typename std::enable_if<detail::gets_value_by_value<T>::value, int>::type = 0> |
||
65 |
32 |
static T getValue(const IBasicReflectableConstPtr<ALLOC>& reflectable, const ALLOC& allocator = ALLOC()) |
|
66 |
{ |
||
67 |
✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ |
32 |
return reflectable->getAnyValue(allocator).template get<T>(); |
68 |
} |
||
69 |
|||
70 |
/** |
||
71 |
* Gets constant reference to the native value from the given constant reflectable. |
||
72 |
* |
||
73 |
* Overload for types where the value is returned by const reference: |
||
74 |
* |
||
75 |
* - compound, bit buffers and arrays. |
||
76 |
* |
||
77 |
* \param reflectable Constant reflectable to use for value extraction. |
||
78 |
* |
||
79 |
* \return Constant reference to the value of the type T. |
||
80 |
* |
||
81 |
* \throw CppRuntimeException When wrong type is requested ("Bad type in AnyHolder"). |
||
82 |
*/ |
||
83 |
template <typename T, typename ALLOC = std::allocator<uint8_t>, |
||
84 |
typename std::enable_if<!detail::gets_value_by_value<T>::value, int>::type = 0> |
||
85 |
2 |
static const T& getValue(const IBasicReflectableConstPtr<ALLOC>& reflectable, |
|
86 |
const ALLOC& allocator = ALLOC()) |
||
87 |
{ |
||
88 |
✓✗✓✗ |
2 |
return reflectable->getAnyValue(allocator).template get<std::reference_wrapper<const T>>().get(); |
89 |
} |
||
90 |
|||
91 |
/** |
||
92 |
* Gets reference to the native value from the given reflectable. |
||
93 |
* |
||
94 |
* Overload for types where the value is returned by reference: |
||
95 |
* |
||
96 |
* - compound, arrays. |
||
97 |
* |
||
98 |
* \param reflectable Reflectable to use for value extraction. |
||
99 |
* |
||
100 |
* \return Reference to the value of the type T. |
||
101 |
* |
||
102 |
* \throw CppRuntimeException When wrong type is requested ("Bad type in AnyHolder"). |
||
103 |
*/ |
||
104 |
template <typename T, typename ALLOC = std::allocator<uint8_t>, |
||
105 |
typename std::enable_if< |
||
106 |
!detail::gets_value_by_value<T>::value && |
||
107 |
!std::is_same<BasicBitBuffer<ALLOC>, T>::value, int>::type = 0> |
||
108 |
20 |
static T& getValue(const IBasicReflectablePtr<ALLOC>& reflectable, const ALLOC& allocator = ALLOC()) |
|
109 |
{ |
||
110 |
✓✗✓✗ ✓✗✓✗ |
20 |
return reflectable->getAnyValue(allocator).template get<std::reference_wrapper<T>>().get(); |
111 |
} |
||
112 |
|||
113 |
/** |
||
114 |
* Gets constant reference to the native value from the given reflectable. |
||
115 |
* |
||
116 |
* Overload for bit buffers which are currently returned only by constant reference. |
||
117 |
* |
||
118 |
* \param reflectable Reflectable to use for value extraction. |
||
119 |
* |
||
120 |
* \return Constant reference to the bit buffer value. |
||
121 |
* |
||
122 |
* \throw CppRuntimeException When wrong type is requested ("Bad type in AnyHolder"). |
||
123 |
*/ |
||
124 |
template <typename T, typename ALLOC = std::allocator<uint8_t>, |
||
125 |
typename std::enable_if<std::is_same<BasicBitBuffer<ALLOC>, T>::value, int>::type = 0> |
||
126 |
1 |
static const T& getValue(const IBasicReflectablePtr<ALLOC>& reflectable, const ALLOC& allocator = ALLOC()) |
|
127 |
{ |
||
128 |
✓✗ | 1 |
return reflectable->getAnyValue(allocator).template get<std::reference_wrapper<const T>>().get(); |
129 |
} |
||
130 |
|||
131 |
private: |
||
132 |
template <typename ALLOC> |
||
133 |
static bool arraysEqual(const IBasicReflectableConstPtr<ALLOC>& lhsArray, |
||
134 |
const IBasicReflectableConstPtr<ALLOC>& rhsArray); |
||
135 |
|||
136 |
template <typename ALLOC> |
||
137 |
static bool compoundsEqual(const IBasicReflectableConstPtr<ALLOC>& lhsCompound, |
||
138 |
const IBasicReflectableConstPtr<ALLOC>& rhsCompound); |
||
139 |
|||
140 |
template <typename ALLOC> |
||
141 |
static bool valuesEqual(const IBasicReflectableConstPtr<ALLOC>& lhsValue, |
||
142 |
const IBasicReflectableConstPtr<ALLOC>& rhsValue); |
||
143 |
|||
144 |
static bool doubleValuesAlmostEqual(double lhs, double rhs); |
||
145 |
}; |
||
146 |
|||
147 |
template <typename ALLOC> |
||
148 |
145 |
bool ReflectableUtil::equal(const IBasicReflectableConstPtr<ALLOC>& lhs, |
|
149 |
const IBasicReflectableConstPtr<ALLOC>& rhs) |
||
150 |
{ |
||
151 |
✓✓✓✓ ✓✓ |
145 |
if (lhs == nullptr || rhs == nullptr) |
152 |
3 |
return lhs == rhs; |
|
153 |
|||
154 |
142 |
const auto& lhsTypeInfo = lhs->getTypeInfo(); |
|
155 |
142 |
const auto& rhsTypeInfo = rhs->getTypeInfo(); |
|
156 |
|||
157 |
✓✓✓✓ ✓✓ |
276 |
if (lhsTypeInfo.getSchemaType() != rhsTypeInfo.getSchemaType() || |
158 |
134 |
lhsTypeInfo.getSchemaName() != rhsTypeInfo.getSchemaName()) |
|
159 |
9 |
return false; |
|
160 |
|||
161 |
✓✓✓✓ ✓✓ |
133 |
if (lhs->isArray() || rhs->isArray()) |
162 |
{ |
||
163 |
✓✓✓✓ ✓✓ |
10 |
if (!lhs->isArray() || !rhs->isArray()) |
164 |
2 |
return false; |
|
165 |
8 |
return arraysEqual<ALLOC>(lhs, rhs); |
|
166 |
} |
||
167 |
✓✓ | 123 |
else if (TypeInfoUtil::isCompound(lhsTypeInfo.getSchemaType())) |
168 |
{ |
||
169 |
21 |
return compoundsEqual<ALLOC>(lhs, rhs); |
|
170 |
} |
||
171 |
else |
||
172 |
{ |
||
173 |
102 |
return valuesEqual<ALLOC>(lhs, rhs); |
|
174 |
} |
||
175 |
} |
||
176 |
|||
177 |
template <typename ALLOC> |
||
178 |
8 |
bool ReflectableUtil::arraysEqual(const IBasicReflectableConstPtr<ALLOC>& lhsArray, |
|
179 |
const IBasicReflectableConstPtr<ALLOC>& rhsArray) |
||
180 |
{ |
||
181 |
✓✓ | 8 |
if (lhsArray->size() != rhsArray->size()) |
182 |
2 |
return false; |
|
183 |
|||
184 |
✓✓ | 19 |
for (size_t i = 0; i < lhsArray->size(); ++i) |
185 |
{ |
||
186 |
✓✗✓✗ ✓✓ |
14 |
if (!equal<ALLOC>(lhsArray->at(i), rhsArray->at(i))) |
187 |
1 |
return false; |
|
188 |
} |
||
189 |
|||
190 |
5 |
return true; |
|
191 |
} |
||
192 |
|||
193 |
template <typename ALLOC> |
||
194 |
21 |
bool ReflectableUtil::compoundsEqual(const IBasicReflectableConstPtr<ALLOC>& lhsCompound, |
|
195 |
const IBasicReflectableConstPtr<ALLOC>& rhsCompound) |
||
196 |
{ |
||
197 |
✓✓ | 25 |
for (const auto& parameterInfo : lhsCompound->getTypeInfo().getParameters()) |
198 |
{ |
||
199 |
✓✗ | 9 |
auto lhsParameter = lhsCompound->getParameter(parameterInfo.schemaName); |
200 |
✓✗✓✓ |
9 |
auto rhsParameter = rhsCompound->getParameter(parameterInfo.schemaName); |
201 |
✓✗✓✓ |
5 |
if (!equal<ALLOC>(lhsParameter, rhsParameter)) |
202 |
✓✓ | 1 |
return false; |
203 |
} |
||
204 |
|||
205 |
✓✓ | 20 |
if (TypeInfoUtil::hasChoice(lhsCompound->getTypeInfo().getSchemaType())) |
206 |
{ |
||
207 |
✓✓ | 16 |
if (lhsCompound->getChoice() != rhsCompound->getChoice()) |
208 |
4 |
return false; |
|
209 |
|||
210 |
✓✓ | 12 |
if (!lhsCompound->getChoice().empty()) |
211 |
{ |
||
212 |
✓✗✓✗ |
19 |
auto lhsField = lhsCompound->getField(lhsCompound->getChoice()); |
213 |
✓✗✓✗ ✓✓ |
19 |
auto rhsField = rhsCompound->getField(rhsCompound->getChoice()); |
214 |
✓✗✓✓ |
11 |
if (!equal<ALLOC>(lhsField, rhsField)) |
215 |
✓✓ | 3 |
return false; |
216 |
} |
||
217 |
} |
||
218 |
else |
||
219 |
{ |
||
220 |
✓✓ | 11 |
for (const auto& fieldInfo : lhsCompound->getTypeInfo().getFields()) |
221 |
{ |
||
222 |
✓✗ | 15 |
auto lhsField = lhsCompound->getField(fieldInfo.schemaName); |
223 |
✓✗✓✓ |
15 |
auto rhsField = rhsCompound->getField(fieldInfo.schemaName); |
224 |
✓✗✓✓ |
8 |
if (!equal<ALLOC>(lhsField, rhsField)) |
225 |
✓✓ | 1 |
return false; |
226 |
} |
||
227 |
} |
||
228 |
|||
229 |
12 |
return true; |
|
230 |
} |
||
231 |
|||
232 |
template <typename ALLOC> |
||
233 |
102 |
bool ReflectableUtil::valuesEqual(const IBasicReflectableConstPtr<ALLOC>& lhsValue, |
|
234 |
const IBasicReflectableConstPtr<ALLOC>& rhsValue) |
||
235 |
{ |
||
236 |
102 |
CppType cppType = lhsValue->getTypeInfo().getCppType(); |
|
237 |
✓✓✓✓ |
102 |
if (cppType == CppType::ENUM || cppType == CppType::BITMASK) |
238 |
10 |
cppType = lhsValue->getTypeInfo().getUnderlyingType().getCppType(); |
|
239 |
|||
240 |
✓✓✓✓ ✓✓✓✓ |
102 |
switch (cppType) |
241 |
{ |
||
242 |
case CppType::BOOL: |
||
243 |
3 |
return lhsValue->getBool() == rhsValue->getBool(); |
|
244 |
case CppType::INT8: |
||
245 |
case CppType::INT16: |
||
246 |
case CppType::INT32: |
||
247 |
case CppType::INT64: |
||
248 |
22 |
return lhsValue->toInt() == rhsValue->toInt(); |
|
249 |
case CppType::UINT8: |
||
250 |
case CppType::UINT16: |
||
251 |
case CppType::UINT32: |
||
252 |
case CppType::UINT64: |
||
253 |
43 |
return lhsValue->toUInt() == rhsValue->toUInt(); |
|
254 |
case CppType::FLOAT: |
||
255 |
case CppType::DOUBLE: |
||
256 |
21 |
return doubleValuesAlmostEqual(lhsValue->toDouble(), rhsValue->toDouble()); |
|
257 |
case CppType::BYTES: |
||
258 |
{ |
||
259 |
✓✗ | 4 |
Span<const uint8_t> lhs = lhsValue->getBytes(); |
260 |
✓✗ | 4 |
Span<const uint8_t> rhs = rhsValue->getBytes(); |
261 |
|||
262 |
✓✓✓✗ ✓✓ |
4 |
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); |
263 |
} |
||
264 |
case CppType::STRING: |
||
265 |
5 |
return lhsValue->getStringView() == rhsValue->getStringView(); |
|
266 |
case CppType::BIT_BUFFER: |
||
267 |
3 |
return lhsValue->getBitBuffer() == rhsValue->getBitBuffer(); |
|
268 |
default: |
||
269 |
✓✗ | 1 |
throw CppRuntimeException("ReflectableUtil::valuesEqual - Unexpected C++ type!"); |
270 |
} |
||
271 |
} |
||
272 |
|||
273 |
21 |
inline bool ReflectableUtil::doubleValuesAlmostEqual(double lhs, double rhs) |
|
274 |
{ |
||
275 |
✓✓✓✓ ✓✓ |
21 |
if (std::isinf(lhs) || std::isinf(rhs)) |
276 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ |
8 |
return std::isinf(lhs) && std::isinf(rhs) && ((lhs > 0.0 && rhs > 0.0) || (lhs < 0.0 && rhs < 0.0)); |
277 |
|||
278 |
✓✓✓✓ ✓✓ |
13 |
if (std::isnan(lhs) || std::isnan(rhs)) |
279 |
✓✓✓✓ |
3 |
return std::isnan(lhs) && std::isnan(rhs); |
280 |
|||
281 |
// see: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon |
||
282 |
10 |
return std::fabs(lhs - rhs) <= std::numeric_limits<double>::epsilon() * std::fabs(lhs + rhs) |
|
283 |
✓✓✓✓ |
10 |
|| std::fabs(lhs - rhs) < std::numeric_limits<double>::min(); |
284 |
} |
||
285 |
|||
286 |
} // namespace zserio |
||
287 |
|||
288 |
#endif // ZSERIO_REFLECTABLE_UTIL_H_INC |
Generated by: GCOVR (Version 4.2) |