-
Notifications
You must be signed in to change notification settings - Fork 1
/
exception.hpp
170 lines (151 loc) · 4.83 KB
/
exception.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/** @file Defines macros for meaningful error reporting and the
* class @c Exception.
*
* The macro @c CU_THROW_EXCEPTION throws such an exception with the
* required information. It takes one argument which must be implicitely
* convertable to std::string.
*
* The macro @c CU_ADD_EXCEPTION_CONTEXT can be used to provide additional
* failure information to the caller in case of an exception. It adds one
* exception to the chain of nested exceptions in case of an error.
* The code
* @code
* try
* {
* doSomething();
* }
* catch(...)
* {
* CU_THROW_EXCEPTION( "Failed to do something." );
* }
* @endcode
* is equivalent to
* @code
* CU_ADD_EXCEPTION_CONTEXT( "Failed to do something." )
* {
* doSomething();
* }; // note the semicolon.
* @endcode
* This has several advantages:
* 1. The name of the macro makes the intent clear.
* 2. It avoids ugly try-catch control flow.
* Note that the argument to the macro is evaluated lazily, even though
* it does not look like it.
*
* The macro @c CU_ENFORCE tests a condition. If the condition is not
* evaluated to @c true, then an exception with a specified error message is
* thrown.
*
* The macro @c QU_ASSERT_THROW checks a condition to be true. If the
* condition is not met, then @c assert() is used to check the condition
* so in debug mode there is a way to debug the application right then and
* there. Furthermore and exception is thrown with a custom message. This
* also happens in release mode, even though the failed assertions indicates
* a programming error.
*
* @author Ralph Tandetzky
*/
#pragma once
#include <cassert>
#include <exception>
#include <stdexcept>
#include <string>
namespace cu
{
struct ThrowSiteInfo
{
explicit ThrowSiteInfo(
const char * file_
, int line_
, const char * date_
, const char * time_ )
: file(file_)
, line(line_)
, date(date_)
, time(time_)
{
}
const char * file = nullptr;
int line = 0;
const char * date = nullptr;
const char * time = nullptr;
};
// General macros for custom exception types.
#define CU_THROW_SITE_INFO ::cu::ThrowSiteInfo{ __FILE__, __LINE__, __DATE__, __TIME__ }
#define CU_THROW_SPECIFIC_EXCEPTION( msg, ExceptionType ) throw ExceptionType( msg, CU_THROW_SITE_INFO )
#define CU_ADD_EXCEPTION_CONTEXT_CUSTOM( Throw ) \
::cu::detail::makeExceptionContextAdder( [&](){Throw;} ) += [&]()
// Macros specifically for cu::Exceptions.
#define CU_THROW_EXCEPTION( msg ) CU_THROW_SPECIFIC_EXCEPTION( msg, ::cu::Exception )
#define CU_ADD_EXCEPTION_CONTEXT( msg ) \
CU_ADD_EXCEPTION_CONTEXT_CUSTOM( CU_THROW_EXCEPTION(msg) )
#define CU_ENFORCE( expr, msg ) \
do { if ( !static_cast<bool>(expr) ) { CU_THROW_EXCEPTION( msg ); } } while (false)
#define CU_ASSERT_THROW( expr, msg ) \
do { if ( !static_cast<bool>(expr) ) { assert(expr); CU_THROW_EXCEPTION( msg ); } } while (false)
/// A general purpose exception class.
///
/// The @c Exception class stores an error message as well as information
/// about the source location and compilation time and possibly pending
/// exceptions. These can be used to produce detailed error messages.
///
/// The class also works well together with the C++ Standard Library,
/// since it derived from @c std::runtime_error and @c std::nested_exception,
/// so that unrelated libraries can (in theory) catch such an exception and
/// produce meaningful error messages.
///
/// Since the @c Exception class nests pending exceptions inside itself,
/// the user can get a detailed error message which is composed from the
/// error messages of the chain of nested exceptions.
class Exception
: public std::runtime_error
, public std::nested_exception
{
public:
/// @c Exceptions should preferably be thrown by @c CU_THROW_EXCEPTION() or
/// by some other macros within this file.
Exception(const std::string & message, const ThrowSiteInfo & tsi_ )
: std::runtime_error( message )
, tsi( tsi_ )
{}
ThrowSiteInfo getThrowSiteInfo() const
{
return tsi;
}
private:
const ThrowSiteInfo tsi;
};
namespace detail
{
template <typename Thrower>
class ExceptionContextAdder
{
public:
ExceptionContextAdder( Thrower && thrower_ )
: thrower( std::forward<Thrower>(thrower_) )
{
}
template <typename F>
auto operator+=( F && f ) const &&
{
try
{
return std::forward<F>(f)();
}
catch(...)
{
std::forward<Thrower>(thrower)();
// The following line is never reached, but avoids a warning.
return std::forward<F>(f)();
}
}
private:
Thrower && thrower;
};
template <typename Thrower>
ExceptionContextAdder<Thrower> makeExceptionContextAdder( Thrower && thrower )
{
return {std::forward<Thrower>(thrower)};
}
} // namespace detail
} // namespace cu