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  // single values
159  int Int;
160  unsigned Unsigned;
161  long long LongLong;
162  unsigned long long UnsignedLongLong;
163  bool Bool;
164  char_type Char;
165  float Float;
166  double Double;
167  long double LongDouble;
168  std::basic_string<char_type> String;
169  std::chrono::time_point<std::chrono::system_clock> TimePoint;
170 
171  // vector values
172  std::vector<double> DoubleVector;
173  };
174 
176  : Type(ValueType::NONE)
177  {
178  }
179 
180  Value(int value)
181  : Type(ValueType::INT)
182  , Int(value)
183  {
184  }
185 
186  Value(unsigned value)
187  : Type(ValueType::UNSIGNED)
188  , Unsigned(value)
189  {
190  }
191 
192  Value(long long value)
193  : Type(ValueType::LONG_LONG)
194  , LongLong(value)
195  {
196  }
197 
198  Value(unsigned long long value)
199  : Type(ValueType::UNSIGNED_LONG_LONG)
200  , UnsignedLongLong(value)
201  {
202  }
203 
204  Value(bool value)
205  : Type(ValueType::BOOL)
206  , Bool(value)
207  {
208  }
209 
210  Value(char_type value)
211  : Type(ValueType::CHAR)
212  , Char(value)
213  {
214  }
215 
216  Value(float value)
217  : Type(ValueType::FLOAT)
218  , Float(value)
219  {
220  }
221 
222  Value(double value)
223  : Type(ValueType::DOUBLE)
224  , Double(value)
225  {
226  }
227 
228  Value(long double value)
229  : Type(ValueType::LONG_DOUBLE)
230  , LongDouble(value)
231  {
232  }
233 
234  Value(const char_type* value)
235  : Type(ValueType::STRING)
236  , String(value)
237  {
238  }
239 
240  Value(const std::basic_string<char_type>& value)
241  : Type(ValueType::STRING)
242  , String(value)
243  {
244  }
245 
246  Value(const std::chrono::time_point<std::chrono::system_clock>& value)
247  : Type(ValueType::TIME_POINT)
248  , TimePoint(value)
249  {
250  }
251 
252  Value(const std::vector<double>& values)
253  : Type(ValueType::DOUBLE_VECTOR)
254  , DoubleVector(values)
255  {
256  }
257 
258  Value(const Value& value)
259  {
260  this->Type = value.Type;
261  switch (value.Type)
262  {
263  case ValueType::INT:
264  this->Int = value.Int;
265  break;
266  case ValueType::UNSIGNED:
267  this->Unsigned = value.Unsigned;
268  break;
269  case ValueType::LONG_LONG:
270  this->LongLong = value.LongLong;
271  break;
272  case ValueType::UNSIGNED_LONG_LONG:
273  this->UnsignedLongLong = value.UnsignedLongLong;
274  break;
275  case ValueType::BOOL:
276  this->Bool = value.Bool;
277  break;
278  case ValueType::CHAR:
279  this->Char = value.Char;
280  break;
281  case ValueType::FLOAT:
282  this->Float = value.Float;
283  break;
284  case ValueType::DOUBLE:
285  this->Double = value.Double;
286  break;
287  case ValueType::LONG_DOUBLE:
288  this->LongDouble = value.LongDouble;
289  break;
290  case ValueType::STRING:
291  new (&this->String) std::basic_string<char_type>(value.String);
292  break;
293  case ValueType::TIME_POINT:
294  this->TimePoint = value.TimePoint;
295  break;
296  case ValueType::DOUBLE_VECTOR:
297  new (&this->DoubleVector) std::vector<double>(value.DoubleVector);
298  break;
299  default:
300  break;
301  }
302  }
303 
305  {
306  switch (this->Type)
307  {
308  case ValueType::STRING:
309  this->String.~basic_string();
310  break;
311  case ValueType::DOUBLE_VECTOR:
312  this->DoubleVector.~vector();
313  break;
314  default:
315  break;
316  }
317  }
318  };
319 
320  std::basic_string<char_type> Name;
321  Value Value;
322 
323  vtkNamedArgument() = default;
324 
325  template <typename DataType>
326  vtkNamedArgument(const std::basic_string<char_type>& name, const DataType& value)
327  : Name(name)
328  , Value(value)
329  {
330  }
331 
332  ~vtkNamedArgument() = default;
333  };
334 
338  class vtkArgumentScope
339  {
340  private:
341  std::vector<vtkNamedArgument> Arguments;
342 
343  public:
344  vtkArgumentScope() = default;
345 
346  vtkArgumentScope(const vtkArgumentScope& other)
347  {
348  this->Arguments.reserve(other.Arguments.size());
349  for (const auto& arg : other.Arguments)
350  {
351  this->Arguments.emplace_back(arg);
352  }
353  }
354 
359  template <typename T>
360  void AddArg(const fmt::detail::named_arg<char_type, T>& fmtArg)
361  {
362  bool argNotFound = std::find_if(this->Arguments.begin(), this->Arguments.end(),
363  [&fmtArg](const vtkNamedArgument& arg) {
364  return arg.Name == fmtArg.name;
365  }) == 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 warning
373  {
374  vtkLogF(WARNING, "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