-
Notifications
You must be signed in to change notification settings - Fork 160
/
exception.cpp
291 lines (232 loc) · 7.3 KB
/
exception.cpp
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/*
# exception
# throw
#catch
Great source: <http://www.cplusplus.com/doc/tutorial/exceptions/>
Application: centralize error handling in a single place, even if outside executing functions.
The application is similar to C's longjmp, but the implementation is different.
TODO how are they implemented in assembly code? <http://stackoverflow.com/questions/490773/how-is-the-c-exception-handling-runtime-implemented>
It is more magic than C longjmp as it does type checking.
Anything can be thrown, but the most standard and extensible method is to throw subclasses of exception,
so just do that always.
There is no finally block: <http://stackoverflow.com/questions/161177/does-c-support-finally-blocks-and-whats-this-raii-i-keep-hearing-about>
Deinitializations are left for destructors.
# Standard exceptions.
- exception base class of all stdlib exceptions.
- bad_alloc thrown by new on allocation failure
- bad_cast thrown by dynamic_cast when fails with a referenced type
- bad_exception thrown when an exception type doesn't match any catch
- bad_typeid thrown by typeid
- ios_base::failure thrown by functions in the iostream library
# exception safety
Different levels of how much excpetion handlind a function does:
<http://en.wikipedia.org/wiki/Exception_safety>
*/
#include "common.hpp"
void exception_func_int() {
throw 1;
}
class myexception: public std::exception {
virtual const char* what() const throw() {
return "myexception::what()";
}
};
// All exceptions are catchable (default):
void exception_func_all() { throw 0; }
#if __cplusplus < 201703L
// Only int exceptions are catchable
void exception_func_int_only(bool throw_int) throw (int) {
if (throw_int)
throw 1;
else
throw 'c';
}
// Only int and exception or derived excpetions are catchable:
void exception_func_int_exception_only(int which) throw (int, std::exception) {
switch (which) {
case 0: throw 0; break;
case 1: throw myexception(); break;
default: throw 'c'; break;
}
}
#endif
// No exceptions are catchable
void exception_func_none() throw() {throw 1;}
void exception_func_none_wrapper() {
exception_func_none();
}
// The destructor of this class throws an exception!!!
class ExceptionDestructor {
public:
~ExceptionDestructor() { throw std::exception(); }
};
void ExceptionDestructorCaller() {
ExceptionDestructor e;
}
int main() {
/*
Exceptions can jump out of functions.
This is their main reason for existing!
*/
{
try {
exception_func_int();
} catch (int i) {
assert(i == 1);
}
}
/*
# std::exception
Anything can be thrown, including classes and base types.
All stdlib exceptions inherit from `exception`, so it is a good idea to only throw
things inherited from it.
`std::exception` has limited use since its constructor does not take any arguments,
so you cannot describe the error. Some stdlib derived class constructors do however.
*/
{
try {
throw std::exception();
} catch (std::exception e) {
}
}
/*
Catch blocks work like function overloads and catch by type.
*/
try {
throw 'c';
} catch (int i) {
assert(false);
} catch (char c) {
}
/*
`...` is the default case
*/
try {
throw 1.0;
} catch (int i) {
assert(false);
} catch (char c) {
assert(false);
} catch (...) {
}
/*
Derived classes.
Just like for function overloading, base classes catch for derived classes.
*/
{
try {
throw myexception();
} catch (std::exception& ex) {
}
/*
This compiles, but generates a warning, since the first catch will always catch instead of this one.
*/
//catch (myexception& ex) {assert(false);}
catch (...) {assert(false);}
/*
this is a more common exception ordering, first derived then base.
*/
{
try {
throw myexception();
} catch (myexception& ex) {
} catch (std::exception& ex) {
assert(false);
} catch (...) {
assert(false);
}
}
}
/*
# what
Returns a string which contains information about the exception.
Many stdlib exceptions simply return the error message given on the constructor.
*/
{
std::string msg = "custom message";
std::ios_base::failure e(msg);
// TODO worked in GCC 4.8, failed in GCC 5.1.
//assert(e.what() == msg);
}
/*
# uncaught exceptions
Uncaught exceptions explose at top level and terminate the program.
Check out the error messages generated by each exception.
Classes that derive from exception and implement `what()` can print custom messages,
which may contain useful debug info. This is a major point in favor of using exception
classes instead of base types.
*/
{
//throw 1;
//throw 'c';
//throw 1.0;
//throw myexception();
}
#if __cplusplus < 201703L
/*
# exception specifications
Functions can specify which exceptions are catchable with the following syntax.
Deprecated since C++11, removed in C++17, noexcept is favored instead.
https://stackoverflow.com/questions/47284705/c1z-dynamic-exception-specification-error
*/
{
try {
exception_func_int_only(true);
} catch (int i) {
} catch (...) {
assert(false);
}
try {
//exception_func_int_only(false);
} catch (...) {
/* not even ... this can catch non int exceptions thrown by this function */
}
try {
exception_func_int_exception_only(1);
} catch (int i) {
} catch (myexception& ex) {
} catch (...) {
assert(false);
}
try {
//exception_func_none();
} catch (...) {
/* no exception thrown by this function is catchable */
}
try {
//exception_func_none_wrapper();
} catch (...) {
/* the same goes if we wrap the function */
}
}
#endif
/*
# exception from destructor
Never throw an exception from a destructor.
Destructors are meant to clean up after exceptions, so if you throw exceptions from them,
things get messy.
C++ specifies that if this happens during stack unwinding, the program may terminate!
What to do to avoid that: <http://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor>
The following code could lead to that.
*/
if (0) {
try {
ExceptionDestructor e;
} catch (...) {
}
try {
ExceptionDestructorCaller();
} catch (...) {
}
}
#if __cplusplus >= 201103L
/*
# noexcept
Improved version of `throw` for functions.
`throw` for functions becomes deprecated in C++11.
TODO
*/
{
}
#endif
}