1 /*========================================================================= 2 3 Program: Visualization Toolkit 4 Module: vtkLogger.h 5 6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 7 All rights reserved. 8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details. 9 10 This software is distributed WITHOUT ANY WARRANTY; without even 11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 PURPOSE. See the above copyright notice for more information. 13 14 =========================================================================*/ 15 /** 16 * @class vtkLogger 17 * @brief logging framework for use in VTK and in applications based on VTK 18 * 19 * vtkLogger acts as the entry point to VTK's logging framework. The 20 * implementation uses the loguru (https://github.com/emilk/loguru). vtkLogger 21 * provides some static API to initialize and configure logging together with a 22 * collection of macros that can be used to add items to the generated log. 23 * 24 * The logging framework is based on verbosity levels. Level 0-9 are supported 25 * in addition to named levels such as ERROR, WARNING, and INFO. When a log for 26 * a particular verbosity level is being generated, all log additions issued 27 * with verbosity level less than or equal to the requested verbosity level will 28 * get logged. 29 * 30 * When using any of the logging macros, it must be noted that unless a log output 31 * is requesting that verbosity provided (or higher), the call is a no-op and 32 * the message stream or printf-style arguments will not be evaluated. 33 * 34 * @section Setup Setup 35 * 36 * To initialize logging, in your application's `main()` you may call 37 * `vtkLogger::Init(argv, argc)`. This is totally optional but useful to 38 * time-stamp the start of the log. Furthermore, it can optionally detect 39 * verbosity level on the command line as `-v` (or any another string pass as 40 * the optional argument to `Init`) that will be used as the verbosity level for 41 * logging on to `stderr`. By default, it is set to `0` (or `INFO`) unless 42 * changed by calling `vtkLogger::SetStderrVerbosity`. 43 * 44 * In additional to logging to `stderr`, one can accumulate logs to one or more files using 45 * `vtkLogger::LogToFile`. Each log file can be given its own verbosity level. 46 * 47 * For multithreaded applications, you may want to name each of the threads so 48 * that the generated log can use human readable names for the threads. For 49 * that, use `vtkLogger::SetThreadName`. Calling `vtkLogger::Init` will set the name 50 * for the main thread. 51 * 52 * To prevent the logging framework from intercepting signals from your application, 53 * you can set the static variable `vtkLogger::EnableUnsafeSignalHandler` to `false` 54 * prior to calling `vtkLogger::Init(argc, argv)` or `vtkLogger::Init()`. 55 * 56 * @section Logging Logging 57 * 58 * vtkLogger provides several macros (again, based on `loguru`) that can be 59 * used to add the log. Both printf-style and stream-style is supported. All 60 * printf-style macros are suffixed with `F` to distinguish them from the stream 61 * macros. Another pattern in naming macros is the presence of `V` 62 * e.g. `vtkVLog` vs `vtkLog`. A macro with the `V` prefix takes a fully 63 * qualified verbosity enum e.g. `vtkLogger::VERBOSITY_INFO` or 64 * `vtkLogger::VERBOSITY_0`, while the non-`V` variant takes the verbosity 65 * name e.g. `INFO` or `0`. 66 * 67 * Following code snippet provides an overview of the available macros and their 68 * usage. 69 * 70 * @code{.cpp} 71 * 72 * // Optional, leaving this as the default value `true` will let the logging 73 * // framework log signals such as segmentation faults. 74 * 75 * vtkLogger::EnableUnsafeSignalHandler = false; 76 * 77 * // Optional, but useful to time-stamp the start of the log. 78 * // Will also detect verbosity level on the command line as -v. 79 * 80 * vtkLogger::Init(argc, argv); 81 * 82 * // Put every log message in "everything.log": 83 * vtkLogger::LogToFile("everything.log", vtkLogger::APPEND, vtkLogger::VERBOSITY_MAX); 84 * 85 * // Only log INFO, WARNING, ERROR to "latest_readable.log": 86 * vtkLogger::LogToFile("latest_readable.log", vtkLogger::TRUNCATE, vtkLogger::VERBOSITY_INFO); 87 * 88 * // Only show most relevant things on stderr: 89 * vtkLogger::SetStderrVerbosity(vtkLogger::VERBOSITY_1); 90 * 91 * // add a line to log using the verbosity name. 92 * vtkLogF(INFO, "I'm hungry for some %.3f!", 3.14159); 93 * vtkLogF(0, "same deal"); 94 * 95 * // add a line to log using the verbosity enum. 96 * vtkVLogF(vtkLogger::VERBOSITY_INFO, "I'm hungry for some %.3f!", 3.14159); 97 * vtkVLogF(vtkLogger::VERBOSITY_0, "same deal"); 98 * 99 * // to add an identifier for a vtkObjectBase or subclass 100 * vtkLogF(INFO, "The object is %s", vtkLogIdentifier(vtkobject)); 101 * 102 * // add a line conditionally to log if the condition succeeds: 103 * vtkLogIfF(INFO, ptr == nullptr, "ptr is nullptr (some number: %.3f)", * 3.14159); 104 * 105 * vtkLogScopeF(INFO, "Will indent all log messages within this scope."); 106 * // in a function, you may use vtkLogScopeFunction(INFO) 107 * 108 * // scope can be explicitly started and closed by vtkLogStartScope (or 109 * // vtkLogStartScopef) and vtkLogEndScope 110 * vtkLogStartScope(INFO, "id-used-as-message"); 111 * vtkLogStartScopeF(INFO, "id", "message-%d", 1); 112 * vtkLogEndScope("id"); 113 * vtkLogEndScope("id-used-as-message"); 114 * 115 * // alternatively, you can use streams instead of printf-style 116 * vtkLog(INFO, "I'm hungry for some " << 3.14159 << "!"); 117 * vtkLogIF(INFO, ptr == nullptr, "ptr is " << "nullptr"); 118 * 119 * @endcode 120 * 121 * @section LoggingAndLegacyMacros Logging and VTK error macros 122 * 123 * VTK has long supported multiple macros to report errors, warnings and debug 124 * messages through `vtkErrorMacro`, `vtkWarningMacro`, `vtkDebugMacro`, etc. 125 * In addition to performing the traditional message reporting via 126 * `vtkOutputWindow`, these macros also log to the logging sub-system with 127 * appropriate verbosity levels. 128 * 129 * To avoid the vtkLogger and vtkOutputWindow both posting the message to the 130 * standard output streams, vtkOutputWindow now supports an ability to specify 131 * terminal display mode, via `vtkOutputWindow::SetDisplayMode`. If display mode 132 * is `vtkOutputWindow::DEFAULT` then the output window will not 133 * post messages originating from the standard error/warning/debug macros to the 134 * standard output if VTK is built with logging support. If VTK is not built 135 * with logging support, then vtkOutputWindow will post the messages to the 136 * standard output streams, unless disabled explicitly. 137 * 138 * @section Callbacks Custom callbacks/handlers for log messages 139 * 140 * vtkLogger supports ability to register callbacks to call on each logged 141 * message. This is useful to show the messages in application specific 142 * viewports, e.g. a special message widget. 143 * 144 * To register a callback use `vtkLogger::AddCallback` and to remove a callback 145 * use `vtkLogger::RemoveCallback` with the id provided when registering the 146 * callback. 147 * 148 */ 149 150 #ifndef vtkLogger_h 151 #define vtkLogger_h 152 153 #include "vtkObjectBase.h" 154 #include "vtkSetGet.h" // needed for macros 155 156 #include <string> // needed for std::string 157 158 #if defined(_MSC_VER) 159 #include <sal.h> // Needed for _In_z_ etc annotations 160 #endif 161 162 // this is copied from `loguru.hpp` 163 #if defined(__clang__) || defined(__GNUC__) 164 // Helper macro for declaring functions as having similar signature to printf. 165 // This allows the compiler to catch format errors at compile-time. 166 #define VTK_PRINTF_LIKE(fmtarg, firstvararg) \ 167 __attribute__((__format__(__printf__, fmtarg, firstvararg))) 168 #define VTK_FORMAT_STRING_TYPE const char* 169 #elif defined(_MSC_VER) 170 #define VTK_PRINTF_LIKE(fmtarg, firstvararg) 171 #define VTK_FORMAT_STRING_TYPE _In_z_ _Printf_format_string_ const char* 172 #else 173 #define VTK_PRINTF_LIKE(fmtarg, firstvararg) 174 #define VTK_FORMAT_STRING_TYPE const char* 175 #endif 176 177 class VTKCOMMONCORE_EXPORT vtkLogger : public vtkObjectBase 178 { 179 public: 180 vtkBaseTypeMacro(vtkLogger, vtkObjectBase); 181 void PrintSelf(ostream& os, vtkIndent indent) override; 182 183 enum Verbosity 184 { 185 // Used to mark an invalid verbosity. Do not log to this level. 186 VERBOSITY_INVALID = -10, // Never do LOG_F(INVALID) 187 188 // You may use VERBOSITY_OFF on g_stderr_verbosity, but for nothing else! 189 VERBOSITY_OFF = -9, // Never do LOG_F(OFF) 190 191 VERBOSITY_ERROR = -2, 192 VERBOSITY_WARNING = -1, 193 194 // Normal messages. By default written to stderr. 195 VERBOSITY_INFO = 0, 196 197 // Same as VERBOSITY_INFO in every way. 198 VERBOSITY_0 = 0, 199 200 // Verbosity levels 1-9 are generally not written to stderr, but are written to file. 201 VERBOSITY_1 = +1, 202 VERBOSITY_2 = +2, 203 VERBOSITY_3 = +3, 204 VERBOSITY_4 = +4, 205 VERBOSITY_5 = +5, 206 VERBOSITY_6 = +6, 207 VERBOSITY_7 = +7, 208 VERBOSITY_8 = +8, 209 VERBOSITY_9 = +9, 210 211 // trace level, same as VERBOSITY_9 212 VERBOSITY_TRACE = +9, 213 214 // Don not use higher verbosity levels, as that will make grepping log files harder. 215 VERBOSITY_MAX = +9, 216 }; 217 218 /** 219 * Initializes logging. This should be called from the main thread, if at all. 220 * Your application doesn't *need* to call this, but if you do: 221 * * signal handlers are installed 222 * * program arguments are logged 223 * * working directory is logged 224 * * optional -v verbosity flag is parsed 225 * * main thread name is set to "main thread" 226 * * explanation of the preamble (date, threadname, etc.) is logged. 227 * 228 * This method will look for arguments meant for logging subsystem and remove 229 * them. Arguments meant for logging subsystem are: 230 * 231 * -v n Set stderr logging verbosity. Examples 232 * -v 3 Show verbosity level 3 and lower. 233 * -v 0 Only show INFO, WARNING, ERROR, FATAL (default). 234 * -v INFO Only show INFO, WARNING, ERROR, FATAL (default). 235 * -v WARNING Only show WARNING, ERROR, FATAL. 236 * -v ERROR Only show ERROR, FATAL. 237 * -v FATAL Only show FATAL. 238 * -v OFF Turn off logging to stderr. 239 * 240 * You can set the default logging verbosity programmatically by calling 241 * `vtkLogger::SetStderrVerbosity` before calling `vtkLogger::Init`. That 242 * way, you can specify a default that the user can override using command 243 * line arguments. Note that this does not affect file logging. 244 * 245 * You can also use something else instead of '-v' flag by the via 246 * `verbosity_flag` argument. You can also set to nullptr to skip parsing 247 * verbosity level from the command line arguments. 248 * 249 * For applications that do not want loguru to handle any signals, i.e., 250 * print a stack trace when a signal is intercepted, the 251 * `vtkLogger::EnableUnsafeSignalHandler` static member variable 252 * should be set to `false`. 253 * @{ 254 */ 255 static void Init(int& argc, char* argv[], const char* verbosity_flag = "-v"); 256 static void Init(); 257 /** @} */ 258 259 /** 260 * Set the verbosity level for the output logged to stderr. Everything with a 261 * verbosity equal or less than the level specified will be written to 262 * stderr. Set to `VERBOSITY_OFF` to write nothing to stderr. 263 * Default is 0. 264 */ 265 static void SetStderrVerbosity(Verbosity level); 266 267 /** 268 * Set internal messages verbosity level. The library used by VTK, `loguru` 269 * generates log messages during initialization and at exit. These are logged 270 * as log level VERBOSITY_1, by default. One can change that using this 271 * method. Typically, you want to call this before `vtkLogger::Init`. 272 */ 273 static void SetInternalVerbosityLevel(Verbosity level); 274 275 /** 276 * Support log file modes: `TRUNCATE` truncates the file clearing any existing 277 * contents while `APPEND` appends to the existing log file contents, if any. 278 */ 279 enum FileMode 280 { 281 TRUNCATE, 282 APPEND 283 }; 284 285 /** 286 * Enable logging to a file at the given path. 287 * Any logging message with verbosity lower or equal to the given verbosity 288 * will be included. This method will create all directories in the 'path' if 289 * needed. To stop the file logging, call `EndLogToFile` with the same path. 290 */ 291 static void LogToFile(const char* path, FileMode filemode, Verbosity verbosity); 292 293 /** 294 * Stop logging to a file at the given path. 295 */ 296 static void EndLogToFile(const char* path); 297 298 ///@{ 299 /** 300 * Get/Set the name to identify the current thread in the log output. 301 */ 302 static void SetThreadName(const std::string& name); 303 static std::string GetThreadName(); 304 ///@} 305 306 /** 307 * Returns a printable string for a vtkObjectBase instance. 308 */ 309 static std::string GetIdentifier(vtkObjectBase* obj); 310 311 /** 312 * The message structure that is passed to custom callbacks registered using 313 * `vtkLogger::AddCallback`. 314 */ 315 struct Message 316 { 317 // You would generally print a Message by just concatenating the buffers without spacing. 318 // Optionally, ignore preamble and indentation. 319 Verbosity verbosity; // Already part of preamble 320 const char* filename; // Already part of preamble 321 unsigned line; // Already part of preamble 322 const char* preamble; // Date, time, uptime, thread, file:line, verbosity. 323 const char* indentation; // Just a bunch of spacing. 324 const char* prefix; // Assertion failure info goes here (or ""). 325 const char* message; // User message goes here. 326 }; 327 328 ///@{ 329 /** 330 * Callback handle types. 331 */ 332 using LogHandlerCallbackT = void (*)(void* user_data, const Message& message); 333 using CloseHandlerCallbackT = void (*)(void* user_data); 334 using FlushHandlerCallbackT = void (*)(void* user_data); 335 ///@} 336 337 /** 338 * Add a callback to call on each log message with a verbosity less or equal 339 * to the given one. Useful for displaying messages in an application output 340 * window, for example. The given `on_close` is also expected to flush (if 341 * desired). 342 * 343 * Note that if logging is disabled at compile time, then these callback will 344 * never be called. 345 */ 346 static void AddCallback(const char* id, LogHandlerCallbackT callback, void* user_data, 347 Verbosity verbosity, CloseHandlerCallbackT on_close = nullptr, 348 FlushHandlerCallbackT on_flush = nullptr); 349 350 /** 351 * Remove a callback using the id specified. 352 * Returns true if and only if the callback was found (and removed). 353 */ 354 static bool RemoveCallback(const char* id); 355 356 /** 357 * Returns true if VTK is built with logging support enabled. 358 */ 359 static bool IsEnabled(); 360 361 /** 362 * Returns the maximum verbosity of all log outputs. A log item for a 363 * verbosity higher than this will not be generated in any of the currently 364 * active outputs. 365 */ 366 static Verbosity GetCurrentVerbosityCutoff(); 367 368 /** 369 * Convenience function to convert an integer to matching verbosity level. If 370 * val is less than or equal to vtkLogger::VERBOSITY_INVALID, then 371 * vtkLogger::VERBOSITY_INVALID is returned. If value is greater than 372 * vtkLogger::VERBOSITY_MAX, then vtkLogger::VERBOSITY_MAX is returned. 373 */ 374 static Verbosity ConvertToVerbosity(int value); 375 376 /** 377 * Convenience function to convert a string to matching verbosity level. 378 * vtkLogger::VERBOSITY_INVALID will be return for invalid strings. 379 * Accepted string values are OFF, ERROR, WARNING, INFO, TRACE, MAX, INVALID or ASCII 380 * representation for an integer in the range [-9,9]. 381 */ 382 static Verbosity ConvertToVerbosity(const char* text); 383 384 ///@{ 385 /** 386 * @internal 387 * 388 * Not intended for public use, please use the logging macros instead. 389 */ 390 static void Log( 391 Verbosity verbosity, VTK_FILEPATH const char* fname, unsigned int lineno, const char* txt); 392 static void StartScope( 393 Verbosity verbosity, const char* id, VTK_FILEPATH const char* fname, unsigned int lineno); 394 static void EndScope(const char* id); 395 #if !defined(__WRAP__) 396 static void LogF(Verbosity verbosity, VTK_FILEPATH const char* fname, unsigned int lineno, 397 VTK_FORMAT_STRING_TYPE format, ...) VTK_PRINTF_LIKE(4, 5); 398 static void StartScopeF(Verbosity verbosity, const char* id, VTK_FILEPATH const char* fname, 399 unsigned int lineno, VTK_FORMAT_STRING_TYPE format, ...) VTK_PRINTF_LIKE(5, 6); 400 401 class VTKCOMMONCORE_EXPORT LogScopeRAII 402 { 403 public: 404 LogScopeRAII(); 405 LogScopeRAII(vtkLogger::Verbosity verbosity, const char* fname, unsigned int lineno, 406 VTK_FORMAT_STRING_TYPE format, ...) VTK_PRINTF_LIKE(5, 6); 407 ~LogScopeRAII(); 408 #if defined(_MSC_VER) && _MSC_VER > 1800 409 // see loguru.hpp for the reason why this is needed on MSVC LogScopeRAII(LogScopeRAII && other)410 LogScopeRAII(LogScopeRAII&& other) 411 : Internals(other.Internals) 412 { 413 other.Internals = nullptr; 414 } 415 #else 416 LogScopeRAII(LogScopeRAII&&) = default; 417 #endif 418 419 private: 420 LogScopeRAII(const LogScopeRAII&) = delete; 421 void operator=(const LogScopeRAII&) = delete; 422 class LSInternals; 423 LSInternals* Internals; 424 }; 425 #endif 426 ///@} 427 428 /** 429 * Flag to enable/disable the logging frameworks printing of a stack trace 430 * when catching signals, which could lead to crashes and deadlocks in 431 * certain circumstances. 432 */ 433 static bool EnableUnsafeSignalHandler; 434 435 protected: 436 vtkLogger(); 437 ~vtkLogger() override; 438 439 private: 440 vtkLogger(const vtkLogger&) = delete; 441 void operator=(const vtkLogger&) = delete; 442 static vtkLogger::Verbosity InternalVerbosityLevel; 443 static std::string ThreadName; 444 }; 445 446 ///@{ 447 /** 448 * Add to log given the verbosity level. 449 * The text will be logged when the log verbosity is set to the specified level 450 * or higher. 451 * 452 * // using printf-style 453 * vtkLogF(INFO, "Hello %s", "world!"); 454 * vtkVLogF(vtkLogger::VERBOSITY_INFO, "Hello %s", "world!"); 455 * 456 * // using streams 457 * vtkLog(INFO, "Hello " << "world!"); 458 * vtkVLog(vtkLogger::VERBOSITY_INFO, << "Hello world!"); 459 * 460 */ 461 #define vtkVLogF(level, ...) \ 462 ((level) > vtkLogger::GetCurrentVerbosityCutoff()) \ 463 ? (void)0 \ 464 : vtkLogger::LogF(level, __FILE__, __LINE__, __VA_ARGS__) 465 #define vtkLogF(verbosity_name, ...) vtkVLogF(vtkLogger::VERBOSITY_##verbosity_name, __VA_ARGS__) 466 #define vtkVLog(level, x) \ 467 if ((level) <= vtkLogger::GetCurrentVerbosityCutoff()) \ 468 { \ 469 vtkOStrStreamWrapper::EndlType endl; \ 470 vtkOStrStreamWrapper::UseEndl(endl); \ 471 vtkOStrStreamWrapper vtkmsg; \ 472 vtkmsg << "" x; \ 473 vtkLogger::Log(level, __FILE__, __LINE__, vtkmsg.str()); \ 474 vtkmsg.rdbuf()->freeze(0); \ 475 } 476 #define vtkLog(verbosity_name, x) vtkVLog(vtkLogger::VERBOSITY_##verbosity_name, x) 477 ///@} 478 479 ///@{ 480 /** 481 * Add to log only when the `cond` passes. 482 * 483 * // using printf-style 484 * vtkLogIfF(ERROR, ptr == nullptr, "`ptr` cannot be null!"); 485 * vtkVLogIfF(vtkLogger::VERBOSITY_ERROR, ptr == nullptr, "`ptr` cannot be null!"); 486 * 487 * // using streams 488 * vtkLogIf(ERROR, ptr == nullptr, "`ptr` cannot be null!"); 489 * vtkVLogIf(vtkLogger::VERBOSITY_ERROR, ptr == nullptr, << "`ptr` cannot be null!"); 490 * 491 */ 492 #define vtkVLogIfF(level, cond, ...) \ 493 ((level) > vtkLogger::GetCurrentVerbosityCutoff() || (cond) == false) \ 494 ? (void)0 \ 495 : vtkLogger::LogF(level, __FILE__, __LINE__, __VA_ARGS__) 496 497 #define vtkLogIfF(verbosity_name, cond, ...) \ 498 vtkVLogIfF(vtkLogger::VERBOSITY_##verbosity_name, cond, __VA_ARGS__) 499 500 #define vtkVLogIf(level, cond, x) \ 501 if ((level) <= vtkLogger::GetCurrentVerbosityCutoff() && (cond)) \ 502 { \ 503 vtkOStrStreamWrapper::EndlType endl; \ 504 vtkOStrStreamWrapper::UseEndl(endl); \ 505 vtkOStrStreamWrapper vtkmsg; \ 506 vtkmsg << "" x; \ 507 vtkLogger::Log(level, __FILE__, __LINE__, vtkmsg.str()); \ 508 vtkmsg.rdbuf()->freeze(0); \ 509 } 510 #define vtkLogIf(verbosity_name, cond, x) vtkVLogIf(vtkLogger::VERBOSITY_##verbosity_name, cond, x) 511 ///@} 512 513 #define VTKLOG_CONCAT_IMPL(s1, s2) s1##s2 514 #define VTKLOG_CONCAT(s1, s2) VTKLOG_CONCAT_IMPL(s1, s2) 515 #define VTKLOG_ANONYMOUS_VARIABLE(x) VTKLOG_CONCAT(x, __LINE__) 516 517 #define vtkVLogScopeF(level, ...) \ 518 auto VTKLOG_ANONYMOUS_VARIABLE(msg_context) = ((level) > vtkLogger::GetCurrentVerbosityCutoff()) \ 519 ? vtkLogger::LogScopeRAII() \ 520 : vtkLogger::LogScopeRAII(level, __FILE__, __LINE__, __VA_ARGS__) 521 522 #define vtkLogScopeF(verbosity_name, ...) \ 523 vtkVLogScopeF(vtkLogger::VERBOSITY_##verbosity_name, __VA_ARGS__) 524 525 #define vtkLogScopeFunction(verbosity_name) vtkLogScopeF(verbosity_name, "%s", __func__) 526 #define vtkVLogScopeFunction(level) vtkVLogScopeF(level, "%s", __func__) 527 528 ///@{ 529 /** 530 * Explicitly mark start and end of log scope. This is useful in cases where the 531 * start and end of the scope does not happen within the same C++ scope. 532 */ 533 #define vtkLogStartScope(verbosity_name, id) \ 534 vtkLogger::StartScope(vtkLogger::VERBOSITY_##verbosity_name, id, __FILE__, __LINE__) 535 #define vtkLogEndScope(id) vtkLogger::EndScope(id) 536 537 #define vtkLogStartScopeF(verbosity_name, id, ...) \ 538 vtkLogger::StartScopeF(vtkLogger::VERBOSITY_##verbosity_name, id, __FILE__, __LINE__, __VA_ARGS__) 539 540 #define vtkVLogStartScope(level, id) vtkLogger::StartScope(level, id, __FILE__, __LINE__) 541 #define vtkVLogStartScopeF(level, id, ...) \ 542 vtkLogger::StartScopeF(level, id, __FILE__, __LINE__, __VA_ARGS__) 543 ///@} 544 545 /** 546 * Convenience macro to generate an identifier string for any vtkObjectBase subclass. 547 * @note do not store the returned value as it returns a char* pointer to a 548 * temporary std::string that will be released as soon as it goes out of scope. 549 */ 550 #define vtkLogIdentifier(vtkobject) vtkLogger::GetIdentifier(vtkobject).c_str() 551 552 #endif 553