CppWAMP
C++11 client library for the WAMP protocol
variantvisitors.hpp
1 /*------------------------------------------------------------------------------
2  Copyright Butterfly Energy Systems 2014-2015, 2022.
3  Distributed under the Boost Software License, Version 1.0.
4  http://www.boost.org/LICENSE_1_0.txt
5 ------------------------------------------------------------------------------*/
6 
7 #ifndef CPPWAMP_INTERNAL_VARIANT_VISITORS_HPP
8 #define CPPWAMP_INTERNAL_VARIANT_VISITORS_HPP
9 
10 #include "../blob.hpp"
11 #include "../error.hpp"
12 #include "../null.hpp"
13 #include "../traits.hpp"
14 #include "../variantdefs.hpp"
15 #include "../visitor.hpp"
16 #include "varianttraits.hpp"
17 
18 namespace wamp
19 {
20 
21 namespace internal
22 {
23 
24 //------------------------------------------------------------------------------
25 // The !isSameType is required because std::vector<bool>::const_reference may
26 // or may not just be bool.
27 template <typename T>
28 using EnableIfBoolRef = EnableIf<isBool<T>() && !isSameType<T, bool>()>;
29 
30 template <typename T, typename U>
31 constexpr bool bothAreNumbers() {return isNumber<T>() && isNumber<U>();}
32 
33 template <typename T, typename U>
34 using EnableIfBothAreNumbers = EnableIf<bothAreNumbers<T, U>()>;
35 
36 template <typename T, typename U>
37 using DisableIfBothAreNumbers = DisableIf<bothAreNumbers<T, U>()>;
38 
39 template <typename T>
40 using DisableIfVariant = DisableIf<isSameType<T, Variant>()>;
41 
42 template <typename TFrom, typename TTo>
43 using EnableIfConvertible = EnableIf<std::is_convertible<TFrom,TTo>::value>;
44 
45 template <typename TFrom, typename TTo>
46 using DisableIfConvertible = DisableIf<std::is_convertible<TFrom,TTo>::value>;
47 
48 
49 //------------------------------------------------------------------------------
50 template <typename TVariant>
51 class VariantEquivalentTo : public Visitor<bool>
52 {
53 public:
54  using ArrayType = typename TVariant::Array;
55  using ObjectType = typename TVariant::Object;
56 
57  template <typename TField>
58  bool operator()(const TField& lhs, const TField& rhs) const
59  {
60  return lhs == rhs;
61  }
62 
63  template <typename TArg, EnableIfBoolRef<TArg> = 0>
64  bool operator()(const bool lhs, const TArg rhs) const
65  {
66  return lhs == bool(rhs);
67  }
68 
69  template <typename TField, typename TArg,
70  DisableIfBothAreNumbers<TField,TArg> = 0>
71  bool operator()(const TField&, const TArg&) const {return false;}
72 
73  template <typename TField, typename TArg,
74  EnableIfBothAreNumbers<TField,TArg> = 0>
75  bool operator()(const TField lhs, const TArg rhs) const
76  {
77  // Avoid directly comparing mixed signed/unsigned numbers
78  using LhsIsSigned = typename std::is_signed<TField>;
79  using RhsIsSigned = typename std::is_signed<TArg>;
80  return compareNumbers(LhsIsSigned(), RhsIsSigned(), lhs, rhs);
81  }
82 
83  template <typename TElem, DisableIfVariant<TElem> = 0>
84  bool operator()(const ArrayType& lhs, const std::vector<TElem>& rhs) const
85  {
86  using VecConstRef = typename std::vector<TElem>::const_reference;
87  // clang libc++ parameterizes equality comparison for default binary
88  // predicate on iterator value type instead of const reference type,
89  // which leads to an ambiguous function resolution.
90  return (lhs.size() != rhs.size()) ? false :
91  std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
92  [](const Variant& lElem, VecConstRef rElem)
93  {return lElem == rElem;});
94  }
95 
96  template <typename TValue, DisableIfVariant<TValue> = 0>
97  bool operator()(const ObjectType& lhs,
98  const std::map<String, TValue>& rhs) const
99  {
100  using Map = std::map<String, TValue>;
101  using MapPair = typename Map::value_type;
102  using ObjectPair = typename ObjectType::value_type;
103  return (lhs.size() != rhs.size()) ? false :
104  std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
105  [](const ObjectPair& lPair, const MapPair& rPair)
106  {
107  return lPair.first == rPair.first &&
108  lPair.second == rPair.second;
109  });
110  }
111 
112 private:
113  template <typename TField, typename TArg>
114  static bool compareNumbers(FalseType, FalseType,
115  const TField lhs, const TArg rhs)
116  {
117  return lhs == rhs;
118  }
119 
120  template <typename TField, typename TArg>
121  static bool compareNumbers(FalseType, TrueType,
122  const TField lhs, const TArg rhs)
123  {
124  if (rhs < 0)
125  return false;
126  using CT = typename std::common_type<TField, TArg>::type;
127  return static_cast<CT>(lhs) == static_cast<CT>(rhs);
128  }
129 
130  template <typename TField, typename TArg>
131  static bool compareNumbers(TrueType, FalseType,
132  const TField lhs, const TArg rhs)
133  {
134  if (lhs < 0)
135  return false;
136  using CT = typename std::common_type<TField, TArg>::type;
137  return static_cast<CT>(lhs) == static_cast<CT>(rhs);
138  }
139 
140  template <typename TField, typename TArg>
141  static bool compareNumbers(TrueType, TrueType,
142  const TField lhs, const TArg rhs)
143  {
144  return lhs == rhs;
145  }
146 };
147 
148 //------------------------------------------------------------------------------
149 template <typename TVariant>
150 class VariantNotEquivalentTo : public Visitor<bool>
151 {
152 public:
153  using ArrayType = typename TVariant::Array;
154  using ObjectType = typename TVariant::Object;
155 
156  template <typename TField>
157  bool operator()(const TField& lhs, const TField& rhs) const
158  {return lhs != rhs;}
159 
160  template <typename TArg, EnableIfBoolRef<TArg> = 0>
161  bool operator()(const bool lhs, const TArg rhs) const
162  {return lhs != rhs;}
163 
164  template <typename TField, typename TArg,
165  DisableIfBothAreNumbers<TField,TArg> = 0>
166  bool operator()(const TField&, const TArg&) const {return true;}
167 
168  template <typename TField, typename TArg,
169  EnableIfBothAreNumbers<TField,TArg> = 0>
170  bool operator()(TField lhs, TArg rhs) const
171  {
172  // Avoid directly comparing mixed signed/unsigned numbers
173  using LhsIsSigned = typename std::is_signed<TField>;
174  using RhsIsSigned = typename std::is_signed<TArg>;
175  return compareNumbers(LhsIsSigned(), RhsIsSigned(), lhs, rhs);
176  }
177 
178  template <typename TElem, DisableIfVariant<TElem> = 0>
179  bool operator()(const ArrayType& lhs, const std::vector<TElem>& rhs) const
180  {
181  using VecConstRef = typename std::vector<TElem>::const_reference;
182  // clang libc++ parameterizes equality comparison for default binary
183  // predicate on iterator value type instead of const reference type,
184  // which leads to an ambiguous function resolution.
185  return (lhs.size() != rhs.size()) ? true :
186  std::mismatch(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
187  [](const Variant& lElem, VecConstRef rElem)
188  {return lElem == rElem;}).first != lhs.cend();
189  }
190 
191  template <typename TValue, DisableIfVariant<TValue> = 0>
192  bool operator()(const ObjectType& lhs,
193  const std::map<String, TValue>& rhs) const
194  {
195  using Map = std::map<String, TValue>;
196  using MapPair = typename Map::value_type;
197  using ObjectPair = typename ObjectType::value_type;
198 
199  auto comp = [](const ObjectPair& lPair, const MapPair& rPair)
200  {
201  return lPair.first == rPair.first &&
202  lPair.second == rPair.second;
203  };
204 
205  return (lhs.size() != rhs.size()) ? true :
206  ( std::mismatch(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
207  std::move(comp)).first != lhs.end() );
208  }
209 
210 private:
211  template <typename TField, typename TArg>
212  static bool compareNumbers(FalseType, FalseType,
213  const TField lhs, const TArg rhs)
214  {
215  return lhs != rhs;
216  }
217 
218  template <typename TField, typename TArg>
219  static bool compareNumbers(FalseType, TrueType,
220  const TField lhs, const TArg rhs)
221  {
222  if (rhs < 0)
223  return true;
224  using CT = typename std::common_type<TField, TArg>::type;
225  return static_cast<CT>(lhs) != static_cast<CT>(rhs);
226  }
227 
228  template <typename TField, typename TArg>
229  static bool compareNumbers(TrueType, FalseType,
230  const TField lhs, const TArg rhs)
231  {
232  if (lhs < 0)
233  return true;
234  using CT = typename std::common_type<TField, TArg>::type;
235  return static_cast<CT>(lhs) != static_cast<CT>(rhs);
236  }
237 
238  template <typename TField, typename TArg>
239  static bool compareNumbers(TrueType, TrueType,
240  const TField lhs, const TArg rhs)
241  {
242  return lhs != rhs;
243  }
244 };
245 
246 
247 //------------------------------------------------------------------------------
248 template <typename TVariant>
249 class VariantIsConvertibleTo : public Visitor<bool>
250 {
251 public:
252  using ArrayType = typename TVariant::Array;
253  using ObjectType = typename TVariant::Array;
254 
255  template <typename T> struct Tag {};
256 
257  // Conversions to same type
258  template <typename TField>
259  bool operator()(const TField&, Tag<TField>) const {return true;}
260 
261  // Implicit conversions
262  template <typename TField, typename TResult,
263  internal::EnableIfConvertible<TField,TResult> = 0>
264  bool operator()(const TField&, Tag<TResult>) const {return true;}
265 
266  // Invalid conversions
267  template <typename TField, typename TResult,
268  internal::DisableIfConvertible<TField,TResult> = 0>
269  bool operator()(const TField&, Tag<TResult>) const {return false;}
270 
271  // Vector conversions
272  template <typename TElem, internal::DisableIfVariant<TElem> = 0>
273  bool operator()(const ArrayType& from, Tag<std::vector<TElem>>) const
274  {
275  if (from.empty())
276  return true;
277  Tag<TElem> toElem;
278  for (const auto& fromElem: from)
279  {
280  if (!applyWithOperand(*this, fromElem, toElem))
281  return false;
282  }
283  return true;
284  }
285 
286  // Map conversions
287  template <typename TValue, internal::DisableIfVariant<TValue> = 0>
288  bool operator()(const ObjectType& from, Tag<std::map<String, TValue>>) const
289  {
290  if (from.empty())
291  return true;
292  Tag<TValue> toValue;
293  for (const auto& fromKv: from)
294  {
295  if (!applyWithOperand(*this, fromKv.second, toValue))
296  return false;
297  }
298  return true;
299  }
300 };
301 
302 //------------------------------------------------------------------------------
303 template <typename TVariant>
304 class VariantConvertTo : public Visitor<>
305 {
306 public:
307  using ArrayType = typename TVariant::Array;
308  using ObjectType = typename TVariant::Object;
309 
310  // Conversions to same type
311  template <typename TField>
312  void operator()(const TField& from, TField& to) const {to = from;}
313 
314  // Implicit conversions
315  template <typename TField, typename TResult,
316  internal::EnableIfConvertible<TField,TResult> = 0>
317  void operator()(const TField& from, TResult& to) const
318  {
319  to = static_cast<TResult>(from);
320  }
321 
322  // Invalid conversions
323  template <typename TField, typename TResult,
324  internal::DisableIfConvertible<TField,TResult> = 0>
325  void operator()(const TField&, TResult&) const
326  {
327  throw error::Conversion(
328  "wamp::error::Conversion: Invalid conversion "
329  "from " + FieldTraits<TField>::typeName() +
330  " to " + ArgTraits<TResult>::typeName());
331  }
332 
333  // Vector conversions
334  template <typename TElem, internal::DisableIfVariant<TElem> = 0>
335  void operator()(const ArrayType& from, std::vector<TElem>& to) const
336  {
337  TElem toElem;
338  for (size_t i=0; i<from.size(); ++i)
339  {
340  try
341  {
342  applyWithOperand(*this, from.at(i), toElem);
343  }
344  catch (const error::Conversion& e)
345  {
346  std::ostringstream oss;
347  oss << e.what() << " (for Array element #" << i << ')';
348  throw error::Conversion(oss.str());
349  }
350  to.push_back(std::move(toElem));
351  }
352  }
353 
354  // Map conversions
355  template <typename TValue, internal::DisableIfVariant<TValue> = 0>
356  void operator()(const ObjectType& from, std::map<String, TValue>& to) const
357  {
358  TValue toValue;
359  for (const auto& fromKv: from)
360  {
361  try
362  {
363  applyWithOperand(*this, fromKv.second, toValue);
364  }
365  catch (const error::Conversion& e)
366  {
367  std::ostringstream oss;
368  oss << e.what() << " (for Object member \""
369  << fromKv.first << "\")";
370  throw error::Conversion(oss.str());
371  }
372  to.emplace(fromKv.first, std::move(toValue));
373  }
374  }
375 };
376 
377 //------------------------------------------------------------------------------
378 class VariantTypeName : public Visitor<String>
379 {
380 public:
381  template <typename TField> String operator()(const TField&) const
382  {
383  return FieldTraits<TField>::typeName();
384  }
385 };
386 
387 } // namespace internal
388 
389 } // namespace wamp
390 
391 #endif // CPPWAMP_INTERNAL_VARIANT_VISITORS_HPP
wamp::FalseType
BoolConstant< false > FalseType
Equivalent to std::false_type provided in C++17.
Definition: traits.hpp:122
wamp::TrueType
BoolConstant< true > TrueType
Equivalent to std::true_type provided in C++17.
Definition: traits.hpp:117
wamp::Object
std::map< String, Variant > Object
Variant bound type for maps of variants.
Definition: variantdefs.hpp:52
wamp
Definition: anyhandler.hpp:36
wamp::Array
std::vector< Variant > Array
Variant bound type for arrays of variants.
Definition: variantdefs.hpp:51
wamp::String
std::string String
Variant bound type for text strings.
Definition: variantdefs.hpp:50