libvisiontransfer  10.8.0
parametervalue.cpp
1 /*******************************************************************************
2  * Copyright (c) 2024 Allied Vision Technologies GmbH
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *******************************************************************************/
14 
15 #include <string>
16 #include <vector>
17 #include <cstring>
18 #include <sstream>
19 #include <set>
20 
21 #include <iostream>
22 
23 #include <visiontransfer/parametervalue.h>
24 #include <visiontransfer/internal/conversionhelpers.h>
25 
26 namespace visiontransfer {
27 namespace param {
28 
29 using namespace internal;
30 
32 class ParameterValue::Pimpl {
33 public:
34  Pimpl();
35  Pimpl(const Pimpl& other);
36  ~Pimpl();
37  static void copyData(ParameterValue::Pimpl& dst, const ParameterValue::Pimpl& src);
38  Pimpl& setType(ParameterValue::ParameterType t);
39  Pimpl& setTensorShape(const std::vector<unsigned int>& shape);
40  bool isDefined() const;
41  bool isUndefined() const;
42  bool isTensor() const;
43  bool isScalar() const;
44  bool isCommand() const;
45  unsigned int getTensorDimension() const;
46  std::vector<unsigned int> getTensorShape() const;
48  std::vector<double> getTensorData() const;
50  std::vector<double>& getTensorDataReference() { return tensorData; };
51  Pimpl& setTensorData(const std::vector<double>& data);
52  unsigned int getTensorNumElements() const;
53  unsigned int getTensorCurrentDataSize() const;
54  ParameterValue::ParameterType getType() const { return type; }
55  double& tensorElementAt(unsigned int x);
56  double& tensorElementAt(unsigned int y, unsigned int x);
57  double& tensorElementAt(unsigned int z, unsigned int y, unsigned int x);
58 
59  template<typename T> Pimpl& setValue(T t);
60  template<typename T> T getValue() const;
61  template<typename T> T getWithDefault(const T& deflt) const { return (type==TYPE_UNDEFINED) ? deflt : getValue<T>(); }
62 
63 private:
64  double numVal;
65  std::string stringVal;
66  unsigned int tensorNumElements; // quick access to number of elements
67  std::vector<unsigned int> tensorShape;
68  std::vector<double> tensorData;
69 
70  ParameterValue::ParameterType type;
71 
73  static std::string sanitizeString(const std::string& s, unsigned int maxLength=4096);
74 };
75 
76 //static
77 std::string ParameterValue::Pimpl::sanitizeString(const std::string& s, unsigned int maxLength /* = 4096 */ ) {
78  std::ostringstream ss;
79  const std::string whitelist("-+_,.:@/ "); // plus all alnums
80  unsigned int len = 0;
81  for (const char c: s) {
82  if (std::isalnum(c) || whitelist.find(c) != std::string::npos) {
83  ss << c;
84  } else {
85  ss << ' ';
86  }
87  if (++len > maxLength) break; // Trim excessively long safestrings
88  }
89  return ss.str();
90 }
91 //static
92 void ParameterValue::Pimpl::copyData(ParameterValue::Pimpl& dst, const ParameterValue::Pimpl& src) {
93  dst.numVal = src.numVal;
94  dst.stringVal = src.stringVal;
95  dst.tensorNumElements = src.tensorNumElements;
96  dst.tensorShape = src.tensorShape;
97  dst.tensorData = src.tensorData;
98  dst.type = src.type;
99 }
100 
101 ParameterValue::Pimpl::Pimpl()
102 : numVal(0.0), type(TYPE_UNDEFINED)
103 {
104 }
105 ParameterValue::Pimpl::Pimpl(const ParameterValue::Pimpl& other)
106 {
107  copyData(*this, other);
108 }
109 ParameterValue::Pimpl::~Pimpl()
110 {
111 }
112 
113 ParameterValue::Pimpl& ParameterValue::Pimpl::setType(ParameterValue::ParameterType t) {
114  type = t;
115  return *this;
116 };
117 
118 ParameterValue::Pimpl& ParameterValue::Pimpl::setTensorShape(const std::vector<unsigned int>& shape) {
119  unsigned int dims = (unsigned int) shape.size();
120  if (dims==0) {
121  throw std::runtime_error("Cannot create a zero-dimensional tensor");
122  }
123  int elems = 1;
124  for (unsigned int i=0; i<dims; ++i) {
125  elems *= shape[i];
126  }
127  if (elems==0) {
128  throw std::runtime_error("Cannot create a tensor with effective size 0");
129  }
130  tensorNumElements = elems;
131  tensorShape = shape;
132  tensorData.reserve(elems);
133  return *this;
134 }
135 
136 bool ParameterValue::Pimpl::isDefined() const {
137  return (type!=TYPE_UNDEFINED);
138 }
139 bool ParameterValue::Pimpl::isUndefined() const {
140  return (type==TYPE_UNDEFINED);
141 }
142 bool ParameterValue::Pimpl::isTensor() const {
143  return type==TYPE_TENSOR;
144 }
145 bool ParameterValue::Pimpl::isScalar() const {
146  return !isTensor();
147 }
148 bool ParameterValue::Pimpl::isCommand() const {
149  return type==TYPE_COMMAND;
150 }
151 
152 unsigned int ParameterValue::Pimpl::getTensorDimension() const {
153  return (unsigned int) tensorShape.size();
154 }
155 
156 std::vector<unsigned int> ParameterValue::Pimpl::getTensorShape() const {
157  return tensorShape;
158 }
159 
160 std::vector<double> ParameterValue::Pimpl::getTensorData() const {
161  return tensorData;
162 }
163 
164 ParameterValue::Pimpl& ParameterValue::Pimpl::setTensorData(const std::vector<double>& data) {
165  if (data.size() != tensorNumElements) throw std::runtime_error("ParameterValue::Pimpl::setTensorData(): wrong number of elements");
166 
167  setType(ParameterType::TYPE_TENSOR);
168  tensorData = data;
169  // Also cache a pre-rendered string version
170  std::ostringstream os;
171  for (unsigned int i=0; i<getTensorNumElements(); ++i) {
172  if (i) os << " ";
173  os << ConversionHelpers::anyToString(tensorData[i]);
174  }
175  stringVal = os.str();
176  return *this;
177 }
178 
179 unsigned int ParameterValue::Pimpl::getTensorNumElements() const {
180  return tensorNumElements;
181 }
182 
183 unsigned int ParameterValue::Pimpl::getTensorCurrentDataSize() const {
184  return (unsigned int) tensorData.size();
185 }
186 
187 double& ParameterValue::Pimpl::tensorElementAt(unsigned int x) {
188  // Pure 1-dim support
189  //if (tensorShape.size()!=1) throw std::runtime_error("ParameterValue::Pimpl::tensorElementAt(): not a tensor of dimension 1");
190  //if (x>=tensorShape[0]) throw std::runtime_error("ParameterValue::Pimpl::tensorElementAt(): access out of bounds");
191  // Any-dim support (allow self-addressing)
192  if (tensorShape.size()==0) throw std::runtime_error("ParameterValue::Pimpl::tensorElementAt(): not a tensor");
193  if (x>=tensorNumElements) throw std::runtime_error("ParameterValue::Pimpl::tensorElementAt(): access out of bounds");
194  return tensorData[x];
195 }
196 double& ParameterValue::Pimpl::tensorElementAt(unsigned int y, unsigned int x) {
197  if (tensorShape.size()!=2) throw std::runtime_error("ParameterValue::Pimpl::tensorElementAt(): not a tensor of dimension 2");
198  if (y>=tensorShape[0] || x>=tensorShape[1]) throw std::runtime_error("ParameterValue::Pimpl::tensorElementAt(): access out of bounds");
199  return tensorData[y*tensorShape[1] + x];
200 }
201 double& ParameterValue::Pimpl::tensorElementAt(unsigned int z, unsigned int y, unsigned int x) {
202  if (tensorShape.size()!=3) throw std::runtime_error("ParameterValue::Pimpl::tensorElementAt(): not a tensor of dimension 3");
203  if (z>=tensorShape[0] || y>=tensorShape[1] || x>=tensorShape[2]) throw std::runtime_error("ParameterValue::Pimpl::tensorElementAt(): access out of bounds");
204  return tensorData[z*tensorShape[1]*tensorShape[2] + y*tensorShape[2] + x];
205 }
206 
207 
208 // setters
209 template<> VT_EXPORT
210 ParameterValue::Pimpl& ParameterValue::Pimpl::setValue(int t) {
211  // always cache string
212  switch (this->type) {
213  case TYPE_INT:
214  case TYPE_DOUBLE:
215  case TYPE_STRING:
216  case TYPE_SAFESTRING:
217  case TYPE_COMMAND:
218  numVal = t;
219  stringVal = ConversionHelpers::anyToString(t);
220  break;
221  case TYPE_BOOL:
222  numVal = (t==0) ? 0 : 1;
223  stringVal = (t==0) ? "false" : "true";
224  break;
225  case TYPE_TENSOR:
226  // could in theory accept iff tensor is of exactly one-element size
227  throw std::runtime_error("Cannot assign a raw scalar to a tensor parameter");
228  case TYPE_UNDEFINED:
229  throw std::runtime_error("Cannot assign a value to an undefined parameter");
230  }
231  return *this;
232 }
233 
234 template<> VT_EXPORT
235 ParameterValue::Pimpl& ParameterValue::Pimpl::setValue(bool t) {
236  // always cache string
237  switch (this->type) {
238  case TYPE_INT:
239  case TYPE_DOUBLE:
240  numVal = t ? 1 : 0;
241  stringVal = ConversionHelpers::anyToString(numVal);
242  break;
243  case TYPE_STRING:
244  case TYPE_SAFESTRING:
245  case TYPE_COMMAND:
246  case TYPE_BOOL:
247  numVal = (t==false) ? 0 : 1;
248  stringVal = (t==false) ? "false" : "true";
249  break;
250  case TYPE_TENSOR:
251  // could in theory accept iff tensor is of exactly one-element size
252  throw std::runtime_error("Cannot assign a raw scalar to a tensor parameter");
253  case TYPE_UNDEFINED:
254  throw std::runtime_error("Cannot assign a value to an undefined parameter");
255  }
256  return *this;
257 }
258 template<> VT_EXPORT
259 ParameterValue::Pimpl& ParameterValue::Pimpl::setValue(double t) {
260  // always cache string
261  switch (this->type) {
262  case TYPE_DOUBLE:
263  case TYPE_STRING:
264  case TYPE_SAFESTRING:
265  case TYPE_COMMAND:
266  numVal = t;
267  stringVal = ConversionHelpers::anyToString(t);
268  break;
269  case TYPE_INT:
270  numVal = (int) t;
271  stringVal = ConversionHelpers::anyToString((int) t); // lose decimals here
272  break;
273  case TYPE_BOOL:
274  numVal = (t==0) ? 0 : 1;
275  stringVal = (t==0) ? "false" : "true";
276  break;
277  case TYPE_TENSOR:
278  // could in theory accept iff tensor is of exactly one-element size
279  throw std::runtime_error("Cannot assign a raw scalar to a tensor parameter");
280  case TYPE_UNDEFINED:
281  throw std::runtime_error("Cannot assign a value to an undefined parameter");
282  }
283  return *this;
284 }
285 
286 template<> VT_EXPORT
287 ParameterValue::Pimpl& ParameterValue::Pimpl::setValue(const char* t) {
288  // always cache string
289  switch (this->type) {
290  case TYPE_COMMAND:
291  case TYPE_SAFESTRING:
292  // sanitize!
293  stringVal = sanitizeString(t);
294  numVal = atof(stringVal.c_str());
295  break;
296  case TYPE_STRING:
297  stringVal = t;
298  numVal = atof(t);
299  break;
300  case TYPE_DOUBLE:
301  numVal = atof(t);
302  stringVal = ConversionHelpers::anyToString(numVal);
303  break;
304  case TYPE_INT:
305  // Lenient parsing of bools as 0/1; otherwise take the int portion of the string
306  if (!std::strncmp("true", t, 4) || !std::strncmp("True", t, 4)) {
307  numVal = 1;
308  } else if (!std::strncmp("false", t, 5) || !std::strncmp("False", t, 5)) {
309  numVal = 0;
310  } else {
311  numVal = atol(t);
312  }
313  stringVal = ConversionHelpers::anyToString((int) numVal);
314  break;
315  case TYPE_BOOL:
316  if (!std::strncmp("true", t, 4) || !std::strncmp("True", t, 4)) {
317  numVal = 1;
318  } else {
319  numVal = atol(t)==0 ? 0 : 1;
320  }
321  stringVal = (numVal==0.0) ? "false" : "true";
322  break;
323  case TYPE_TENSOR:
324  // could in theory accept iff tensor is of exactly one-element size
325  throw std::runtime_error("Cannot assign a raw scalar to a tensor parameter");
326  case TYPE_UNDEFINED:
327  throw std::runtime_error("Cannot assign a value to an undefined parameter");
328  }
329  return *this;
330 }
331 
332 template<> VT_EXPORT
333 ParameterValue::Pimpl& ParameterValue::Pimpl::setValue(const std::string& t) {
334  return setValue<const char*>(t.c_str());
335 }
336 template<> VT_EXPORT
337 ParameterValue::Pimpl& ParameterValue::Pimpl::setValue(std::string t) {
338  return setValue<const char*>(t.c_str());
339 }
340 
341 // getters
342 template<> VT_EXPORT
343 int ParameterValue::Pimpl::getValue() const {
344  switch (this->type) {
345  case TYPE_INT: case TYPE_DOUBLE: case TYPE_BOOL:
346  return (int) numVal;
347  case TYPE_STRING:
348  case TYPE_SAFESTRING:
349  case TYPE_COMMAND:
350  return (int) numVal; // also ok, since cached
351  case TYPE_TENSOR:
352  // could also return 0 or a dedicated type exception (or be OK if size ==1 element)
353  throw std::runtime_error("Attempted to get tensor parameter as scalar- undefined value");
354  case TYPE_UNDEFINED:
355  default:
356  return 0; // silent default
357  }
358 }
359 template<> VT_EXPORT
360 double ParameterValue::Pimpl::getValue() const {
361  switch (this->type) {
362  case TYPE_INT: case TYPE_DOUBLE: case TYPE_BOOL:
363  return (double) numVal;
364  case TYPE_STRING:
365  case TYPE_SAFESTRING:
366  case TYPE_COMMAND:
367  return (double) numVal; // also ok, since cached
368  case TYPE_TENSOR:
369  // could also return 0 or a dedicated type exception (or be OK if size ==1 element)
370  throw std::runtime_error("Attempted to get tensor parameter as scalar- undefined value");
371  case TYPE_UNDEFINED:
372  default:
373  return 0.0; // silent default
374  }
375 }
376 template<> VT_EXPORT
377 bool ParameterValue::Pimpl::getValue() const {
378  switch (this->type) {
379  case TYPE_INT: case TYPE_DOUBLE: case TYPE_BOOL:
380  return (bool) numVal;
381  case TYPE_STRING:
382  case TYPE_SAFESTRING:
383  case TYPE_COMMAND:
384  return (bool) numVal; // also ok, since cached
385  case TYPE_TENSOR:
386  // could also return 0 or a dedicated type exception (or be OK if size ==1 element)
387  throw std::runtime_error("Attempted to get tensor parameter as scalar- undefined value");
388  case TYPE_UNDEFINED:
389  default:
390  return false; // silent default
391  }
392 }
393 template<> VT_EXPORT
394 std::string ParameterValue::Pimpl::getValue() const {
395  switch (this->type) {
396  case TYPE_INT: case TYPE_DOUBLE: case TYPE_BOOL:
397  // string is pre-rendered
398  case TYPE_TENSOR:
399  // also pre-rendered (in setTensorData)
400  case TYPE_STRING: case TYPE_SAFESTRING: case TYPE_COMMAND:
401  return stringVal;
402  case TYPE_UNDEFINED:
403  default:
404  return ""; // silent default
405  }
406 }
407 
408 // Mainly for literals placed in test code etc - maybe remove?
409 template<> VT_EXPORT
410 const char* ParameterValue::Pimpl::getValue() const {
411  switch (this->type) {
412  case TYPE_INT: case TYPE_DOUBLE: case TYPE_BOOL:
413  // string is pre-rendered
414  case TYPE_TENSOR:
415  // also pre-rendered (in setTensorData)
416  case TYPE_STRING: case TYPE_SAFESTRING: case TYPE_COMMAND:
417  return stringVal.c_str();
418  case TYPE_UNDEFINED:
419  default:
420  return ""; // silent default
421  }
422 }
423 
424 
425 //
426 //
427 // External (API) class
428 //
429 //
430 
431 ParameterValue::ParameterValue()
432 : pimpl(new ParameterValue::Pimpl())
433 {
434 }
435 
436 ParameterValue::ParameterValue(const ParameterValue& other)
437 : pimpl(new ParameterValue::Pimpl())
438 {
439  ParameterValue::Pimpl::copyData(*pimpl, *(other.pimpl));
440 }
441 
442 ParameterValue::~ParameterValue() {
443  delete pimpl;
444 }
445 
446 ParameterValue& ParameterValue::operator=(const ParameterValue& other) {
447  ParameterValue::Pimpl::copyData(*pimpl, *(other.pimpl));
448  return *this;
449 }
450 
451 ParameterValue& ParameterValue::setType(ParameterValue::ParameterType t) {
452  pimpl->setType(t);
453  return *this;
454 }
455 
456 ParameterValue& ParameterValue::setTensorShape(const std::vector<unsigned int>& shape) {
457  pimpl->setTensorShape(shape);
458  return *this;
459 }
460 
461 bool ParameterValue::isDefined() const {
462  return pimpl->isDefined();
463 }
464 
465 bool ParameterValue::isUndefined() const {
466  return pimpl->isUndefined();
467 }
468 bool ParameterValue::isTensor() const {
469  return pimpl->isTensor();
470 }
471 bool ParameterValue::isScalar() const {
472  return pimpl->isScalar();
473 }
474 bool ParameterValue::isCommand() const {
475  return pimpl->isCommand();
476 }
477 unsigned int ParameterValue::getTensorDimension() const {
478  return pimpl->getTensorDimension();
479 }
480 std::vector<unsigned int> ParameterValue::getTensorShape() const {
481  return pimpl->getTensorShape();
482 }
484 std::vector<double> ParameterValue::getTensorData() const {
485  return pimpl->getTensorData();
486 }
488 std::vector<double>& ParameterValue::getTensorDataReference() {
489  return pimpl->getTensorDataReference();
490 }
491 ParameterValue& ParameterValue::setTensorData(const std::vector<double>& data) {
492  pimpl->setTensorData(data);
493  return *this;
494 }
495 unsigned int ParameterValue::getTensorNumElements() const {
496  return pimpl->getTensorNumElements();
497 }
498 unsigned int ParameterValue::getTensorCurrentDataSize() const {
499  return pimpl->getTensorCurrentDataSize();
500 }
501 ParameterValue::ParameterType ParameterValue::getType() const {
502  return pimpl->getType();
503 }
504 double& ParameterValue::tensorElementAt(unsigned int x) {
505  return pimpl->tensorElementAt(x);
506 }
507 double& ParameterValue::tensorElementAt(unsigned int y, unsigned int x) {
508  return pimpl->tensorElementAt(y, x);
509 }
510 double& ParameterValue::tensorElementAt(unsigned int z, unsigned int y, unsigned int x) {
511  return pimpl->tensorElementAt(z, y, x);
512 }
513 
514 // getters
515 
516 template<> VT_EXPORT
517 int ParameterValue::getValue() const {
518  return pimpl->getValue<int>();
519 }
520 template<> VT_EXPORT
521 bool ParameterValue::getValue() const {
522  return pimpl->getValue<bool>();
523 }
524 template<> VT_EXPORT
525 double ParameterValue::getValue() const {
526  return pimpl->getValue<double>();
527 }
528 template<> VT_EXPORT
529 std::string ParameterValue::getValue() const {
530  return pimpl->getValue<std::string>();
531 }
532 template<> VT_EXPORT
533 const char* ParameterValue::getValue() const {
534  return pimpl->getValue<const char*>();
535 }
536 
537 // setters
538 template<> VT_EXPORT
539 ParameterValue& ParameterValue::setValue(int t) {
540  pimpl->setValue<int>(t);
541  return *this;
542 }
543 template<> VT_EXPORT
544 ParameterValue& ParameterValue::setValue(bool t) {
545  pimpl->setValue<bool>(t);
546  return *this;
547 }
548 template<> VT_EXPORT
549 ParameterValue& ParameterValue::setValue(double t) {
550  pimpl->setValue<double>(t);
551  return *this;
552 }
553 template<> VT_EXPORT
554 ParameterValue& ParameterValue::setValue(const char* t) {
555  pimpl->setValue<const char*>(t);
556  return *this;
557 }
558 template<> VT_EXPORT
559 ParameterValue& ParameterValue::setValue(const std::string& t) {
560  return setValue<const char*>(t.c_str());
561 }
562 template<> VT_EXPORT
563 ParameterValue& ParameterValue::setValue(std::string t) {
564  return setValue<const char*>(t.c_str());
565 }
566 
567 } // namespace param
568 } // namespace visiontransfer
569 
visiontransfer::param::ParameterValue::getTensorDataReference
VT_EXPORT std::vector< double > & getTensorDataReference()
Return a reference to the internal tensor data (caution)
Definition: parametervalue.cpp:512
visiontransfer::param::ParameterValue::getTensorData
VT_EXPORT std::vector< double > getTensorData() const
Return a copy of the internal tensor data.
Definition: parametervalue.cpp:508
visiontransfer::internal::ConversionHelpers::anyToString
static std::string anyToString(T val)
Converts any type to a string.
Definition: conversionhelpers.h:66
Allied Vision