Zserio C++17 runtime library  0.5.0
Built for Zserio 2.17.0
ReflectableUtil.h
Go to the documentation of this file.
1 #ifndef ZSERIO_REFLECTABLE_UTIL_H_INC
2 #define ZSERIO_REFLECTABLE_UTIL_H_INC
3 
4 #include <algorithm>
5 #include <cmath>
6 #include <functional>
7 #include <limits>
8 #include <string_view>
9 
11 #include "zserio/Enums.h"
13 #include "zserio/ITypeInfo.h"
14 #include "zserio/Traits.h"
15 #include "zserio/TypeInfoUtil.h"
16 
17 namespace zserio
18 {
19 
20 namespace detail
21 {
22 
23 template <typename T>
24 struct gets_value_by_value
25  : std::integral_constant<bool,
26  std::is_arithmetic_v<T> || std::is_same_v<std::string_view, T> || std::is_enum_v<T> ||
27  is_bitmask_v<T> || is_numeric_wrapper_v<T>>
28 {};
29 
30 } // namespace detail
31 
36 {
37 public:
48  template <typename ALLOC = std::allocator<uint8_t>>
49  static bool equal(
51 
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  static T getValue(const IBasicReflectableDataConstPtr<ALLOC>& reflectable, const ALLOC& allocator = ALLOC())
66  {
67  return reflectable->getAnyValue(allocator).template get<T>();
68  }
69 
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  static const T& getValue(
86  const IBasicReflectableDataConstPtr<ALLOC>& reflectable, const ALLOC& allocator = ALLOC())
87  {
88  return reflectable->getAnyValue(allocator).template get<std::reference_wrapper<const T>>().get();
89  }
90 
104  template <typename T, typename ALLOC = std::allocator<uint8_t>,
105  typename std::enable_if<!detail::gets_value_by_value<T>::value &&
106  !std::is_same<BasicBitBuffer<ALLOC>, T>::value,
107  int>::type = 0>
108  static T& getValue(const IBasicReflectableDataPtr<ALLOC>& reflectable, const ALLOC& allocator = ALLOC())
109  {
110  return reflectable->getAnyValue(allocator).template get<std::reference_wrapper<T>>().get();
111  }
112 
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  static const T& getValue(
127  const IBasicReflectableDataPtr<ALLOC>& reflectable, const ALLOC& allocator = ALLOC())
128  {
129  return reflectable->getAnyValue(allocator).template get<std::reference_wrapper<const T>>().get();
130  }
131 
141  template <typename T, typename ALLOC>
142  static T fromAny(const BasicAny<ALLOC>& any);
143 
144 private:
145  template <typename ALLOC>
146  static bool arraysEqual(const IBasicReflectableDataConstPtr<ALLOC>& lhsArray,
147  const IBasicReflectableDataConstPtr<ALLOC>& rhsArray);
148 
149  template <typename ALLOC>
150  static bool compoundsEqual(const IBasicReflectableDataConstPtr<ALLOC>& lhsCompound,
151  const IBasicReflectableDataConstPtr<ALLOC>& rhsCompound);
152 
153  template <typename ALLOC>
154  static bool valuesEqual(const IBasicReflectableDataConstPtr<ALLOC>& lhsValue,
155  const IBasicReflectableDataConstPtr<ALLOC>& rhsValue);
156 
157  static bool doubleValuesAlmostEqual(double lhs, double rhs);
158 };
159 
160 template <typename ALLOC>
163 {
164  if (lhs == nullptr || rhs == nullptr)
165  {
166  return lhs == rhs;
167  }
168 
169  const auto& lhsTypeInfo = lhs->getTypeInfo();
170  const auto& rhsTypeInfo = rhs->getTypeInfo();
171 
172  if (lhsTypeInfo.getSchemaType() != rhsTypeInfo.getSchemaType() ||
173  lhsTypeInfo.getSchemaName() != rhsTypeInfo.getSchemaName())
174  {
175  return false;
176  }
177 
178  if (lhs->isArray() || rhs->isArray())
179  {
180  if (!lhs->isArray() || !rhs->isArray())
181  {
182  return false;
183  }
184  return arraysEqual<ALLOC>(lhs, rhs);
185  }
186  else if (TypeInfoUtil::isCompound(lhsTypeInfo.getSchemaType()))
187  {
188  return compoundsEqual<ALLOC>(lhs, rhs);
189  }
190  else
191  {
192  return valuesEqual<ALLOC>(lhs, rhs);
193  }
194 }
195 
196 template <typename ALLOC>
197 bool ReflectableUtil::arraysEqual(const IBasicReflectableDataConstPtr<ALLOC>& lhsArray,
198  const IBasicReflectableDataConstPtr<ALLOC>& rhsArray)
199 {
200  if (lhsArray->size() != rhsArray->size())
201  {
202  return false;
203  }
204 
205  for (size_t i = 0; i < lhsArray->size(); ++i)
206  {
207  if (!equal<ALLOC>(lhsArray->at(i), rhsArray->at(i)))
208  {
209  return false;
210  }
211  }
212 
213  return true;
214 }
215 
216 template <typename ALLOC>
217 bool ReflectableUtil::compoundsEqual(const IBasicReflectableDataConstPtr<ALLOC>& lhsCompound,
218  const IBasicReflectableDataConstPtr<ALLOC>& rhsCompound)
219 {
220  if (TypeInfoUtil::hasChoice(lhsCompound->getTypeInfo().getSchemaType()))
221  {
222  if (lhsCompound->getChoice() != rhsCompound->getChoice())
223  {
224  return false;
225  }
226 
227  if (!lhsCompound->getChoice().empty())
228  {
229  auto lhsField = lhsCompound->getField(lhsCompound->getChoice());
230  auto rhsField = rhsCompound->getField(rhsCompound->getChoice());
231  if (!equal<ALLOC>(lhsField, rhsField))
232  {
233  return false;
234  }
235  }
236  }
237  else
238  {
239  for (const auto& fieldInfo : lhsCompound->getTypeInfo().getFields())
240  {
241  auto lhsField = lhsCompound->getField(fieldInfo.schemaName);
242  auto rhsField = rhsCompound->getField(fieldInfo.schemaName);
243  if (!equal<ALLOC>(lhsField, rhsField))
244  {
245  return false;
246  }
247  }
248  }
249 
250  return true;
251 }
252 
253 template <typename ALLOC>
254 bool ReflectableUtil::valuesEqual(const IBasicReflectableDataConstPtr<ALLOC>& lhsValue,
255  const IBasicReflectableDataConstPtr<ALLOC>& rhsValue)
256 {
257  CppType cppType = lhsValue->getTypeInfo().getCppType();
258  if (cppType == CppType::ENUM || cppType == CppType::BITMASK)
259  {
260  cppType = lhsValue->getTypeInfo().getUnderlyingType().getCppType();
261  }
262 
263  switch (cppType)
264  {
265  case CppType::BOOL:
266  return lhsValue->getBool() == rhsValue->getBool();
267  case CppType::INT8:
268  case CppType::INT16:
269  case CppType::INT32:
270  case CppType::INT64:
271  return lhsValue->toInt() == rhsValue->toInt();
272  case CppType::UINT8:
273  case CppType::UINT16:
274  case CppType::UINT32:
275  case CppType::UINT64:
276  return lhsValue->toUInt() == rhsValue->toUInt();
277  case CppType::FLOAT:
278  case CppType::DOUBLE:
279  return doubleValuesAlmostEqual(lhsValue->toDouble(), rhsValue->toDouble());
280  case CppType::BYTES:
281  {
282  Span<const uint8_t> lhs = lhsValue->getBytes();
283  Span<const uint8_t> rhs = rhsValue->getBytes();
284 
285  return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
286  }
287  case CppType::STRING:
288  return lhsValue->getStringView() == rhsValue->getStringView();
289  case CppType::BIT_BUFFER:
290  return lhsValue->getBitBuffer() == rhsValue->getBitBuffer();
291  default:
292  throw CppRuntimeException("ReflectableUtil::valuesEqual - Unexpected C++ type!");
293  }
294 }
295 
296 inline bool ReflectableUtil::doubleValuesAlmostEqual(double lhs, double rhs)
297 {
298  if (std::isinf(lhs) || std::isinf(rhs))
299  {
300  return std::isinf(lhs) && std::isinf(rhs) && ((lhs > 0.0 && rhs > 0.0) || (lhs < 0.0 && rhs < 0.0));
301  }
302 
303  if (std::isnan(lhs) || std::isnan(rhs))
304  {
305  return std::isnan(lhs) && std::isnan(rhs);
306  }
307 
308  // see: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
309  return std::fabs(lhs - rhs) <= std::numeric_limits<double>::epsilon() * std::fabs(lhs + rhs) ||
310  std::fabs(lhs - rhs) < std::numeric_limits<double>::min();
311 }
312 
313 template <typename T, typename ALLOC>
315 {
316  if constexpr (is_bitmask_v<T>)
317  {
318  using ZserioType = typename T::ZserioType;
319  using UnderlyingType = typename ZserioType::ValueType;
320 
321  if (any.template isType<T>())
322  {
323  return any.template get<T>();
324  }
325  else if (any.template isType<ZserioType>())
326  {
327  return T(any.template get<ZserioType>());
328  }
329  else
330  {
331  return T(any.template get<UnderlyingType>());
332  }
333  }
334  else if constexpr (std::is_enum_v<T>)
335  {
336  using ZserioType = typename EnumTraits<T>::ZserioType;
337  using UnderlyingType = std::underlying_type_t<T>;
338 
339  if (any.template isType<T>())
340  {
341  return any.template get<T>();
342  }
343  else if (any.template isType<ZserioType>())
344  {
345  return valueToEnum<T>(any.template get<ZserioType>());
346  }
347  else
348  {
349  return valueToEnum<T>(any.template get<UnderlyingType>());
350  }
351  }
352  else if constexpr (is_numeric_wrapper_v<T>)
353  {
354  if (any.template isType<T>())
355  {
356  return any.template get<T>();
357  }
358  else
359  {
360  return any.template get<typename T::ValueType>();
361  }
362  }
363  else
364  {
365  return any.template get<T>();
366  }
367 }
368 
369 } // namespace zserio
370 
371 #endif // ZSERIO_REFLECTABLE_UTIL_H_INC
static T getValue(const IBasicReflectableDataConstPtr< ALLOC > &reflectable, const ALLOC &allocator=ALLOC())
static const T & getValue(const IBasicReflectableDataPtr< ALLOC > &reflectable, const ALLOC &allocator=ALLOC())
static bool equal(const IBasicReflectableDataConstPtr< ALLOC > &lhs, const IBasicReflectableDataConstPtr< ALLOC > &rhs)
static T fromAny(const BasicAny< ALLOC > &any)
static const T & getValue(const IBasicReflectableDataConstPtr< ALLOC > &reflectable, const ALLOC &allocator=ALLOC())
static T & getValue(const IBasicReflectableDataPtr< ALLOC > &reflectable, const ALLOC &allocator=ALLOC())
typename IBasicReflectableData< ALLOC >::ConstPtr IBasicReflectableDataConstPtr
typename IBasicReflectableData< ALLOC >::Ptr IBasicReflectableDataPtr
IBasicReflectableDataConstPtr< ALLOC > reflectable(const T &value, const ALLOC &allocator=ALLOC())
decltype(auto) get(BasicVariant< ALLOC, INDEX, T... > &var)
Definition: Variant.h:812
static bool hasChoice(SchemaType schemaType)
static bool isCompound(SchemaType schemaType)
Definition: TypeInfoUtil.cpp:6