variant.hpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright 2016-2017 Davide Faconti
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage, Inc. nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  * *******************************************************************/
34 
35 #ifndef VARIANT_H
36 #define VARIANT_H
37 
38 #include <string.h>
39 #include <type_traits>
40 #include <limits>
41 #include <string_view>
45 
46 namespace RosMsgParser
47 {
48 
49 class Variant
50 {
51 public:
52  Variant()
53  {
54  _type = OTHER;
55  _storage.raw_string = nullptr;
56  }
57 
58  ~Variant();
59 
60  Variant(const Variant& other) : _type(OTHER)
61  {
62  if (other._type == STRING)
63  {
64  const char* raw = other._storage.raw_string;
65  const uint32_t size = *(reinterpret_cast<const uint32_t*>(&raw[0]));
66  const char* data = (&raw[4]);
67  assign(data, size);
68  }
69  else
70  {
71  _type = other._type;
72  _storage.raw_data = other._storage.raw_data;
73  }
74  }
75 
76  Variant(Variant&& other) : _type(OTHER)
77  {
78  if (other._type == STRING)
79  {
80  _storage.raw_string = nullptr;
81  std::swap(_storage.raw_string, other._storage.raw_string);
82  std::swap(_type, other._type);
83  }
84  else
85  {
86  _type = other._type;
87  _storage.raw_data = other._storage.raw_data;
88  }
89  }
90 
91  Variant& operator=(const Variant& other)
92  {
93  if (other._type == STRING)
94  {
95  const char* raw = other._storage.raw_string;
96  const uint32_t size = *(reinterpret_cast<const uint32_t*>(&raw[0]));
97  const char* data = (&raw[4]);
98  assign(data, size);
99  }
100  else
101  {
102  _type = other._type;
103  _storage.raw_data = other._storage.raw_data;
104  }
105  return *this;
106  }
107 
108  template <typename T>
109  Variant(const T& value);
110 
111  // specialization for raw string
112  Variant(const char* buffer, size_t length);
113 
114  BuiltinType getTypeID() const;
115 
116  template <typename T>
117  T convert() const;
118 
119  template <typename T>
120  T extract() const;
121 
122  template <typename T>
123  void assign(const T& value);
124 
125  void assign(const char* buffer, size_t length);
126 
127  // Direct access to raw data. Undefined behavior if this variant holds a STRING
128  const uint8_t* getRawStorage() const;
129 
130 private:
131  union
132  {
133  std::array<uint8_t, 8> raw_data;
134  char* raw_string;
135  } _storage;
136 
137  void clearStringIfNecessary();
138 
140 };
141 
142 //----------------------- Implementation ----------------------------------------------
143 
144 template <typename T>
145 inline Variant::Variant(const T& value) : _type(OTHER)
146 {
147  static_assert(std::numeric_limits<T>::is_specialized ||
148  std::is_same<T, RosMsgParser::Time>::value ||
149  std::is_same<T, std::string_view>::value ||
150  std::is_same<T, std::string>::value,
151  "not a valid type");
152 
153  _storage.raw_string = (nullptr);
154  assign(value);
155 }
156 
157 inline Variant::Variant(const char* buffer, size_t length) : _type(OTHER)
158 {
159  _storage.raw_string = (nullptr);
160  assign(buffer, length);
161 }
162 
163 inline Variant::~Variant()
164 {
166 }
167 
168 //-------------------------------------
169 
170 inline BuiltinType Variant::getTypeID() const
171 {
172  return _type;
173 }
174 
175 inline const uint8_t* Variant::getRawStorage() const
176 {
177  return _storage.raw_data.data();
178 }
179 
180 template <typename T>
181 inline T Variant::extract() const
182 {
183  static_assert(std::numeric_limits<T>::is_specialized ||
184  std::is_same<T, RosMsgParser::Time>::value,
185  "not a valid type");
186 
187  if (_type != RosMsgParser::getType<T>())
188  {
189  throw TypeException("Variant::extract -> wrong type");
190  }
191  return *reinterpret_cast<const T*>(&_storage.raw_data[0]);
192 }
193 
194 template <>
196 {
197  if (_type != STRING)
198  {
199  throw TypeException("Variant::extract -> wrong type");
200  }
201  const uint32_t size = *(reinterpret_cast<const uint32_t*>(&_storage.raw_string[0]));
202  char* data = static_cast<char*>(&_storage.raw_string[4]);
204 }
205 
206 template <>
207 inline std::string Variant::extract() const
208 {
209  if (_type != STRING)
210  {
211  throw TypeException("Variant::extract -> wrong type");
212  }
213  const uint32_t size = *(reinterpret_cast<const uint32_t*>(&_storage.raw_string[0]));
214  char* data = static_cast<char*>(&_storage.raw_string[4]);
215  return std::string(data, size);
216 }
217 
218 //-------------------------------------
219 
220 template <typename T>
221 inline void Variant::assign(const T& value)
222 {
223  static_assert(std::numeric_limits<T>::is_specialized ||
224  std::is_same<T, RosMsgParser::Time>::value,
225  "not a valid type");
226 
228  _type = RosMsgParser::getType<T>();
229  *reinterpret_cast<T*>(&_storage.raw_data[0]) = value;
230 }
231 
233 {
234  if (_storage.raw_string && _type == STRING)
235  {
236  delete[] _storage.raw_string;
237  _storage.raw_string = nullptr;
238  }
239 }
240 
241 inline void Variant::assign(const char* buffer, size_t size)
242 {
244  _type = STRING;
245 
246  _storage.raw_string = new char[size + 5];
247  *reinterpret_cast<uint32_t*>(&_storage.raw_string[0]) = size;
248  memcpy(&_storage.raw_string[4], buffer, size);
249  _storage.raw_string[size + 4] = '\0';
250 }
251 
252 template <>
253 inline void Variant::assign(const std::string_view& value)
254 {
255  assign(value.data(), value.size());
256 }
257 
258 template <>
259 inline void Variant::assign(const std::string& value)
260 {
261  assign(value.data(), value.size());
262 }
263 
264 //-------------------------------------
265 
266 template <typename DST>
267 inline DST Variant::convert() const
268 {
269  static_assert(std::numeric_limits<DST>::is_specialized ||
270  std::is_same<DST, RosMsgParser::Time>::value,
271  "not a valid type");
272 
273  using namespace RosMsgParser::details;
274  DST target;
275 
276  const auto& raw_data = &_storage.raw_data[0];
277  //----------
278  switch (_type)
279  {
280  case CHAR:
281  case INT8:
282  convert_impl<int8_t, DST>(*reinterpret_cast<const int8_t*>(raw_data), target);
283  break;
284 
285  case INT16:
286  convert_impl<int16_t, DST>(*reinterpret_cast<const int16_t*>(raw_data), target);
287  break;
288  case INT32:
289  convert_impl<int32_t, DST>(*reinterpret_cast<const int32_t*>(raw_data), target);
290  break;
291  case INT64:
292  convert_impl<int64_t, DST>(*reinterpret_cast<const int64_t*>(raw_data), target);
293  break;
294 
295  case BOOL:
296  case BYTE:
297  case UINT8:
298  convert_impl<uint8_t, DST>(*reinterpret_cast<const uint8_t*>(raw_data), target);
299  break;
300 
301  case UINT16:
302  convert_impl<uint16_t, DST>(*reinterpret_cast<const uint16_t*>(raw_data), target);
303  break;
304  case UINT32:
305  convert_impl<uint32_t, DST>(*reinterpret_cast<const uint32_t*>(raw_data), target);
306  break;
307  case UINT64:
308  convert_impl<uint64_t, DST>(*reinterpret_cast<const uint64_t*>(raw_data), target);
309  break;
310 
311  case FLOAT32:
312  convert_impl<float, DST>(*reinterpret_cast<const float*>(raw_data), target);
313  break;
314  case FLOAT64:
315  convert_impl<double, DST>(*reinterpret_cast<const double*>(raw_data), target);
316  break;
317 
318  case STRING: {
319  throw TypeException("String will not be converted to a numerical value implicitly");
320  }
321  break;
322 
323  case DURATION:
324  case TIME: {
325  throw TypeException("ros::Duration and ros::Time can be converted only to double "
326  "(will be seconds)");
327  }
328  break;
329 
330  default:
331  throw TypeException("Variant::convert -> cannot convert type" +
332  std::to_string(_type));
333  break;
334  }
335  return target;
336 }
337 
338 template <>
339 inline double Variant::convert() const
340 {
341  using namespace RosMsgParser::details;
342  double target = 0;
343  const auto& raw_data = &_storage.raw_data[0];
344  //----------
345  switch (_type)
346  {
347  case CHAR:
348  case INT8:
349  convert_impl<int8_t, double>(*reinterpret_cast<const int8_t*>(raw_data), target);
350  break;
351 
352  case INT16:
353  convert_impl<int16_t, double>(*reinterpret_cast<const int16_t*>(raw_data), target);
354  break;
355  case INT32:
356  convert_impl<int32_t, double>(*reinterpret_cast<const int32_t*>(raw_data), target);
357  break;
358  case INT64:
359  convert_impl<int64_t, double>(*reinterpret_cast<const int64_t*>(raw_data), target);
360  break;
361 
362  case BOOL:
363  case BYTE:
364  case UINT8:
365  convert_impl<uint8_t, double>(*reinterpret_cast<const uint8_t*>(raw_data), target);
366  break;
367 
368  case UINT16:
369  convert_impl<uint16_t, double>(*reinterpret_cast<const uint16_t*>(raw_data),
370  target);
371  break;
372  case UINT32:
373  convert_impl<uint32_t, double>(*reinterpret_cast<const uint32_t*>(raw_data),
374  target);
375  break;
376  case UINT64:
377  convert_impl<uint64_t, double>(*reinterpret_cast<const uint64_t*>(raw_data),
378  target);
379  break;
380 
381  case FLOAT32:
382  convert_impl<float, double>(*reinterpret_cast<const float*>(raw_data), target);
383  break;
384  case FLOAT64:
385  return extract<double>();
386 
387  case STRING: {
388  throw TypeException("String will not be converted to a double implicitly");
389  }
390  break;
391 
392  case DURATION:
393  case TIME: {
394  RosMsgParser::Time tmp = extract<RosMsgParser::Time>();
395  target = tmp.toSec();
396  }
397  break;
398 
399  default:
400  throw TypeException("Variant::convert -> cannot convert type" +
401  std::to_string(_type));
402  }
403  return target;
404 }
405 
406 template <>
408 {
409  if (_type != TIME)
410  {
411  throw TypeException("Variant::convert -> cannot convert RosMsgParser::Time");
412  }
413  return extract<RosMsgParser::Time>();
414 }
415 
416 template <>
417 inline std::string Variant::convert() const
418 {
419  if (_type != STRING)
420  {
421  throw TypeException("Variant::convert -> cannot convert to std::string");
422  }
423  return extract<std::string>();
424 }
425 
426 } // namespace RosMsgParser
427 
428 #endif // VARIANT_H
RosMsgParser::FLOAT32
@ FLOAT32
Definition: builtin_types.hpp:56
exceptions.hpp
RosMsgParser::Variant::getRawStorage
const uint8_t * getRawStorage() const
Definition: variant.hpp:207
RosMsgParser::INT8
@ INT8
Definition: builtin_types.hpp:52
RosMsgParser::BYTE
@ BYTE
Definition: builtin_types.hpp:46
RosMsgParser::Time::toSec
double toSec()
Definition: builtin_types.hpp:155
RosMsgParser::Variant::convert
T convert() const
RosMsgParser::TIME
@ TIME
Definition: builtin_types.hpp:58
RosMsgParser::Variant::assign
void assign(const T &value)
Definition: variant.hpp:253
RosMsgParser::Variant::raw_data
std::array< uint8_t, 8 > raw_data
Definition: variant.hpp:197
RosMsgParser::Variant::_type
BuiltinType _type
Definition: variant.hpp:203
RosMsgParser::details
Definition: conversion_impl.hpp:80
nonstd::span_lite::size
span_constexpr std::size_t size(span< T, Extent > const &spn)
Definition: span.hpp:1554
RosMsgParser::Variant::~Variant
~Variant()
Definition: variant.hpp:195
RosMsgParser::UINT8
@ UINT8
Definition: builtin_types.hpp:48
RosMsgParser::FLOAT64
@ FLOAT64
Definition: builtin_types.hpp:57
RosMsgParser
Definition: builtin_types.hpp:34
builtin_types.hpp
RosMsgParser::TypeException
Definition: exceptions.hpp:97
RosMsgParser::UINT64
@ UINT64
Definition: builtin_types.hpp:51
RosMsgParser::Variant::Variant
Variant()
Definition: variant.hpp:116
std::swap
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.hpp:21884
RosMsgParser::Variant::operator=
Variant & operator=(const Variant &other)
Definition: variant.hpp:155
RosMsgParser::DURATION
@ DURATION
Definition: builtin_types.hpp:59
RosMsgParser::UINT32
@ UINT32
Definition: builtin_types.hpp:50
RosMsgParser::UINT16
@ UINT16
Definition: builtin_types.hpp:49
string_view
basic_string_view< char > string_view
Definition: core.h:518
RosMsgParser::OTHER
@ OTHER
Definition: builtin_types.hpp:61
RosMsgParser::Variant::_storage
union RosMsgParser::Variant::@64 _storage
RosMsgParser::Variant::clearStringIfNecessary
void clearStringIfNecessary()
Definition: variant.hpp:264
RosMsgParser::Variant::getTypeID
BuiltinType getTypeID() const
Definition: variant.hpp:202
RosMsgParser::Variant::raw_string
char * raw_string
Definition: variant.hpp:198
RosMsgParser::STRING
@ STRING
Definition: builtin_types.hpp:60
conversion_impl.hpp
mqtt_test.data
dictionary data
Definition: mqtt_test.py:22
RosMsgParser::CHAR
@ CHAR
Definition: builtin_types.hpp:47
RosMsgParser::BOOL
@ BOOL
Definition: builtin_types.hpp:45
RosMsgParser::Time
Definition: builtin_types.hpp:150
RosMsgParser::INT16
@ INT16
Definition: builtin_types.hpp:53
RosMsgParser::BuiltinType
BuiltinType
Definition: builtin_types.hpp:43
RosMsgParser::Variant::extract
T extract() const
Definition: variant.hpp:213
RosMsgParser::INT32
@ INT32
Definition: builtin_types.hpp:54
RosMsgParser::INT64
@ INT64
Definition: builtin_types.hpp:55


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Nov 11 2024 03:23:48