AAO DRAMA/DRAMA2 C++ Interface
DRAMA C++11 and later interface
exception.hh
Go to the documentation of this file.
1#ifndef _DRAMA2_EXCEPT_H
2#define _DRAMA2_EXCEPT_H
3
17/*
18 * History:
19 08-Jan-2014 - TJF - Original version.
20 30-Sep-2016 - TJF - There were many changes before now.
21 Fix some formatting in Print() and ErsRep().
22 20-Jan-2019 - TJF - Resolve with with the results of what() being
23 rubbish. This required I added the _whatString
24 item and update it each time the value of toString
25 would be changed. This is likely due to what in
26 effect return a c_str() result from a temp.
27 25-May-2021 - TJF - Have now created exception.cpp and the implementation
28 of all methods with and complexity which are not
29 templates or others required as inline have been
30 moved to it.
31
32 */
33
34#include "drama.h"
35#include "status.h"
36#include "mess.h"
37#include "drama2_err.h"
38#include <string>
39#include <stdio.h>
40#include <stdarg.h>
41#include <string.h>
42#include <execinfo.h> // For backtrace and backtrace_symbols
43#include <sstream> // For ostringstream
44#include <cxxabi.h>
45#include <iostream>
46#include "Ers.h"
47#include <iomanip> // To allow std::hex() to be used.
48#include <vector>
49#include "drama/util.hh"
50#include "drama/fmt/format.h"
51
63#define DramaTHROW(status_,message_) throw drama::Exception(__func__, __FILE__,__LINE__,(message_), (status_))
64
65// From C++20, we could use std::source_location instead of
66// the macros for these calls. But we want to support back to C++11
67// at this time.
68
69
70
88#define DramaTHROW_S(status_,format_,...) drama::ExceptionThrowSafe(__func__, __FILE__,__LINE__,(status_), (format_), __VA_ARGS__)
89
102#define DramaTHROW_F(status_,format_,...) drama::ExceptionThrowSafeF(__func__, __FILE__,__LINE__,(status_), (format_), __VA_ARGS__)
103
104
105namespace drama
106{
156 class Exception : public std::exception {
157
158 private :
159 // Note line and file are not currently used
160 // through the values are set correctly. It may be appropriate
161 // for methods to be added to access them later.
162 int _lineNum; // Line number exception was thrown from
163 std::string _sourceFile; // Source file exception was thrown from
164 std::string _sourceFunc; // Function exception was thrown from.
165 std::string _origMessage; // Original contextual message
166 StatusType _dramaStatus; // DRAMA Error code.
167 std::ostringstream _message; // Message to be output.
168 std::ostringstream _stackTrace; // Stack trace recorded here.
169 static const unsigned _stackTraceMax = 25; // Max elements in stack trace.
170 std::string _whatString; //Used for result of what() to avoid temp going out of scope.
171
172 static bool _firstReportDone; // We report about the environment variable
173 // that causes more details to be output
174 // on the first report only.
175 bool _alwaysStacktrace = false; // Does we always output the stack trace.
176 /*
177 * Generate the stack track.
178 *
179 * array size is _stackTraceMax, and has been field out
180 * by a call to backtrace(). nSize elements are valid.
181 */
182 void GenerateStackTrace(const int nSize, void *array[]);
183 /*
184 * Support method for GenerateStackTrace.
185 */
186 void AddStackTraceLine(char *stackString);
187
188 public:
195 Exception() : _lineNum(0), _sourceFile("none"),
196 _sourceFunc("none"), _origMessage("none"),
197 _dramaStatus(STATUS__OK) { }
198
199
218 Exception(const std::string func,
219 const std::string &file,
220 const int lineNum,
221 const std::string &message = "",
223 const bool alwaysStacktrace = false);
224
225
241 D2DEPRECATED Exception(
242 bool makeUnique DUNUSED,
243 const std::string func, // Set to __func__
244 const std::string &file, // Set to __FILE__
245 const int lineNum, // Set to __LINE__
246 const StatusType status,
247 const char *format, ...) :
248 Exception(func, file, lineNum, "<<C-Format>", status)
249 {
250 va_list ap;
252 char buffer[200];
253 vsnprintf(buffer, sizeof(buffer), format, ap);
254 _message.str(""); // Clear result of primary constructor.
255 _origMessage = buffer;
256 _message << buffer; // Add the real message required.
257 _whatString = toString();
258 }
259
264 // Needed as _message can't be copied directly.
265 Exception(const Exception &source) :
266 _lineNum(source._lineNum),
267 _sourceFile(source._sourceFile),
268 _sourceFunc(source._sourceFunc),
269 _origMessage(source._origMessage),
270 _dramaStatus(source._dramaStatus),
271 _whatString(source._whatString),
272 _alwaysStacktrace(source._alwaysStacktrace) {
273 _message.str(source._message.str());
274 }
275
281 _lineNum(std::move(source._lineNum)),
282 _sourceFile(std::move(source._sourceFile)),
283 _sourceFunc(std::move(source._sourceFunc)),
284 _origMessage(std::move(source._origMessage)),
285 _dramaStatus(std::move(source._dramaStatus)),
286 _whatString(std::move(source._whatString)),
287 _alwaysStacktrace(source._alwaysStacktrace) {
288
289 // I thought I should have been able to initialize _message above()
290 // using _message(std::move(source._message)) as streams are
291 // are supposed to support move, but this didn't work when tried
292 // with GCC 4.7. So we still use this expensive operation.
293
294 _message.str(source._message.str());
295 _stackTrace.str(source._stackTrace.str());
296 }
297
303 // Needed as _message can't be copied directly.
305
306 if (this == &rhs)
307 return *this;
308
309 this->_sourceFile = rhs._sourceFile;
310 this->_sourceFunc = rhs._sourceFunc;
311 this->_lineNum = rhs._lineNum;
312 this->_origMessage = rhs._origMessage;
313 this->_dramaStatus = rhs._dramaStatus;
314 this->_whatString = rhs._whatString;
315
316 this->_message.str(rhs._message.str()); // Copy in message string.
317 this->_stackTrace.str(rhs._stackTrace.str());
318 this->_alwaysStacktrace = rhs._alwaysStacktrace;
319 return *this;
320 }
326 Exception& operator=(Exception &&rhs) noexcept {
327
328 if (this == &rhs)
329 return *this;
330
331 this->_sourceFile = std::move(rhs._sourceFile);
332 this->_sourceFunc = std::move(rhs._sourceFunc);
333 this->_lineNum = std::move(rhs._lineNum);
334 this->_origMessage = std::move(rhs._origMessage);
335 this->_dramaStatus = std::move(rhs._dramaStatus);
336 this->_whatString = std::move(rhs._whatString);
337
338 // I Think Should be possible, but didn't work in GCC 4.7.1, 4.8.2
339 // this->_message = std::move(rhs._message);
340
341 this->_message.str(rhs._message.str()); // Copy in message string.
342 this->_stackTrace.str(rhs._stackTrace.str());
343
344 this->_alwaysStacktrace = rhs._alwaysStacktrace;
345
346 return *this;
347 }
348
349
350
351
360 virtual std::string toString(bool details=false) const;
361
366 virtual const std::string & getOrigMessage() const {
367 return _origMessage;
368 }
373 virtual int lineNumber() const {
374 return _lineNum;
375 }
381 virtual const std::string & fileName() const {
382 return _sourceFile;
383 }
389 virtual const std::string & function() const {
390 return _sourceFunc;
391 }
399 virtual StatusType dramaStatus() const {
400 return _dramaStatus;
401 }
408 virtual std::string dramaStatusStr() const;
409
419 virtual int statusAsSysExitCode() const {
420 return MessStatusToSysExit(_dramaStatus);
421 }
422
428 void AddErs();
429
430
439 template<class T>
440 Exception & operator << (const T &t) {
441 _message << t;
442 //_message << std::endl << t;
443 _whatString = toString(); // Ensure what string updated.
444 return *this;
445 }
451 virtual std::string getStackTrace() const {
452 return _stackTrace.str();
453 }
454
465 virtual void Print(FILE *file=stderr, bool include_stacktrace=false) const;
466
478 virtual void ErsRep() const;
479
480
486 virtual void ErsOut() const;
487
503 const char* what() const noexcept override {
504 return _whatString.c_str();
506
507
519 template<typename... Args>
520 void FmtWriteF(const fmt::format_string<Args...> fmt,
521 Args&&... args) {
522 auto formated = fmt::vformat(fmt.get(), fmt::make_format_args(args...));
523 *this << formated;
524 }
525
526
534#ifndef RUNNING_DOXYGEN
535 D2_FMT_DEPRECATED("Replace FmtWrite() by FmtWriteF(). See MessageUserF() page for details.")
536#endif
537 void FmtWrite(const char *str);
538
556 template<typename T, typename... Types>
557#ifndef RUNNING_DOXYGEN
558 D2_FMT_DEPRECATED("Replace FmtWrite() by FmtWriteF(). See MessageUserF() page for details.")
559#endif
560 void FmtWrite(const char * format,
561 T value,
562 Types... args) {
563 while (*format)
565 if (*format == '%')
566 {
567 if (*(format + 1) == '%')
568 {
569 ++format;
570 }
571 else
572 {
573 *this << value;
574 FmtWrite(format + 1, args...); // call even when *s == 0 to detect extra arguments
575 return;
576 }
577 }
578 *this << *format++;
579 }
580 if (_stackTrace.str().size() != 0)
581 {
582 /* If we have a stack trace, output it */
583 std::cerr << "----------------------------------------\n"
584 << "drama::Exception::FmtWrite():Extra arguments provided to drama::ExceptionThrowSafe, a stack trace follows:\n"
585 << " This problem is likely triggered by a call to DramaTHROW_S() (a macro)\n"
586 << " Most likely case - look in the stack trace for a routine calling ExceptionThrowSafe()\n"
587 << " And then look in the source for a call to DramaTHROW_S()\n\n"
588 << _stackTrace.str()
589 << "----------------------------------------" << std::endl;
590 }
592 "Extra arguments provided to drama::ExceptionThrowSafe (probably via a call to DramaTHROW_S())");
593 }
594
597 virtual ~Exception() {};
598
599 }; // Class Exception
600
601
602
628 template<typename... Types>
629#ifndef RUNNING_DOXYGEN
630 D2_FMT_DEPRECATED ("replace DramaTHROW_S() by DramaTHROW_F(). See MessageUserF() page for details.")
631#endif
632 void ExceptionThrowSafe[[ noreturn ]] (const std::string func,
633 const std::string &file,
634 const int lineNum,
636 const char * format,
637 Types... args)
638 {
640
641 e.FmtWrite(format, args...);
642
643 throw e;
644
645 }
646
669 template<typename... Args>
670 void ExceptionThrowSafeF[[ noreturn ]] (const std::string func,
671 const std::string &file,
672 const int lineNum,
674 const fmt::format_string<Args...> fmt,
675 Args&&... args)
676 {
678
679 e << fmt::vformat(fmt.get(), fmt::make_format_args(args...));
680
681 throw e;
682
683 }
684
685
686} // namespace drama
687
688
689
690
699inline std::ostream& operator << (std::ostream &strm, const drama::Exception & e)
700{
701 strm << e.toString(true);
702 return strm;
703}
704
705
706#endif /* _DRAMA2_EXCEPT_H */
void FmtWriteF(const fmt::format_string< Args... > fmt, Args &&... args)
Safe formatted write to an exception message string with fmt::format string formatting.
Definition exception.hh:547
D2DEPRECATED Exception(bool makeUnique, const std::string func, const std::string &file, const int lineNum, const StatusType status, const char *format,...)
Create an exception using C printf() style string formatting.
Definition exception.hh:268
virtual std::string getStackTrace() const
Return the stack trace created when the exception was created.
Definition exception.hh:478
virtual int lineNumber() const
Returns the source file line number at which the exception occurred.
Definition exception.hh:400
virtual int statusAsSysExitCode() const
Return a system exit code based on the DRAMA Status.
Definition exception.hh:446
virtual void ErsRep() const
Report details of this exception using ERS.
virtual void ErsOut() const
Output a DRAMA ERS report about this exception.
Exception(const std::string func, const std::string &file, const int lineNum, const std::string &message="", const StatusType status=DRAMA2__CPP_ERROR, const bool alwaysStacktrace=false)
Create an Exception.
virtual const std::string & fileName() const
Returns the name of the C++ source file in which the exception occurred.
Definition exception.hh:408
virtual const std::string & getOrigMessage() const
Returns the exception original message.
Definition exception.hh:393
const char * what() const noexcept override
Get string identifying exception.
Definition exception.hh:530
virtual void Print(FILE *file=stderr, bool include_stacktrace=false) const
Print exception details to a file.
Exception(const Exception &source)
Copy constructor.
Definition exception.hh:292
Exception & operator=(const Exception &rhs)
Assignment operator.
Definition exception.hh:331
void FmtWrite(const char *format, T value, Types... args)
Safe formatted write to an exception message string.
Definition exception.hh:587
void FmtWrite(const char *str)
Safe formatted write to an exception message string.
virtual const std::string & function() const
Returns the name of the function in which the exception occurred.
Definition exception.hh:416
void AddErs()
Add the any errors from the current ERS context to the exception.
virtual std::string dramaStatusStr() const
Return the DRAMA Status code converted to a DRAMA.
virtual std::string toString(bool details=false) const
Return the exception string.
Exception & operator<<(const T &t)
Add extra information to an exception message.
Definition exception.hh:467
Exception(Exception &&source) noexcept
Move constructor.
Definition exception.hh:307
Exception()
Create an Exception, details empty.
Definition exception.hh:222
virtual StatusType dramaStatus() const
Return the DRAMA Status code associated with the exception.
Definition exception.hh:426
virtual ~Exception()
Virtual Destructor - since we will be inherited.
Definition exception.hh:624
Exception & operator=(Exception &&rhs) noexcept
Move assignment operator.
Definition exception.hh:353
An Exception class for exceptions thrown by DRAMA V2 classes.
Definition exception.hh:183
std::ostream & operator<<(std::ostream &strm, const drama::Exception &e)
drama::Exception stream output operator
Definition exception.hh:726
#define DramaTHROW(status_, message_)
Throw a Drama exception.
Definition exception.hh:90
void CreateRunDramaTask()
Create and run a DRAMA task, with standard exception handling.
Definition task.hh:1339
void ExceptionThrowSafe(const std::string func, const std::string &file, const int lineNum, StatusType status, const char *format, Types... args)
Create and throw an exception, variable argument list, Safe formatting of arguments.
Definition exception.hh:659
void ExceptionThrowSafeF(const std::string func, const std::string &file, const int lineNum, StatusType status, const fmt::format_string< Args... > fmt, Args &&... args)
Create and throw an exception, - format using fmt::format.
Definition exception.hh:697
The drama namespace contains all the classes, types etc of the DRAMA 2 implementation.
Definition drama.hh:99
DRAMA 2 include file - Utility macros, types etc.