SMTK  @SMTK_VERSION@
Simulation Modeling Tool Kit
ValueItemTemplate.h
1 //=========================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //=========================================================================
10 // .NAME ValueItemTemplate.h -
11 // .SECTION Description
12 // .SECTION See Also
13 
14 #ifndef smtk_attribute_ValueItemTemplate_h
15 #define smtk_attribute_ValueItemTemplate_h
16 
17 #include "smtk/attribute/ComponentItem.h"
18 #include "smtk/attribute/Evaluator.h"
19 #include "smtk/attribute/ValueItem.h"
20 #include "smtk/attribute/ValueItemDefinitionTemplate.h"
21 #include "smtk/io/Logger.h"
22 #include <cassert>
23 #include <cstdio>
24 #include <limits>
25 #include <sstream>
26 #include <vector>
27 
28 namespace smtk
29 {
30 namespace attribute
31 {
32 template<typename DataT>
33 class SMTK_ALWAYS_EXPORT ValueItemTemplate : public ValueItem
34 {
35  //template<DataT> friend class ValueItemDefinitionTemplate;
36 public:
37  typedef DataT DataType;
38  typedef typename std::vector<DataT> value_type;
39  typedef typename value_type::const_iterator const_iterator;
41 
42  ~ValueItemTemplate() override = default;
43  const_iterator begin() const { return m_values.begin(); }
44  const_iterator end() const { return m_values.end(); }
45  bool setNumberOfValues(std::size_t newSize) override;
46 
49  DataT value(std::size_t element = 0) const;
50  DataT value(smtk::io::Logger& log) const { return this->value(0, log); }
51  DataT value(std::size_t element, smtk::io::Logger& log) const;
53 
54  using ValueItem::valueAsString;
55  std::string valueAsString(std::size_t element) const override;
56  virtual bool setValue(const DataT& val) { return this->setValue(0, val); }
57  virtual bool setValue(std::size_t element, const DataT& val);
58 
59  using ValueItem::setValueFromString;
60  bool setValueFromString(std::size_t element, const std::string& val) override;
61 
62  template<typename I>
63  bool setValues(I vbegin, I vend)
64  {
65  bool ok = false;
66  std::size_t num = std::distance(vbegin, vend);
67  if (this->setNumberOfValues(num))
68  {
69  ok = true;
70  std::size_t i = 0;
71  for (I it = vbegin; it != vend; ++it, ++i)
72  {
73  if (!this->setValue(i, *it))
74  {
75  ok = false;
76  break;
77  }
78  }
79  }
80  return ok;
81  }
82  virtual bool appendValue(const DataT& val);
83  virtual bool removeValue(std::size_t element);
84  void reset() override;
85  bool rotate(std::size_t fromPosition, std::size_t toPosition) override;
86  bool setToDefault(std::size_t element = 0) override;
87  bool isUsingDefault(std::size_t element) const override;
88  bool isUsingDefault() const override;
89  DataT defaultValue() const;
90  const std::vector<DataT>& defaultValues() const;
91 
92  using Item::assign;
93  // Assigns this item to be equivalent to another. Options are processed by derived item classes
94  // Returns true if success and false if a problem occurred. By default, an attribute being used by this
95  // to represent an expression will be copied if needed. Use itemOptions.setIgnoreExpressions option to prevent this
96  // When an expression attribute is copied, its associations are by default not.
97  // Use attributeOptions.setCopyAssociations option if you want them copied as well.These options are defined in CopyAssigmentOptions.h .
98  Item::Status assign(
99  const smtk::attribute::ConstItemPtr& sourceItem,
100  const CopyAssignmentOptions& options,
101  smtk::io::Logger& logger) override;
102 
103  shared_ptr<const DefType> concreteDefinition() const
104  {
105  return dynamic_pointer_cast<const DefType>(this->definition());
106  }
107 
108 protected:
109  ValueItemTemplate(Attribute* owningAttribute, int itemPosition);
110  ValueItemTemplate(Item* owningItem, int myPosition, int mySubGroupPosition);
111  void updateDiscreteValue(std::size_t element) override;
112  bool initializeValues() override;
113 
114  std::vector<DataT> m_values;
115  const std::vector<DataT> m_dummy; //(1, DataT());
116 
117  std::string streamValue(const DataT& val) const;
118 };
119 
120 template<typename DataT>
121 ValueItemTemplate<DataT>::ValueItemTemplate(Attribute* owningAttribute, int itemPosition)
122  : ValueItem(owningAttribute, itemPosition)
123  , m_dummy(1, DataT())
124 {
125 }
126 
127 template<typename DataT>
128 ValueItemTemplate<DataT>::ValueItemTemplate(
129  Item* inOwningItem,
130  int itemPosition,
131  int mySubGroupPosition)
132  : ValueItem(inOwningItem, itemPosition, mySubGroupPosition)
133  , m_dummy(1, DataT())
134 {
135 }
136 
137 template<typename DataT>
138 DataT ValueItemTemplate<DataT>::value(std::size_t element) const
139 {
140  return this->value(element, smtk::io::Logger::instance());
141 }
142 
143 // When DataT does not have a default constructor, as in the case of double or
144 // int. DataT() results in a zero-initialized value:
145 // https://en.cppreference.com/w/cpp/language/value_initialization
146 template<typename DataT>
147 DataT ValueItemTemplate<DataT>::value(std::size_t element, smtk::io::Logger& log) const
148 {
149  if (!this->isSet(element))
150  {
152  log,
153  "Item \"" << this->name() << "\" element " << element << " is not set (attribute \""
154  << this->attribute()->name() << "\").");
155  return DataT();
156  }
157 
158  if (isExpression())
159  {
160  smtk::attribute::AttributePtr expAtt = expression();
161  if (!expAtt)
162  {
164  log,
165  "Item \"" << this->name() << "\" has no reference expression (attribute \""
166  << this->attribute()->name() << "\").");
167  return DataT();
168  }
169 
170  std::unique_ptr<smtk::attribute::Evaluator> evaluator = expAtt->createEvaluator();
171  if (!evaluator)
172  {
174  log,
175  "Item \"" << this->name() << "\" expression is not evaluate (attribute \""
176  << this->attribute()->name() << "\").");
177  return DataT();
178  }
179 
180  smtk::attribute::Evaluator::ValueType result;
181  // |evaluator| will report errors in |log| for the caller.
182  if (!evaluator->evaluate(
183  result, log, element, Evaluator::DependentEvaluationMode::EVALUATE_DEPENDENTS))
184  {
185  return DataT();
186  }
187 
188  DataT resultAsDataT;
189  try
190  {
191  resultAsDataT = boost::get<DataT>(result);
192  }
193  catch (const boost::bad_get&)
194  {
196  log,
197  "Item \"" << this->name() << "\" evaluation result was not compatible (attribute \""
198  << this->attribute()->name() << "\").");
199  return DataT();
200  }
201 
202  return resultAsDataT;
203  }
204  else
205  {
206  return m_values[element];
207  }
208 }
209 
210 template<typename DataT>
212 {
213  const DefType* def = dynamic_cast<const DefType*>(this->definition().get());
214  if (def == nullptr)
215  {
216  return false; // Can't initialize values without a definition
217  }
218  size_t n = def->numberOfRequiredValues();
219  if (n)
220  {
221  if (def->hasDefault())
222  {
223  // Assumes that if the definition is discrete then default value
224  // will be based on the default discrete index
225  if (def->defaultValues().size() > 1)
226  {
227  m_values = def->defaultValues();
228  }
229  else
230  {
231  m_values.resize(n, def->defaultValue());
232  }
233  }
234  else
235  {
236  m_values.resize(n);
237  }
238  }
239  return true;
240 }
241 
242 template<typename DataT>
243 bool ValueItemTemplate<DataT>::setValueFromString(std::size_t element, const std::string& sval)
244 {
245  // If the string is empty then unset the value.
246  if (sval.empty())
247  {
248  this->unset(element);
249  return true;
250  }
251 
252  std::istringstream iss(sval);
253  DataT val;
254  iss >> val;
255  return !iss.fail() && this->setValue(element, val);
256 }
257 
258 template<typename DataT>
259 bool ValueItemTemplate<DataT>::setValue(std::size_t element, const DataT& val)
260 {
261  // Simple Fail check
262  if (m_values.size() <= element)
263  {
264  return false;
265  }
266  const DefType* def = static_cast<const DefType*>(this->definition().get());
267  if (def->isDiscrete())
268  {
269  int index = def->findDiscreteIndex(val);
270  // Is this the current value?
271  assert(m_discreteIndices.size() > element);
272  if (index == m_discreteIndices[element])
273  {
274  return true;
275  }
276 
277  if (index != -1)
278  {
279  m_discreteIndices[element] = index;
280  m_values[element] = val;
281  if (def->allowsExpressions())
282  {
283  m_expression->unset();
284  }
285  assert(m_isSet.size() > element);
286  m_isSet[element] = true;
287  // Update active children if needed - note that
288  // we currently only support active children based on the
289  // 0th value changing
290  if (element == 0)
291  {
292  this->updateActiveChildrenItems();
293  }
294  return true;
295  }
296  return false;
297  }
298  if (def->isValueValid(val))
299  {
300  m_values[element] = val;
301  assert(m_isSet.size() > element);
302  m_isSet[element] = true;
303  if (def->allowsExpressions())
304  {
305  m_expression->unset();
306  }
307  return true;
308  }
309  return false;
310 }
311 
312 template<typename DataT>
313 void ValueItemTemplate<DataT>::updateDiscreteValue(std::size_t element)
314 {
315  const DefType* def = static_cast<const DefType*>(this->definition().get());
316  m_values[element] = def->discreteValue(static_cast<size_t>(m_discreteIndices[element]));
317 }
318 
319 template<typename DataT>
320 std::string ValueItemTemplate<DataT>::valueAsString(std::size_t element) const
321 {
322  if (this->isExpression())
323  {
324  // Can the expression be evaluated by value()? |log| will have errors when
325  // evaluation is not possible or failed.
326  smtk::io::Logger log;
327  DataT val = value(element, log);
328  if (log.hasErrors())
329  {
330  return "CANNOT_EVALUATE";
331  }
332 
333  return streamValue(val);
334  }
335  else
336  {
337  assert(m_isSet.size() > element);
338  if (m_isSet[element])
339  {
340  assert(m_values.size() > element);
341  return streamValue(m_values[element]);
342  }
343 
344  return "VALUE_IS_NOT_SET";
345  }
346 }
347 
348 template<typename DataT>
349 bool ValueItemTemplate<DataT>::appendValue(const DataT& val)
350 {
351  //First - are we allowed to change the number of values?
352  const DefType* def = static_cast<const DefType*>(this->definition().get());
353  if (!this->isExtensible())
354  {
355  return false; // The number of values is fixed
356  }
357 
358  std::size_t n = this->maxNumberOfValues();
359  if (n && (this->numberOfValues() >= n))
360  {
361  return false; // max number reached
362  }
363 
364  if (def->isDiscrete())
365  {
366  int index = def->findDiscreteIndex(val);
367  if (index != -1)
368  {
369  m_values.push_back(val);
370  m_discreteIndices.push_back(index);
371  m_isSet.push_back(true);
372  return true;
373  }
374  return false;
375  }
376  if (def->isValueValid(val))
377  {
378  if (def->allowsExpressions())
379  {
380  m_expression->unset();
381  }
382  m_values.push_back(val);
383  m_isSet.push_back(true);
384  return true;
385  }
386  return false;
387 }
388 
389 template<typename DataT>
390 bool ValueItemTemplate<DataT>::setNumberOfValues(std::size_t newSize)
391 {
392  // If the current size is the same just return
393  if (this->numberOfValues() == newSize)
394  {
395  return true;
396  }
397 
398  //Next - are we allowed to change the number of values?
399  if (!this->isExtensible())
400  {
401  return false; // The number of values is fixed
402  }
403 
404  // Is this size between the required number and the max?
405  if (newSize < this->numberOfRequiredValues())
406  {
407  return false;
408  }
409 
410  std::size_t n = this->maxNumberOfValues();
411  if (n && (newSize > n))
412  {
413  return false; // greater than max number
414  }
415 
416  const DefType* def = static_cast<const DefType*>(this->definition().get());
417  n = this->numberOfValues();
418  // Are we increasing or decreasing?
419  if (newSize < n)
420  {
421  m_values.resize(newSize);
422  m_isSet.resize(newSize);
423  if (def->isDiscrete())
424  {
425  m_discreteIndices.resize(newSize);
426  }
427  return true;
428  }
429  if (def->hasDefault())
430  {
431  if (def->defaultValues().size() == newSize)
432  {
433  m_values = def->defaultValues();
434  }
435  else
436  {
437  m_values.resize(newSize, def->defaultValue());
438  }
439  m_isSet.resize(newSize, true);
440  }
441  else
442  {
443  m_values.resize(newSize);
444  m_isSet.resize(newSize, false);
445  }
446  if (def->isDiscrete())
447  {
448  m_discreteIndices.resize(newSize, def->defaultDiscreteIndex());
449  }
450  return true;
451 }
452 
453 template<typename DataT>
454 bool ValueItemTemplate<DataT>::removeValue(std::size_t i)
455 {
456  const DefType* def = static_cast<const DefType*>(this->definition().get());
457  // If i < the required number of values this is the same as unset - else if
458  // its extensible remove it completely
459  if (i < def->numberOfRequiredValues())
460  {
461  this->unset(i);
462  return true;
463  }
464  if (i >= this->numberOfValues())
465  {
466  return false; // i can't be greater than the number of values
467  }
468  m_values.erase(m_values.begin() + i);
469  m_isSet.erase(m_isSet.begin() + i);
470  if (def->isDiscrete())
471  {
472  m_discreteIndices.erase(m_discreteIndices.begin() + i);
473  }
474  return true;
475 }
476 
477 template<typename DataT>
478 bool ValueItemTemplate<DataT>::setToDefault(std::size_t element)
479 {
480  const DefType* def = static_cast<const DefType*>(this->definition().get());
481  if (!def->hasDefault())
482  {
483  return false; // Doesn't have a default value
484  }
485 
486  if (def->isDiscrete())
487  {
488  this->setDiscreteIndex(element, def->defaultDiscreteIndex());
489  }
490  else
491  {
492  assert(def->defaultValues().size() > element);
493  this->setValue(
494  element,
495  def->defaultValues().size() > 1 ? def->defaultValues()[element] : def->defaultValue());
496  }
497  return true;
498 }
499 
500 template<typename DataT>
501 bool ValueItemTemplate<DataT>::isUsingDefault() const
502 {
503  const DefType* def = static_cast<const DefType*>(this->definition().get());
504  if (!def->hasDefault())
505  {
506  return false; // Doesn't have a default value
507  }
508 
509  std::size_t i, n = this->numberOfValues();
510  const DataT& dval = def->defaultValue();
511  const std::vector<DataT>& dvals = def->defaultValues();
512  bool vectorDefault = (dvals.size() == n);
513  for (i = 0; i < n; i++)
514  {
515  assert(m_isSet.size() > i);
516  assert(m_values.size() > i);
517  if (!(m_isSet[i] && (vectorDefault ? m_values[i] == dvals[i] : m_values[i] == dval)))
518  {
519  return false;
520  }
521  }
522  return true;
523 }
524 
525 template<typename DataT>
526 bool ValueItemTemplate<DataT>::isUsingDefault(std::size_t element) const
527 {
528  const DefType* def = static_cast<const DefType*>(this->definition().get());
529  assert(m_isSet.size() > element);
530  if (!(def->hasDefault() && m_isSet[element]))
531  {
532  return false; // Doesn't have a default value
533  }
534 
535  const DataT& dval = def->defaultValue();
536  const std::vector<DataT>& dvals = def->defaultValues();
537  bool vectorDefault = (dvals.size() == def->numberOfRequiredValues());
538  assert(m_values.size() > element);
539  return (vectorDefault ? m_values[element] == dvals[element] : m_values[element] == dval);
540 }
541 
542 template<typename DataT>
543 DataT ValueItemTemplate<DataT>::defaultValue() const
544 {
545  const DefType* def = static_cast<const DefType*>(this->definition().get());
546  if (!def)
547  return m_dummy[0];
548 
549  return def->defaultValue();
550 }
551 
552 template<typename DataT>
553 const std::vector<DataT>& ValueItemTemplate<DataT>::defaultValues() const
554 {
555  const DefType* def = static_cast<const DefType*>(this->definition().get());
556  if (!def)
557  return m_dummy;
558 
559  return def->defaultValues();
560 }
561 
562 template<typename DataT>
563 void ValueItemTemplate<DataT>::reset()
564 {
565  const DefType* def = static_cast<const DefType*>(this->definition().get());
566  // If we can have an expression then clear it
567  if (def->allowsExpressions())
568  {
569  m_expression->unset();
570  }
571 
572  // Was the initial size 0?
573  std::size_t i, n = this->numberOfRequiredValues();
574  if (this->numberOfValues() != n)
575  {
576  this->setNumberOfValues(n);
577  }
578  if (!n)
579  {
580  m_values.clear();
581  m_isSet.clear();
582  m_discreteIndices.clear();
583  ValueItem::reset();
584  return;
585  }
586 
587  if (!def->hasDefault()) // Do we have default values
588  {
589  for (i = 0; i < n; i++)
590  {
591  this->unset(i);
592  }
593  ValueItem::reset();
594  return;
595  }
596 
597  if (def->isDiscrete())
598  {
599  int index = def->defaultDiscreteIndex();
600  for (i = 0; i < n; i++)
601  {
602  this->setDiscreteIndex(i, index);
603  }
604  }
605  else
606  {
607  DataT dval = def->defaultValue();
608  const std::vector<DataT>& dvals = def->defaultValues();
609  bool vectorDefault = (dvals.size() == n);
610  for (i = 0; i < n; i++)
611  {
612  this->setValue(i, vectorDefault ? dvals[i] : dval);
613  }
614  }
615  ValueItem::reset();
616 }
617 
618 template<typename DataT>
619 bool ValueItemTemplate<DataT>::rotate(std::size_t fromPosition, std::size_t toPosition)
620 {
621  // Let's first verify that ValueItem was OK with the rotation.
622  if (!ValueItem::rotate(fromPosition, toPosition))
623  {
624  return false;
625  }
626 
627  // No need to check to see if the rotation is valid since ValueItem already checked it
628  this->rotateVector(m_values, fromPosition, toPosition);
629  return true;
630 }
631 
632 template<typename DataT>
634  const smtk::attribute::ConstItemPtr& sourceItem,
635  const CopyAssignmentOptions& options,
636  smtk::io::Logger& logger)
637 {
638  Item::Status result = ValueItem::assign(sourceItem, options, logger);
639  if (!result.success())
640  {
641  return result;
642  }
643  // Cast input pointer to ValueItemTemplate
644  const ValueItemTemplate<DataT>* sourceValueItemTemplate =
645  dynamic_cast<const ValueItemTemplate<DataT>*>(sourceItem.get());
646 
647  if (!sourceValueItemTemplate)
648  {
649  result.markFailed();
650  smtkErrorMacro(logger, "Source Item: " << name() << " is not a ValueItemTemplate");
651  return result; // Source is not the right type of item
652  }
653  // If the item is discrete or an expression there is nothing to be done
654  if (sourceValueItemTemplate->isExpression() || sourceValueItemTemplate->isDiscrete())
655  {
656  return result;
657  }
658 
659  // Update values
660  bool status;
661  if (this->numberOfValues() != sourceValueItemTemplate->numberOfValues())
662  {
663  status = this->setNumberOfValues(sourceValueItemTemplate->numberOfValues());
664  if (status)
665  {
666  result.markModified();
667  }
668  }
669 
670  // Were we able to allocate enough space to fit all of the source's values?
671  std::size_t myNumVals, sourceNumVals, numVals;
672  myNumVals = this->numberOfValues();
673  sourceNumVals = sourceValueItemTemplate->numberOfValues();
674  if (myNumVals < sourceNumVals)
675  {
676  // Ok so the source has more values than we can deal with - was partial copying permitted?
677  if (options.itemOptions.allowPartialValues())
678  {
679  numVals = myNumVals;
681  logger,
682  "ValueItem: " << this->name() << "'s number of values (" << myNumVals
683  << ") is smaller than source Item's number of values (" << sourceNumVals
684  << ") - will partially copy the values");
685  }
686  else
687  {
688  result.markFailed();
690  logger,
691  "ValueItem: " << name() << "'s number of values (" << myNumVals
692  << ") can not hold source ValueItem's number of values (" << sourceNumVals
693  << ") and Partial Copying was not permitted");
694  return result;
695  }
696  }
697  else
698  {
699  numVals = sourceNumVals;
700  }
701 
702  for (std::size_t i = 0; i < numVals; ++i)
703  {
704  if (sourceValueItemTemplate->isSet(i))
705  {
706  if (this->valueAsString(i) == sourceValueItemTemplate->valueAsString(i))
707  {
708  continue;
709  }
710  status = this->setValueFromString(i, sourceValueItemTemplate->valueAsString(i));
711  if (!status)
712  {
713  if (options.itemOptions.allowPartialValues())
714  {
716  logger,
717  "Could not assign Value:" << sourceValueItemTemplate->value(i)
718  << " to ValueItem: " << sourceItem->name());
719  this->unset(i);
720  result.markModified();
721  }
722  else
723  {
724  result.markFailed();
726  logger,
727  "Could not assign Value:" << sourceValueItemTemplate->value(i)
728  << " to ValueItem: " << sourceItem->name()
729  << " and allowPartialValues options was not specified.");
730  return result;
731  }
732  }
733  if (status)
734  {
735  result.markModified();
736  }
737  }
738  }
739  return result;
740 }
741 
742 template<typename DataT>
743 std::string ValueItemTemplate<DataT>::streamValue(const DataT& val) const
744 {
745  std::stringstream buffer;
746  buffer.precision(std::numeric_limits<DataT>::max_digits10);
747  buffer << val;
748  return buffer.str();
749 }
750 
751 } // namespace attribute
752 } // namespace smtk
753 
754 #endif /* smtk_attribute_ValueItemTemplate_h */
smtk
The main namespace for the Simulation Modeling Tool Kit (SMTK).
Definition: doc.h:33
Logger.h
smtk::common::Status::markFailed
bool markFailed()
Mark the Status to indicate a method failed.
Definition: Status.cxx:24
smtk::attribute::CopyAssignmentOptions
Class used to specify copy and assignment options.
Definition: CopyAssignmentOptions.h:272
smtk::attribute::ValueItemTemplate
Definition: ValueItemTemplate.h:33
smtk::attribute::ValueItemTemplate::value
DataT value(std::size_t element=0) const
Returns a value of the item in the units specified in the units of its definition.
Definition: ValueItemTemplate.h:138
smtk::attribute::Item::assign
virtual Status assign(const smtk::attribute::ConstItemPtr &sourceItem, const CopyAssignmentOptions &options=CopyAssignmentOptions())
Definition: Item.cxx:274
smtk::io::Logger
Log messages for later presentation to a user or a file.
Definition: Logger.h:94
smtkErrorMacro
#define smtkErrorMacro(logger, x)
Write the expression x to logger as an error message.
Definition: Logger.h:37
smtk::common::Status::markModified
bool markModified()
Mark the Status to indicate an object was modified.
Definition: Status.cxx:17
smtk::common::Status::success
bool success() const
Return whether or not a method succeeded (true) or not (false).
Definition: Status.h:39
smtk::common::Status
A return value for methods that need to indicate both success/failure and modification/stasis.
Definition: Status.h:30
smtk::attribute::AttributePtr
smtk::shared_ptr< smtk::attribute::Attribute > AttributePtr
Definition: PublicPointerDefs.h:463
smtk::attribute::ConstItemPtr
smtk::shared_ptr< const smtk::attribute::Item > ConstItemPtr
Definition: PublicPointerDefs.h:476
smtk::attribute::ValueItem
A concrete base class for items that store a vector of plain-old-data (POD) as values.
Definition: ValueItem.h:37
smtk::attribute::ValueItemTemplate::value
DataT value(smtk::io::Logger &log) const
Returns a value of the item in the units specified in the units of its definition.
Definition: ValueItemTemplate.h:50
smtk::attribute::ValueItemDefinitionTemplate
Definition: ValueItemDefinitionTemplate.h:28
smtk::attribute::ItemAssignmentOptions::allowPartialValues
bool allowPartialValues() const
Methods to set and retrieve the allowPartialValues Option.
Definition: CopyAssignmentOptions.h:208
smtkInfoMacro
#define smtkInfoMacro(logger, x)
Write the expression x to logger as an informational message.
Definition: Logger.h:76