1 /* $Id: CoinMessageHandler.hpp 2083 2019-01-06 19:38:09Z unxusr $ */ 2 // Copyright (C) 2002, International Business Machines 3 // Corporation and others. All Rights Reserved. 4 // This code is licensed under the terms of the Eclipse Public License (EPL). 5 6 #ifndef CoinMessageHandler_H 7 #define CoinMessageHandler_H 8 9 #include "CoinUtilsConfig.h" 10 #include "CoinPragma.hpp" 11 #include "CoinTypes.hpp" 12 13 #include <iostream> 14 #include <cstdio> 15 #include <string> 16 #include <vector> 17 18 /** \file CoinMessageHandler.hpp 19 \brief This is a first attempt at a message handler. 20 21 The COIN Project is in favo(u)r of multi-language support. This implementation 22 of a message handler tries to make it as lightweight as possible in the sense 23 that only a subset of messages need to be defined --- the rest default to US 24 English. 25 26 The default handler at present just prints to stdout or to a FILE pointer 27 28 \todo 29 This needs to be worked over for correct operation with ISO character codes. 30 */ 31 32 /* 33 I (jjf) am strongly in favo(u)r of language support for an open 34 source project, but I have tried to make it as lightweight as 35 possible in the sense that only a subset of messages need to be 36 defined - the rest default to US English. There will be different 37 sets of messages for each component - so at present there is a 38 Clp component and a Coin component. 39 40 Because messages are only used in a controlled environment and have no 41 impact on code and are tested by other tests I have included tests such 42 as language and derivation in other unit tests. 43 */ 44 /* 45 Where there are derived classes I (jjf) have started message numbers at 1001. 46 */ 47 48 /** \brief Class for one massaged message. 49 50 A message consists of a text string with formatting codes (#message_), 51 an integer identifier (#externalNumber_) which also determines the severity 52 level (#severity_) of the message, and a detail (logging) level (#detail_). 53 54 CoinOneMessage is just a container to hold this information. The 55 interpretation is set by CoinMessageHandler, which see. 56 */ 57 58 class CoinOneMessage { 59 60 public: 61 /**@name Constructors etc */ 62 //@{ 63 /** Default constructor. */ 64 CoinOneMessage(); 65 /** Normal constructor */ 66 CoinOneMessage(int externalNumber, char detail, 67 const char *message); 68 /** Destructor */ 69 ~CoinOneMessage(); 70 /** The copy constructor */ 71 CoinOneMessage(const CoinOneMessage &); 72 /** assignment operator. */ 73 CoinOneMessage &operator=(const CoinOneMessage &); 74 //@} 75 76 /**@name Useful stuff */ 77 //@{ 78 /// Replace message text (<i>e.g.</i>, text in a different language) 79 void replaceMessage(const char *message); 80 //@} 81 82 /**@name Get and set methods */ 83 //@{ 84 /** Get message ID number */ externalNumber() const85 inline int externalNumber() const 86 { 87 return externalNumber_; 88 } 89 /** \brief Set message ID number 90 91 In the default CoinMessageHandler, this number is printed in the message 92 prefix and is used to determine the message severity level. 93 */ setExternalNumber(int number)94 inline void setExternalNumber(int number) 95 { 96 externalNumber_ = number; 97 } 98 /// Severity severity() const99 inline char severity() const 100 { 101 return severity_; 102 } 103 /// Set detail level setDetail(int level)104 inline void setDetail(int level) 105 { 106 detail_ = static_cast< char >(level); 107 } 108 /// Get detail level detail() const109 inline int detail() const 110 { 111 return detail_; 112 } 113 /// Return the message text message() const114 inline char *message() const 115 { 116 return message_; 117 } 118 //@} 119 120 /**@name member data */ 121 //@{ 122 /// number to print out (also determines severity) 123 int externalNumber_; 124 /// Will only print if detail matches 125 char detail_; 126 /// Severity 127 char severity_; 128 /// Messages (in correct language) (not all 400 may exist) 129 mutable char message_[400]; 130 //@} 131 }; 132 133 /** \brief Class to hold and manipulate an array of massaged messages. 134 135 Note that the message index used to reference a message in the array of 136 messages is completely distinct from the external ID number stored with the 137 message. 138 */ 139 140 class CoinMessages { 141 142 public: 143 /** \brief Supported languages 144 145 These are the languages that are supported. At present only 146 us_en is serious and the rest are for testing. 147 */ 148 enum Language { 149 us_en = 0, 150 uk_en, 151 it 152 }; 153 154 /**@name Constructors etc */ 155 //@{ 156 /** Constructor with number of messages. */ 157 CoinMessages(int numberMessages = 0); 158 /** Destructor */ 159 ~CoinMessages(); 160 /** The copy constructor */ 161 CoinMessages(const CoinMessages &); 162 /** assignment operator. */ 163 CoinMessages &operator=(const CoinMessages &); 164 //@} 165 166 /**@name Useful stuff */ 167 //@{ 168 /*! \brief Installs a new message in the specified index position 169 170 Any existing message is replaced, and a copy of the specified message is 171 installed. 172 */ 173 void addMessage(int messageNumber, const CoinOneMessage &message); 174 /*! \brief Replaces the text of the specified message 175 176 Any existing text is deleted and the specified text is copied into the 177 specified message. 178 */ 179 void replaceMessage(int messageNumber, const char *message); 180 /** Language. Need to think about iso codes */ language() const181 inline Language language() const 182 { 183 return language_; 184 } 185 /** Set language */ setLanguage(Language newlanguage)186 void setLanguage(Language newlanguage) 187 { 188 language_ = newlanguage; 189 } 190 /// Change detail level for one message 191 void setDetailMessage(int newLevel, int messageNumber); 192 /** \brief Change detail level for several messages 193 194 messageNumbers is expected to contain the indices of the messages to be 195 changed. 196 If numberMessages >= 10000 or messageNumbers is NULL, the detail level 197 is changed on all messages. 198 */ 199 void setDetailMessages(int newLevel, int numberMessages, 200 int *messageNumbers); 201 /** Change detail level for all messages with low <= ID number < high */ 202 void setDetailMessages(int newLevel, int low, int high); 203 204 /// Returns class getClass() const205 inline int getClass() const 206 { 207 return class_; 208 } 209 /// Moves to compact format 210 void toCompact(); 211 /// Moves from compact format 212 void fromCompact(); 213 //@} 214 215 /**@name member data */ 216 //@{ 217 /// Number of messages 218 int numberMessages_; 219 /// Language 220 Language language_; 221 /// Source (null-terminated string, maximum 4 characters). 222 char source_[5]; 223 /// Class - see later on before CoinMessageHandler 224 int class_; 225 /** Length of fake CoinOneMessage array. 226 First you get numberMessages_ pointers which point to stuff 227 */ 228 int lengthMessages_; 229 /// Messages 230 CoinOneMessage **message_; 231 //@} 232 }; 233 234 // for convenience eol 235 enum CoinMessageMarker { 236 CoinMessageEol = 0, 237 CoinMessageNewline = 1 238 }; 239 240 /** Base class for message handling 241 242 The default behavior is described here: messages are printed, and (if the 243 severity is sufficiently high) execution will be aborted. Inherit and 244 redefine the methods #print and #checkSeverity to augment the behaviour. 245 246 Messages can be printed with or without a prefix; the prefix will consist 247 of a source string, the external ID number, and a letter code, 248 <i>e.g.</i>, Clp6024W. 249 A prefix makes the messages look less nimble but is very useful 250 for "grep" <i>etc</i>. 251 252 <h3> Usage </h3> 253 254 The general approach to using the COIN messaging facility is as follows: 255 <ul> 256 <li> Define your messages. For each message, you must supply an external 257 ID number, a log (detail) level, and a format string. Typically, you 258 define a convenience structure for this, something that's easy to 259 use to create an array of initialised message definitions at compile 260 time. 261 <li> Create a CoinMessages object, sized to accommodate the number of 262 messages you've defined. (Incremental growth will happen if 263 necessary as messages are loaded, but it's inefficient.) 264 <li> Load the messages into the CoinMessages object. Typically this 265 entails creating a CoinOneMessage object for each message and 266 passing it as a parameter to CoinMessages::addMessage(). You specify 267 the message's internal ID as the other parameter to addMessage. 268 <li> Create and use a CoinMessageHandler object to print messages. 269 </ul> 270 See, for example, CoinMessage.hpp and CoinMessage.cpp for an example of 271 the first three steps. `Format codes' below has a simple example of 272 printing a message. 273 274 <h3> External ID numbers and severity </h3> 275 276 CoinMessageHandler assumes the following relationship between the 277 external ID number of a message and the severity of the message: 278 \li <3000 are informational ('I') 279 \li <6000 warnings ('W') 280 \li <9000 non-fatal errors ('E') 281 \li >=9000 aborts the program (after printing the message) ('S') 282 283 <h3> Log (detail) levels </h3> 284 285 The default behaviour is that a message will print if its detail level 286 is less than or equal to the handler's log level. If all you want to 287 do is set a single log level for the handler, use #setLogLevel(int). 288 289 If you want to get fancy, here's how it really works: There's an array, 290 #logLevels_, which you can manipulate with #setLogLevel(int,int). Each 291 entry logLevels_[i] specifies the log level for messages of class i (see 292 CoinMessages::class_). If logLevels_[0] is set to the magic number -1000 293 you get the simple behaviour described above, whatever the class of the 294 messages. If logLevels_[0] is set to a valid log level (>= 0), then 295 logLevels_[i] really is the log level for messages of class i. 296 297 <h3> Format codes </h3> 298 299 CoinMessageHandler can print integers (normal, long, and long long), 300 doubles, characters, and strings. See the descriptions of the 301 various << operators. 302 303 When processing a standard message with a format string, the formatting 304 codes specified in the format string will be passed to the sprintf 305 function, along with the argument. When generating a message with no 306 format string, each << operator uses a simple format code appropriate for 307 its argument. Consult the documentation for the standard printf facility 308 for further information on format codes. 309 310 The special format code `%?' provides a hook to enable or disable 311 printing. For each `%?' code, there must be a corresponding call to 312 printing(bool). This provides a way to define optional parts in 313 messages, delineated by the code `%?' in the format string. Printing can 314 be suppressed for these optional parts, but any operands must still be 315 supplied. For example, given the message string 316 \verbatim 317 "A message with%? an optional integer %d and%? a double %g." 318 \endverbatim 319 installed in CoinMessages \c exampleMsgs with index 5, and 320 \c CoinMessageHandler \c hdl, the code 321 \code 322 hdl.message(5,exampleMsgs) ; 323 hdl.printing(true) << 42 ; 324 hdl.printing(true) << 53.5 << CoinMessageEol ; 325 \endcode 326 will print 327 \verbatim 328 A message with an optional integer 42 and a double 53.5. 329 \endverbatim 330 while 331 \code 332 hdl.message(5,exampleMsgs) ; 333 hdl.printing(false) << 42 ; 334 hdl.printing(true) << 53.5 << CoinMessageEol ; 335 \endcode 336 will print 337 \verbatim 338 A message with a double 53.5. 339 \endverbatim 340 341 For additional examples of usage, see CoinMessageHandlerUnitTest in 342 CoinMessageHandlerTest.cpp. 343 */ 344 345 class CoinMessageHandler { 346 347 friend bool CoinMessageHandlerUnitTest(); 348 349 public: 350 /**@name Virtual methods that the derived classes may provide */ 351 //@{ 352 /** Print message, return 0 normally. 353 */ 354 virtual int print(); 355 /** Check message severity - if too bad then abort 356 */ 357 virtual void checkSeverity(); 358 //@} 359 360 /**@name Constructors etc */ 361 //@{ 362 /// Constructor 363 CoinMessageHandler(); 364 /// Constructor to put to file pointer (won't be closed) 365 CoinMessageHandler(FILE *fp); 366 /** Destructor */ 367 virtual ~CoinMessageHandler(); 368 /** The copy constructor */ 369 CoinMessageHandler(const CoinMessageHandler &); 370 /** Assignment operator. */ 371 CoinMessageHandler &operator=(const CoinMessageHandler &); 372 /// Clone 373 virtual CoinMessageHandler *clone() const; 374 //@} 375 /**@name Get and set methods */ 376 //@{ 377 /// Get detail level of a message. detail(int messageNumber,const CoinMessages & normalMessage) const378 inline int detail(int messageNumber, const CoinMessages &normalMessage) const 379 { 380 return normalMessage.message_[messageNumber]->detail(); 381 } 382 /** Get current log (detail) level. */ logLevel() const383 inline int logLevel() const 384 { 385 return logLevel_; 386 } 387 /** \brief Set current log (detail) level. 388 389 If the log level is equal or greater than the detail level of a message, 390 the message will be printed. A rough convention for the amount of output 391 expected is 392 - 0 - none 393 - 1 - minimal 394 - 2 - normal low 395 - 3 - normal high 396 - 4 - verbose 397 398 Please assign log levels to messages accordingly. Log levels of 8 and 399 above (8,16,32, <i>etc</i>.) are intended for selective debugging. 400 The logical AND of the log level specified in the message and the current 401 log level is used to determine if the message is printed. (In other words, 402 you're using individual bits to determine which messages are printed.) 403 */ 404 void setLogLevel(int value); 405 /** Get alternative log level. */ logLevel(int which) const406 inline int logLevel(int which) const 407 { 408 return logLevels_[which]; 409 } 410 /*! \brief Set alternative log level value. 411 412 Can be used to store alternative log level information within the handler. 413 */ 414 void setLogLevel(int which, int value); 415 416 /// Set the number of significant digits for printing floating point numbers 417 void setPrecision(unsigned int new_precision); 418 /// Current number of significant digits for printing floating point numbers precision()419 inline int precision() { return (g_precision_); } 420 421 /// Switch message prefix on or off. 422 void setPrefix(bool yesNo); 423 /// Current setting for printing message prefix. 424 bool prefix() const; 425 /*! \brief Values of double fields already processed. 426 427 As the parameter for a double field is processed, the value is saved 428 and can be retrieved using this function. 429 */ doubleValue(int position) const430 inline double doubleValue(int position) const 431 { 432 return doubleValue_[position]; 433 } 434 /*! \brief Number of double fields already processed. 435 436 Incremented each time a field of type double is processed. 437 */ numberDoubleFields() const438 inline int numberDoubleFields() const 439 { 440 return static_cast< int >(doubleValue_.size()); 441 } 442 /*! \brief Values of integer fields already processed. 443 444 As the parameter for a integer field is processed, the value is saved 445 and can be retrieved using this function. 446 */ intValue(int position) const447 inline CoinBigIndex intValue(int position) const 448 { 449 return longValue_[position]; 450 } 451 /*! \brief Number of integer fields already processed. 452 453 Incremented each time a field of type integer is processed. 454 */ numberIntFields() const455 inline int numberIntFields() const 456 { 457 return static_cast< int >(longValue_.size()); 458 } 459 /*! \brief Values of char fields already processed. 460 461 As the parameter for a char field is processed, the value is saved 462 and can be retrieved using this function. 463 */ charValue(int position) const464 inline char charValue(int position) const 465 { 466 return charValue_[position]; 467 } 468 /*! \brief Number of char fields already processed. 469 470 Incremented each time a field of type char is processed. 471 */ numberCharFields() const472 inline int numberCharFields() const 473 { 474 return static_cast< int >(charValue_.size()); 475 } 476 /*! \brief Values of string fields already processed. 477 478 As the parameter for a string field is processed, the value is saved 479 and can be retrieved using this function. 480 */ stringValue(int position) const481 inline std::string stringValue(int position) const 482 { 483 return stringValue_[position]; 484 } 485 /*! \brief Number of string fields already processed. 486 487 Incremented each time a field of type string is processed. 488 */ numberStringFields() const489 inline int numberStringFields() const 490 { 491 return static_cast< int >(stringValue_.size()); 492 } 493 494 /// Current message currentMessage() const495 inline CoinOneMessage currentMessage() const 496 { 497 return currentMessage_; 498 } 499 /// Source of current message currentSource() const500 inline std::string currentSource() const 501 { 502 return source_; 503 } 504 /// Output buffer messageBuffer() const505 inline const char *messageBuffer() const 506 { 507 return messageBuffer_; 508 } 509 /// Highest message number (indicates any errors) highestNumber() const510 inline int highestNumber() const 511 { 512 return highestNumber_; 513 } 514 /// Get current file pointer filePointer() const515 inline FILE *filePointer() const 516 { 517 return fp_; 518 } 519 /// Set new file pointer setFilePointer(FILE * fp)520 inline void setFilePointer(FILE *fp) 521 { 522 fp_ = fp; 523 } 524 //@} 525 526 /**@name Actions to create a message */ 527 //@{ 528 /*! \brief Start a message 529 530 Look up the specified message. A prefix will be generated if enabled. 531 The message will be printed if the current log level is equal or greater 532 than the log level of the message. 533 */ 534 CoinMessageHandler &message(int messageNumber, 535 const CoinMessages &messages); 536 537 /*! \brief Start or continue a message 538 539 With detail = -1 (default), does nothing except return a reference to the 540 handler. (I.e., msghandler.message() << "foo" is precisely equivalent 541 to msghandler << "foo".) If \p msgDetail is >= 0, is will be used 542 as the detail level to determine whether the message should print 543 (assuming class 0). 544 545 This can be used with any of the << operators. One use is to start 546 a message which will be constructed entirely from scratch. Another 547 use is continuation of a message after code that interrupts the usual 548 sequence of << operators. 549 */ 550 CoinMessageHandler &message(int detail = -1); 551 552 /*! \brief Print a complete message 553 554 Generate a standard prefix and append \c msg `as is'. This is intended as 555 a transition mechanism. The standard prefix is generated (if enabled), 556 and \c msg is appended. The message must be ended with a CoinMessageEol 557 marker. Attempts to add content with << will have no effect. 558 559 The default value of \p detail will not change printing status. If 560 \p detail is >= 0, it will be used as the detail level to determine 561 whether the message should print (assuming class 0). 562 563 */ 564 CoinMessageHandler &message(int externalNumber, const char *source, 565 const char *msg, 566 char severity, int detail = -1); 567 568 /*! \brief Process an integer parameter value. 569 570 The default format code is `%d'. 571 */ 572 CoinMessageHandler &operator<<(int intvalue); 573 #if COIN_BIG_INDEX == 1 574 /*! \brief Process a long integer parameter value. 575 576 The default format code is `%ld'. 577 */ 578 CoinMessageHandler &operator<<(long longvalue); 579 #endif 580 #if COIN_BIG_INDEX == 2 581 /*! \brief Process a long long integer parameter value. 582 583 The default format code is `%ld'. 584 */ 585 CoinMessageHandler &operator<<(long long longvalue); 586 #endif 587 /*! \brief Process a double parameter value. 588 589 The default format code is `%d'. 590 */ 591 CoinMessageHandler &operator<<(double doublevalue); 592 /*! \brief Process a STL string parameter value. 593 594 The default format code is `%g'. 595 */ 596 CoinMessageHandler &operator<<(const std::string &stringvalue); 597 /*! \brief Process a char parameter value. 598 599 The default format code is `%s'. 600 */ 601 CoinMessageHandler &operator<<(char charvalue); 602 /*! \brief Process a C-style string parameter value. 603 604 The default format code is `%c'. 605 */ 606 CoinMessageHandler &operator<<(const char *stringvalue); 607 /*! \brief Process a marker. 608 609 The default format code is `%s'. 610 */ 611 CoinMessageHandler &operator<<(CoinMessageMarker); 612 /** Finish (and print) the message. 613 614 Equivalent to using the CoinMessageEol marker. 615 */ 616 int finish(); 617 /*! \brief Enable or disable printing of an optional portion of a message. 618 619 Optional portions of a message are delimited by `%?' markers, and 620 printing processes one %? marker. If \c onOff is true, the subsequent 621 portion of the message (to the next %? marker or the end of the format 622 string) will be printed. If \c onOff is false, printing is suppressed. 623 Parameters must still be supplied, whether printing is suppressed or not. 624 See the class documentation for an example. 625 */ 626 CoinMessageHandler &printing(bool onOff); 627 628 //@} 629 630 /** Log levels will be by type and will then use type 631 given in CoinMessage::class_ 632 633 - 0 - Branch and bound code or similar 634 - 1 - Solver 635 - 2 - Stuff in Coin directory 636 - 3 - Cut generators 637 */ 638 #define COIN_NUM_LOG 4 639 /// Maximum length of constructed message (characters) 640 #define COIN_MESSAGE_HANDLER_MAX_BUFFER_SIZE 1000 641 protected: 642 /**@name Protected member data */ 643 //@{ 644 /// values in message 645 std::vector< double > doubleValue_; 646 std::vector< CoinBigIndex > longValue_; 647 std::vector< char > charValue_; 648 std::vector< std::string > stringValue_; 649 /// Log level 650 int logLevel_; 651 /// Log levels 652 int logLevels_[COIN_NUM_LOG]; 653 /// Whether we want prefix (may get more subtle so is int) 654 int prefix_; 655 /// Current message 656 CoinOneMessage currentMessage_; 657 /// Internal number for use with enums 658 int internalNumber_; 659 /// Format string for message (remainder) 660 char *format_; 661 /// Output buffer 662 char messageBuffer_[COIN_MESSAGE_HANDLER_MAX_BUFFER_SIZE]; 663 /// Position in output buffer 664 char *messageOut_; 665 /// Current source of message 666 std::string source_; 667 /** 0 - Normal. 668 1 - Put in values, move along format, but don't print. 669 2 - A complete message was provided; nothing more to do but print 670 when CoinMessageEol is processed. Any << operators are treated 671 as noops. 672 3 - do nothing except look for CoinMessageEol (i.e., the message 673 detail level was not sufficient to cause it to print). 674 */ 675 int printStatus_; 676 /// Highest message number (indicates any errors) 677 int highestNumber_; 678 /// File pointer 679 FILE *fp_; 680 /// Current format for floating point numbers 681 char g_format_[8]; 682 /// Current number of significant digits for floating point numbers 683 int g_precision_; 684 //@} 685 686 private: 687 /** The body of the copy constructor and the assignment operator */ 688 void gutsOfCopy(const CoinMessageHandler &rhs); 689 690 /*! \brief Internal function to locate next format code. 691 692 Intended for internal use. Side effects modify the format string. 693 */ 694 char *nextPerCent(char *start, const bool initial = false); 695 696 /*! \brief Internal printing function. 697 698 Makes it easier to split up print into clean, print and check severity 699 */ 700 int internalPrint(); 701 702 /// Decide if this message should print. 703 void calcPrintStatus(int msglvl, int msgclass); 704 }; 705 706 //############################################################################# 707 /** A function that tests the methods in the CoinMessageHandler class. The 708 only reason for it not to be a member method is that this way it doesn't 709 have to be compiled into the library. And that's a gain, because the 710 library should be compiled with optimization on, but this method should be 711 compiled with debugging. */ 712 bool CoinMessageHandlerUnitTest(); 713 714 #endif 715 716 /* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2 717 */ 718