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" // Needed for vtkLog
17 #include "vtkObject.h"
18 #include "vtkPVVTKExtensionsCoreModule.h" // Needed for export macro
19 #include "vtkStringFormatter.h" // Needed for fmt
20 
21 #include <algorithm>
22 #include <memory>
23 #include <sstream>
24 #include <stack>
25 
27 {
28 public:
29  static vtkPVStringFormatter* New();
31  void PrintSelf(ostream& os, vtkIndent indent) override;
32 
37  class TraceScope
38  {
39  public:
40  template <typename... Args>
41  TraceScope(Args&&... args)
42  {
44  }
45 
46  template <typename... Args>
47  TraceScope(const char* scopeName, Args&&... args)
48  {
49  vtkPVStringFormatter::PushScope(scopeName, args...);
50  }
51 
53  };
54 
59  template <typename... Args>
60  static void PushScope(Args&&... args)
61  {
62  std::shared_ptr<vtkArgumentScope> newScope;
63 
64  if (vtkPVStringFormatter::ScopeStack.empty()) // check if stack is empty
65  {
66  newScope = std::make_shared<vtkArgumentScope>();
67  }
68  else // else use the top scope as a baseline for the new scope
69  {
70  newScope = std::make_shared<vtkArgumentScope>(*vtkPVStringFormatter::ScopeStack.top());
71  }
72  vtkPVStringFormatter::Push(*newScope, args...);
73  vtkPVStringFormatter::ScopeStack.push(newScope);
74  }
75 
86  template <typename... Args>
87  static void PushScope(const char* scopeName, Args&&... args)
88  {
89  std::shared_ptr<vtkArgumentScope> newScope;
90 
91  if (vtkPVStringFormatter::ScopeStack.empty()) // check if stack is empty
92  {
93  newScope = std::make_shared<vtkArgumentScope>();
94  }
95  else // else use the top scope as a baseline for the new scope
96  {
97  newScope = std::make_shared<vtkArgumentScope>(*vtkPVStringFormatter::ScopeStack.top());
98  }
99  vtkPVStringFormatter::Push(*newScope, scopeName, args...);
100  vtkPVStringFormatter::ScopeStack.push(newScope);
101  }
102 
106  static void PopScope();
107 
112  static std::string Format(const std::string& formattableString);
113 
114 protected:
116  ~vtkPVStringFormatter() override;
117 
118 private:
120  void operator=(const vtkPVStringFormatter&) = delete;
121 
122  using char_type = fmt::format_context::char_type;
123 
127  struct vtkNamedArgument
128  {
129  enum class ValueType
130  {
131  // single values
132  NONE,
133  INT,
134  UNSIGNED,
135  LONG_LONG,
136  UNSIGNED_LONG_LONG,
137  BOOL,
138  CHAR,
139  FLOAT,
140  DOUBLE,
141  LONG_DOUBLE,
142  STRING,
143  TIME_POINT,
144  // vector values
145  DOUBLE_VECTOR
146  };
147  struct Value
148  {
149  ValueType Type;
150  union
151  {
152  // single values
153  int Int;
154  unsigned Unsigned;
155  long long LongLong;
156  unsigned long long UnsignedLongLong;
157  bool Bool;
158  char_type Char;
159  float Float;
160  double Double;
161  long double LongDouble;
162  std::basic_string<char_type> String;
163  std::chrono::time_point<std::chrono::system_clock> TimePoint;
164 
165  // vector values
166  std::vector<double> DoubleVector;
167  };
168 
170  : Type(ValueType::NONE)
171  {
172  }
173 
174  Value(int value)
175  : Type(ValueType::INT)
176  , Int(value)
177  {
178  }
179 
180  Value(unsigned value)
181  : Type(ValueType::UNSIGNED)
182  , Unsigned(value)
183  {
184  }
185 
186  Value(long long value)
187  : Type(ValueType::LONG_LONG)
188  , LongLong(value)
189  {
190  }
191 
192  Value(unsigned long long value)
193  : Type(ValueType::UNSIGNED_LONG_LONG)
194  , UnsignedLongLong(value)
195  {
196  }
197 
198  Value(bool value)
199  : Type(ValueType::BOOL)
200  , Bool(value)
201  {
202  }
203 
204  Value(char_type value)
205  : Type(ValueType::CHAR)
206  , Char(value)
207  {
208  }
209 
210  Value(float value)
211  : Type(ValueType::FLOAT)
212  , Float(value)
213  {
214  }
215 
216  Value(double value)
217  : Type(ValueType::DOUBLE)
218  , Double(value)
219  {
220  }
221 
222  Value(long double value)
223  : Type(ValueType::LONG_DOUBLE)
224  , LongDouble(value)
225  {
226  }
227 
228  Value(const char_type* value)
229  : Type(ValueType::STRING)
230  , String(value)
231  {
232  }
233 
234  Value(const std::basic_string<char_type>& value)
235  : Type(ValueType::STRING)
236  , String(value)
237  {
238  }
239 
240  Value(const std::chrono::time_point<std::chrono::system_clock>& value)
241  : Type(ValueType::TIME_POINT)
242  , TimePoint(value)
243  {
244  }
245 
246  Value(const std::vector<double>& values)
247  : Type(ValueType::DOUBLE_VECTOR)
248  , DoubleVector(values)
249  {
250  }
251 
252  Value(const Value& value)
253  {
254  this->Type = value.Type;
255  switch (value.Type)
256  {
257  case ValueType::INT:
258  this->Int = value.Int;
259  break;
260  case ValueType::UNSIGNED:
261  this->Unsigned = value.Unsigned;
262  break;
263  case ValueType::LONG_LONG:
264  this->LongLong = value.LongLong;
265  break;
266  case ValueType::UNSIGNED_LONG_LONG:
267  this->UnsignedLongLong = value.UnsignedLongLong;
268  break;
269  case ValueType::BOOL:
270  this->Bool = value.Bool;
271  break;
272  case ValueType::CHAR:
273  this->Char = value.Char;
274  break;
275  case ValueType::FLOAT:
276  this->Float = value.Float;
277  break;
278  case ValueType::DOUBLE:
279  this->Double = value.Double;
280  break;
281  case ValueType::LONG_DOUBLE:
282  this->LongDouble = value.LongDouble;
283  break;
284  case ValueType::STRING:
285  new (&this->String) std::basic_string<char_type>(value.String);
286  break;
287  case ValueType::TIME_POINT:
288  this->TimePoint = value.TimePoint;
289  break;
290  case ValueType::DOUBLE_VECTOR:
291  new (&this->DoubleVector) std::vector<double>(value.DoubleVector);
292  break;
293  default:
294  break;
295  }
296  }
297 
299  {
300  switch (this->Type)
301  {
302  case ValueType::STRING:
303  this->String.~basic_string();
304  break;
305  case ValueType::DOUBLE_VECTOR:
306  this->DoubleVector.~vector();
307  break;
308  default:
309  break;
310  }
311  }
312  };
313 
314  std::basic_string<char_type> Name;
315  Value Value;
316 
317  vtkNamedArgument() = default;
318 
319  template <typename DataType>
320  vtkNamedArgument(const std::basic_string<char_type>& name, const DataType& value)
321  : Name(name)
322  , Value(value)
323  {
324  }
325 
326  ~vtkNamedArgument() = default;
327  };
328 
332  class vtkArgumentScope
333  {
334  private:
335  std::vector<vtkNamedArgument> Arguments;
336 
337  public:
338  vtkArgumentScope() = default;
339 
340  vtkArgumentScope(const vtkArgumentScope& other)
341  {
342  this->Arguments.reserve(other.Arguments.size());
343  for (const auto& arg : other.Arguments)
344  {
345  this->Arguments.emplace_back(arg);
346  }
347  }
348 
353  template <typename T>
354 #if FMT_VERSION >= 120200
355  void AddArg(const fmt::named_arg<T, char_type>& fmtArg)
356 #else
357  void AddArg(const fmt::detail::named_arg<char_type, T>& fmtArg)
358 #endif
359  {
360  bool argNotFound = std::find_if(this->Arguments.begin(), this->Arguments.end(),
361  [&fmtArg](const vtkNamedArgument& arg)
362  { return arg.Name == fmtArg.name; }) == this->Arguments.end();
363  // if argument was not found
364  if (argNotFound)
365  {
366  vtkNamedArgument newArg(fmtArg.name, fmtArg.value);
367  this->Arguments.push_back(newArg);
368  }
369  else // else print message
370  {
371  vtkLogF(TRACE, "Argument %s already exists. Try to add another one.", fmtArg.name);
372  }
373  }
374 
378  std::basic_string<char_type> GetArgInfo() const
379  {
380  std::basic_stringstream<char_type> argInfo;
381  for (const auto& arg : this->Arguments)
382  {
383  argInfo << "\tName: " << arg.Name;
384  argInfo << "\tType: ";
385  switch (arg.Value.Type)
386  {
387  case vtkNamedArgument::ValueType::INT:
388  argInfo << "int";
389  break;
390  case vtkNamedArgument::ValueType::UNSIGNED:
391  argInfo << "unsigned";
392  break;
393  case vtkNamedArgument::ValueType::LONG_LONG:
394  argInfo << "long long";
395  break;
396  case vtkNamedArgument::ValueType::UNSIGNED_LONG_LONG:
397  argInfo << "unsigned long long";
398  break;
399  case vtkNamedArgument::ValueType::BOOL:
400  argInfo << "bool";
401  break;
402  case vtkNamedArgument::ValueType::CHAR:
403  argInfo << "char";
404  break;
406  argInfo << "float";
407  break;
408  case vtkNamedArgument::ValueType::DOUBLE:
409  argInfo << "double";
410  break;
411  case vtkNamedArgument::ValueType::LONG_DOUBLE:
412  argInfo << "long double";
413  break;
414  case vtkNamedArgument::ValueType::STRING:
415  argInfo << "std::string";
416  break;
417  case vtkNamedArgument::ValueType::TIME_POINT:
418  argInfo << "std::chrono::time_point<std::chrono::system_clock>";
419  break;
420  case vtkNamedArgument::ValueType::DOUBLE_VECTOR:
421  argInfo << "std::vector<double>";
422  break;
423  default:
424  argInfo << "unknown";
425  break;
426  }
427  argInfo << "\n";
428  }
429  return argInfo.str();
430  }
431 
435  fmt::dynamic_format_arg_store<fmt::format_context> GetArgs() const
436  {
437  fmt::dynamic_format_arg_store<fmt::format_context> args;
438  for (const auto& arg : this->Arguments)
439  {
440  switch (arg.Value.Type)
441  {
442  case vtkNamedArgument::ValueType::INT:
443  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Int));
444  break;
445  case vtkNamedArgument::ValueType::UNSIGNED:
446  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Unsigned));
447  break;
448  case vtkNamedArgument::ValueType::LONG_LONG:
449  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.LongLong));
450  break;
451  case vtkNamedArgument::ValueType::UNSIGNED_LONG_LONG:
452  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.UnsignedLongLong));
453  break;
454  case vtkNamedArgument::ValueType::BOOL:
455  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Bool));
456  break;
457  case vtkNamedArgument::ValueType::CHAR:
458  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Char));
459  break;
461  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Float));
462  break;
463  case vtkNamedArgument::ValueType::DOUBLE:
464  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.Double));
465  break;
466  case vtkNamedArgument::ValueType::LONG_DOUBLE:
467  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.LongDouble));
468  break;
469  case vtkNamedArgument::ValueType::STRING:
470  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.String));
471  break;
472  case vtkNamedArgument::ValueType::TIME_POINT:
473  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.TimePoint));
474  break;
475  case vtkNamedArgument::ValueType::DOUBLE_VECTOR:
476  args.push_back(fmt::arg(arg.Name.c_str(), arg.Value.DoubleVector));
477  break;
478  default:
479  break;
480  }
481  }
482  return args;
483  }
484 
488  void clear() { this->Arguments.clear(); }
489  };
490 
494  static std::string GetArgInfo();
495 
499  static void Push(vtkArgumentScope& vtkNotUsed(scope)) {}
500 
504  template <typename T0, typename... TArgs>
505  static void Push(vtkArgumentScope& scope, T0& arg0, TArgs&... args)
506  {
507  scope.AddArg(arg0);
508  vtkPVStringFormatter::Push(scope, args...);
509  }
510 
514  static void Push(vtkArgumentScope& vtkNotUsed(scope), const char* vtkNotUsed(scopeName)) {}
515 
519  template <typename T0, typename... TArgs>
520  static void Push(vtkArgumentScope& scope, const char* scopeName, T0& arg0, TArgs&... args)
521  {
522  auto scopeBasedArgName = std::string(scopeName) + "_" + arg0.name;
523  scope.AddArg(fmt::arg(scopeBasedArgName.c_str(), arg0.value));
524  scope.AddArg(arg0);
525  vtkPVStringFormatter::Push(scope, scopeName, args...);
526  }
527 
528  static std::stack<std::shared_ptr<vtkArgumentScope>> ScopeStack;
529 };
530 
531 #define PV_STRING_FORMATTER_SCOPE_0(x, y) x##y
532 #define PV_STRING_FORMATTER_SCOPE_1(x, y) PV_STRING_FORMATTER_SCOPE_0(x, y)
533 #define PV_STRING_FORMATTER_SCOPE(...) \
534  vtkPVStringFormatter::TraceScope PV_STRING_FORMATTER_SCOPE_1(_trace_item, __LINE__)(__VA_ARGS__)
535 #define PV_STRING_FORMATTER_NAMED_SCOPE(NAME, ...) \
536  vtkPVStringFormatter::TraceScope PV_STRING_FORMATTER_SCOPE_1(_trace_item, __LINE__)( \
537  NAME, __VA_ARGS__)
538 
539 #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