vtkPVStringFormatter.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
2 // SPDX-License-Identifier: BSD-3-Clause
13 #ifndef vtkPVStringFormatter_h
14 #define vtkPVStringFormatter_h
15 
16 #include "vtkLogger.h"
17 #include "vtkObject.h"
18 #include "vtkPVVTKExtensionsCoreModule.h" // needed for export macro
19 
20 #include <algorithm>
21 #include <memory>
22 #include <sstream>
23 #include <stack>
24 
25 // clang-format off
26 #include <vtk_fmt.h> // needed for `fmt`
27 #include VTK_FMT(fmt/args.h)
28 #include VTK_FMT(fmt/chrono.h)
29 #include VTK_FMT(fmt/core.h)
30 #include VTK_FMT(fmt/ranges.h)
31 // clang-format on
32 
34 {
35 public:
36  static vtkPVStringFormatter* New();
38  void PrintSelf(ostream& os, vtkIndent indent) override;
39 
44  class TraceScope
45  {
46  public:
47  template <typename... Args>
48  TraceScope(Args&&... args)
49  {
51  }
52 
53  template <typename... Args>
54  TraceScope(const char* scopeName, Args&&... args)
55  {
56  vtkPVStringFormatter::PushScope(scopeName, args...);
57  }
58 
60  };
61 
66  template <typename... Args>
67  static void PushScope(Args&&... args)
68  {
69  std::shared_ptr<vtkArgumentScope> newScope;
70 
71  if (vtkPVStringFormatter::ScopeStack.empty()) // check if stack is empty
72  {
73  newScope = std::make_shared<vtkArgumentScope>();
74  }
75  else // else use the top scope as a baseline for the new scope
76  {
77  newScope = std::make_shared<vtkArgumentScope>(*vtkPVStringFormatter::ScopeStack.top());
78  }
79  vtkPVStringFormatter::Push(*newScope, args...);
80  vtkPVStringFormatter::ScopeStack.push(newScope);
81  }
82 
93  template <typename... Args>
94  static void PushScope(const char* scopeName, Args&&... args)
95  {
96  std::shared_ptr<vtkArgumentScope> newScope;
97 
98  if (vtkPVStringFormatter::ScopeStack.empty()) // check if stack is empty
99  {
100  newScope = std::make_shared<vtkArgumentScope>();
101  }
102  else // else use the top scope as a baseline for the new scope
103  {
104  newScope = std::make_shared<vtkArgumentScope>(*vtkPVStringFormatter::ScopeStack.top());
105  }
106  vtkPVStringFormatter::Push(*newScope, scopeName, args...);
107  vtkPVStringFormatter::ScopeStack.push(newScope);
108  }
109 
113  static void PopScope();
114 
119  static std::string Format(const std::string& formattableString);
120 
121 protected:
123  ~vtkPVStringFormatter() override;
124 
125 private:
127  void operator=(const vtkPVStringFormatter&) = delete;
128 
129  using char_type = fmt::format_context::char_type;
130 
134  struct vtkNamedArgument
135  {
136  enum class ValueType
137  {
138  // single values
139  NONE,
140  INT,
141  UNSIGNED,
142  LONG_LONG,
143  UNSIGNED_LONG_LONG,
144  BOOL,
145  CHAR,
146  FLOAT,
147  DOUBLE,
148  LONG_DOUBLE,
149  STRING,
150  TIME_POINT,
151  // vector values
152  DOUBLE_VECTOR
153  };
154  struct Value
155  {
156  ValueType Type;
157  union
158  {
159  // single values
160  int Int;
161  unsigned Unsigned;
162  long long LongLong;
163  unsigned long long UnsignedLongLong;
164  bool Bool;
165  char_type Char;
166  float Float;
167  double Double;
168  long double LongDouble;
169  std::basic_string<char_type> String;
170  std::chrono::time_point<std::chrono::system_clock> TimePoint;
171 
172  // vector values
173  std::vector<double> DoubleVector;
174  };
175 
177  : Type(ValueType::NONE)
178  {
179  }
180 
181  Value(int value)
182  : Type(ValueType::INT)
183  , Int(value)
184  {
185  }
186 
187  Value(unsigned value)
188  : Type(ValueType::UNSIGNED)
189  , Unsigned(value)
190  {
191  }
192 
193  Value(long long value)
194  : Type(ValueType::LONG_LONG)
195  , LongLong(value)
196  {
197  }
198 
199  Value(unsigned long long value)
200  : Type(ValueType::UNSIGNED_LONG_LONG)
201  , UnsignedLongLong(value)
202  {
203  }
204 
205  Value(bool value)
206  : Type(ValueType::BOOL)
207  , Bool(value)
208  {
209  }
210 
211  Value(char_type value)
212  : Type(ValueType::CHAR)
213  , Char(value)
214  {
215  }
216 
217  Value(float value)
218  : Type(ValueType::FLOAT)
219  , Float(value)
220  {
221  }
222 
223  Value(double value)
224  : Type(ValueType::DOUBLE)
225  , Double(value)
226  {
227  }
228 
229  Value(long double value)
230  : Type(ValueType::LONG_DOUBLE)
231  , LongDouble(value)
232  {
233  }
234 
235  Value(const char_type* value)
236  : Type(ValueType::STRING)
237  , String(value)
238  {
239  }
240 
241  Value(const std::basic_string<char_type>& value)
242  : Type(ValueType::STRING)
243  , String(value)
244  {
245  }
246 
247  Value(const std::chrono::time_point<std::chrono::system_clock>& value)
248  : Type(ValueType::TIME_POINT)
249  , TimePoint(value)
250  {
251  }
252 
253  Value(const std::vector<double>& values)
254  : Type(ValueType::DOUBLE_VECTOR)
255  , DoubleVector(values)
256  {
257  }
258 
259  Value(const Value& value)
260  {
261  this->Type = value.Type;
262  switch (value.Type)
263  {
264  case ValueType::INT:
265  this->Int = value.Int;
266  break;
267  case ValueType::UNSIGNED:
268  this->Unsigned = value.Unsigned;
269  break;
270  case ValueType::LONG_LONG:
271  this->LongLong = value.LongLong;
272  break;
273  case ValueType::UNSIGNED_LONG_LONG:
274  this->UnsignedLongLong = value.UnsignedLongLong;
275  break;
276  case ValueType::BOOL:
277  this->Bool = value.Bool;
278  break;
279  case ValueType::CHAR:
280  this->Char = value.Char;
281  break;
282  case ValueType::FLOAT:
283  this->Float = value.Float;
284  break;
285  case ValueType::DOUBLE:
286  this->Double = value.Double;
287  break;
288  case ValueType::LONG_DOUBLE:
289  this->LongDouble = value.LongDouble;
290  break;
291  case ValueType::STRING:
292  new (&this->String) std::basic_string<char_type>(value.String);
293  break;
294  case ValueType::TIME_POINT:
295  this->TimePoint = value.TimePoint;
296  break;
297  case ValueType::DOUBLE_VECTOR:
298  new (&this->DoubleVector) std::vector<double>(value.DoubleVector);
299  break;
300  default:
301  break;
302  }
303  }
304 
306  {
307  switch (this->Type)
308  {
309  case ValueType::STRING:
310  this->String.~basic_string();
311  break;
312  case ValueType::DOUBLE_VECTOR:
313  this->DoubleVector.~vector();
314  break;
315  default:
316  break;
317  }
318  }
319  };
320 
321  std::basic_string<char_type> Name;
322  Value Value;
323 
324  vtkNamedArgument() = default;
325 
326  template <typename DataType>
327  vtkNamedArgument(const std::basic_string<char_type>& name, const DataType& value)
328  : Name(name)
329  , Value(value)
330  {
331  }
332 
333  ~vtkNamedArgument() = default;
334  };
335 
339  class vtkArgumentScope
340  {
341  private:
342  std::vector<vtkNamedArgument> Arguments;
343 
344  public:
345  vtkArgumentScope() = default;
346 
347  vtkArgumentScope(const vtkArgumentScope& other)
348  {
349  this->Arguments.reserve(other.Arguments.size());
350  for (const auto& arg : other.Arguments)
351  {
352  this->Arguments.emplace_back(arg);
353  }
354  }
355 
360  template <typename T>
361  void AddArg(const fmt::detail::named_arg<char_type, T>& fmtArg)
362  {
363  bool argNotFound = std::find_if(this->Arguments.begin(), this->Arguments.end(),
364  [&fmtArg](const vtkNamedArgument& arg)
365  { return arg.Name == fmtArg.name; }) == this->Arguments.end();
366  // if argument was not found
367  if (argNotFound)
368  {
369  vtkNamedArgument newArg(fmtArg.name, fmtArg.value);
370  this->Arguments.push_back(newArg);
371  }
372  else // else print message
373  {
374  vtkLogF(TRACE, "Argument %s already exists. Try to add another one.", fmtArg.name);
375  }
376  }
377 
381  std::basic_string<char_type> GetArgInfo() const
382  {
383  std::basic_stringstream<char_type> argInfo;
384  for (const auto& arg : this->Arguments)
385  {
386  argInfo << "\tName: " << arg.Name;
387  argInfo << "\tType: ";
388  switch (arg.Value.Type)
389  {
390  case vtkNamedArgument::ValueType::INT:
391  argInfo << "int";
392  break;
393  case vtkNamedArgument::ValueType::UNSIGNED:
394  argInfo << "unsigned";
395  break;
396  case vtkNamedArgument::ValueType::LONG_LONG:
397  argInfo << "long long";
398  break;
399  case vtkNamedArgument::ValueType::UNSIGNED_LONG_LONG:
400  argInfo << "unsigned long long";
401  break;
402  case vtkNamedArgument::ValueType::BOOL:
403  argInfo << "bool";
404  break;
405  case vtkNamedArgument::ValueType::CHAR:
406  argInfo << "char";
407  break;
409  argInfo << "float";
410  break;
411  case vtkNamedArgument::ValueType::DOUBLE:
412  argInfo << "double";
413  break;
414  case vtkNamedArgument::ValueType::LONG_DOUBLE:
415  argInfo << "long double";
416  break;
417  case vtkNamedArgument::ValueType::STRING:
418  argInfo << "std::string";
419  break;
420  case vtkNamedArgument::ValueType::TIME_POINT:
421  argInfo << "std::chrono::time_point<std::chrono::system_clock>";
422  break;
423  case vtkNamedArgument::ValueType::DOUBLE_VECTOR:
424  argInfo << "std::vector<double>";
425  break;
426  default:
427  argInfo << "unknown";
428  break;
429  }
430  argInfo << "\n";
431  }
432  return argInfo.str();
433  }
434 
438  fmt::dynamic_format_arg_store<fmt::format_context> GetArgs() const
439  {
440  fmt::dynamic_format_arg_store<fmt::format_context> args;
441  for (const auto& arg : this->Arguments)
442  {
443  switch (arg.Value.Type)
444  {
445  case vtkNamedArgument::ValueType::INT:
446  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Int));
447  break;
448  case vtkNamedArgument::ValueType::UNSIGNED:
449  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Unsigned));
450  break;
451  case vtkNamedArgument::ValueType::LONG_LONG:
452  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.LongLong));
453  break;
454  case vtkNamedArgument::ValueType::UNSIGNED_LONG_LONG:
455  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.UnsignedLongLong));
456  break;
457  case vtkNamedArgument::ValueType::BOOL:
458  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Bool));
459  break;
460  case vtkNamedArgument::ValueType::CHAR:
461  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Char));
462  break;
464  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Float));
465  break;
466  case vtkNamedArgument::ValueType::DOUBLE:
467  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Double));
468  break;
469  case vtkNamedArgument::ValueType::LONG_DOUBLE:
470  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.LongDouble));
471  break;
472  case vtkNamedArgument::ValueType::STRING:
473  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.String));
474  break;
475  case vtkNamedArgument::ValueType::TIME_POINT:
476  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.TimePoint));
477  break;
478  case vtkNamedArgument::ValueType::DOUBLE_VECTOR:
479  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.DoubleVector));
480  break;
481  default:
482  break;
483  }
484  }
485  return args;
486  }
487 
491  void clear() { this->Arguments.clear(); }
492  };
493 
497  static std::string GetArgInfo();
498 
502  static void Push(vtkArgumentScope& vtkNotUsed(scope)) {}
503 
507  template <typename T0, typename... TArgs>
508  static void Push(vtkArgumentScope& scope, T0& arg0, TArgs&... args)
509  {
510  scope.AddArg(arg0);
511  vtkPVStringFormatter::Push(scope, args...);
512  }
513 
517  static void Push(vtkArgumentScope& vtkNotUsed(scope), const char* vtkNotUsed(scopeName)) {}
518 
522  template <typename T0, typename... TArgs>
523  static void Push(vtkArgumentScope& scope, const char* scopeName, T0& arg0, TArgs&... args)
524  {
525  auto scopeBasedArgName = std::string(scopeName) + "_" + arg0.name;
526  scope.AddArg(fmt::arg(scopeBasedArgName.c_str(), arg0.value));
527  scope.AddArg(arg0);
528  vtkPVStringFormatter::Push(scope, scopeName, args...);
529  }
530 
531  static std::stack<std::shared_ptr<vtkArgumentScope>> ScopeStack;
532 };
533 
534 #define PV_STRING_FORMATTER_SCOPE_0(x, y) x##y
535 #define PV_STRING_FORMATTER_SCOPE_1(x, y) PV_STRING_FORMATTER_SCOPE_0(x, y)
536 #define PV_STRING_FORMATTER_SCOPE(...) \
537  vtkPVStringFormatter::TraceScope PV_STRING_FORMATTER_SCOPE_1(_trace_item, __LINE__)(__VA_ARGS__)
538 #define PV_STRING_FORMATTER_NAMED_SCOPE(NAME, ...) \
539  vtkPVStringFormatter::TraceScope PV_STRING_FORMATTER_SCOPE_1(_trace_item, __LINE__)( \
540  NAME, __VA_ARGS__)
541 
542 #endif
const int FLOAT
static void PopScope()
Pops the top scope of the scope stack.
TraceScope(const char *scopeName, Args &&... args)
#define VTKPVVTKEXTENSIONSCORE_EXPORT
void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
name
Value(const std::chrono::time_point< std::chrono::system_clock > &value)
static void PushScope(Args &&... args)
Pushes arguments using fmt::arg(name, arg) to add a new scope to the scope stack. ...
const int NONE
Value(const std::basic_string< char_type > &value)
static void PushScope(const char *scopeName, Args &&... args)
Pushes arguments using a scope name and arguments in the form of fmt::arg(name, arg) to add a new sco...
Utility class used for string formatting.
This subclass should ONLY be used to enable automatic push/pop of argument scopes in the same scope o...
value
Value(const std::vector< double > &values)
static vtkObject * New()
std::chrono::time_point< std::chrono::system_clock > TimePoint