1 | /*
|
---|
2 | * Assert.hpp
|
---|
3 | *
|
---|
4 | * Created on: Mar 18, 2010
|
---|
5 | * Author: crueger
|
---|
6 | */
|
---|
7 |
|
---|
8 | #ifndef ASSERT_HPP_
|
---|
9 | #define ASSERT_HPP_
|
---|
10 |
|
---|
11 | #include<string>
|
---|
12 | #include<vector>
|
---|
13 |
|
---|
14 | /**
|
---|
15 | * \file Helpers/Assert.hpp
|
---|
16 | * <H1> ASSERT Howto </H1>
|
---|
17 | *
|
---|
18 | * <H2> Introduction </H2>
|
---|
19 | *
|
---|
20 | * ASSERT() is a small macro that allows easier debugging, when it is widely used. The custom
|
---|
21 | * ASSERT macro defined in this file works mainly the same way as the assert() macro that
|
---|
22 | * is defined in the Ansi-C standard, but includes a few nice additions.
|
---|
23 | *
|
---|
24 | * <H3> What ASSERT() does </H3>
|
---|
25 | *
|
---|
26 | * ASSERT can be used to make sure that a condition that always needs to be true for the code to
|
---|
27 | * work correctly is holding. If you have a function that takes a value greater than 0 and a value
|
---|
28 | * smaller than 0 indicates a mistake you should always do it the following way: <br>
|
---|
29 | * @code
|
---|
30 | * void foo(int a) // a should be greater 0
|
---|
31 | * {
|
---|
32 | * ASSERT(a>0,"Parameter passed to foo was smaller than 0");
|
---|
33 | * ...
|
---|
34 | * }
|
---|
35 | * @endcode
|
---|
36 | *
|
---|
37 | * (Note: some people say, that assertions like these should not be used to check function parameters.
|
---|
38 | * This is mainly due to the reason, that a failed assertion will show up inside the function. The buggy
|
---|
39 | * code however is at a completely different place, i.e. at the callers side. Always put the
|
---|
40 | * Assertions as close to the code that produces the value as possible, when looking at function
|
---|
41 | * parameters however this would mean, that any code calling foo would have an ASSERT(...) before
|
---|
42 | * it, which makes it easy to forget the Assertion at some places. Also this makes an easy example.)
|
---|
43 | *
|
---|
44 | * If the condition inside the ASSERT does not evaluate to true the user is shown a message, including
|
---|
45 | * the condition that failed, the line in which the failure was observed and the message of the assertion.
|
---|
46 | * In the above case that would look something like this:<br>
|
---|
47 | * @code
|
---|
48 | * Assertion "a>0" failed in foo.cpp in line 3.
|
---|
49 | * Assertion Message: Parameter passed to foo was smaller than 0
|
---|
50 | * @endcode
|
---|
51 | *
|
---|
52 | * In normal conditions, i.e. when no default action is set (see below for default actions) the user
|
---|
53 | * is then shown a short choice menu, on how to handle the assertion. The user can choose to abort the
|
---|
54 | * program, throw an exception of type AssertionFailure that contains the file, line and message,
|
---|
55 | * ignore the assertion or even to always ignore the assertion at that point (i.e. the ASSERT() macro
|
---|
56 | * at this file and line is fully disabled).
|
---|
57 | *
|
---|
58 | * Both ASSERT() and assert() handle debugging in the same way, i.e. they are only used when the
|
---|
59 | * NDEBUG macro is not defined. If the NDEBUG macro is defined, for example using a CXXFLAG then
|
---|
60 | * all asserts and ASSERTs will be disabled in the compiled program. That way in a end-user version
|
---|
61 | * all assertions can be removed with a single switch, thus not hassling the end-user with potential
|
---|
62 | * bugs.
|
---|
63 | *
|
---|
64 | * <H2> Special functions of ASSERT() </H2>
|
---|
65 | *
|
---|
66 | * Compared to the standard assert() macro the custom ASSERT() contains a few special functions. As
|
---|
67 | * first it is possible to set a global default behavior that is used anytime an assertion fails.
|
---|
68 | * This default behavior can be either of Assert::Ask, Assert::Abort, Assert::Throw or Assert::ignore.
|
---|
69 | * The default behavior is set using the ASSERT_DO() macro. For example if you want to check in a
|
---|
70 | * unittest that wrong code at another point actually makes a certain assert fail you could set
|
---|
71 | * ASSERT_DO(Assert::Throw) to make sure a exception is thrown and catch that exception using
|
---|
72 | * the CPPUNIT_ASSERT_THROW() macro. The current set default behavior can be queried as a string
|
---|
73 | * using the ASSERT_DEFAULT macro.
|
---|
74 | *
|
---|
75 | * As a second enhancement it is possible to install callback functions as hooks that will be executed
|
---|
76 | * when an assertion aborts the program. These callback functions could for example be used to flush
|
---|
77 | * any open streams, thus making sure files on the disk are not corrupted by a unexpected abortion.
|
---|
78 | * It would also be possible to install functions that produce some kind of "coredump" of important
|
---|
79 | * internal data-structures, thus giving the person looking for the bug some valuable information.
|
---|
80 | * These assertion hooks should however not be used to clean up the reserved memory of the program,
|
---|
81 | * because a) this memory is under normal circumstances reclaimed by the OS anyway, once the program
|
---|
82 | * has aborted and b) the memory might still contain some hints that could be useful when running
|
---|
83 | * the program inside a debugger and which could be destroyed by the clean-up. To use the hooking
|
---|
84 | * mechanism you can simply use the ASSERT_HOOK() macro, passing this macro any kind of void function.
|
---|
85 | * For example:<br/>
|
---|
86 | * @code
|
---|
87 | * void foo(){
|
---|
88 | * // produce a coredump
|
---|
89 | * ...
|
---|
90 | * // close and flush all open handles
|
---|
91 | * ...
|
---|
92 | * }
|
---|
93 | *
|
---|
94 | * int main(int argc, char **argv){
|
---|
95 | * ASSERT_HOOK(foo);
|
---|
96 | * ...
|
---|
97 | * return 0;
|
---|
98 | * }
|
---|
99 | * @endcode
|
---|
100 | *
|
---|
101 | * All hooks will be executed in the reverse order of hooking, i.e. the function hooked last will be
|
---|
102 | * executed first when the abortion is handled. It is also possible to remove a hook to any function
|
---|
103 | * using the ASSERT_UNHOOK() macro and passing it the pointer to the function one wants to remove.
|
---|
104 | *
|
---|
105 | * Assertion hooks will only be executed when the program is terminated by an assertion using the
|
---|
106 | * abort mechanism. They will not be executed when the program exits in any other way. They also
|
---|
107 | * wont be executed when the assertion is ignored or an exception is thrown (even when the exception
|
---|
108 | * is not caught and thus terminates the program).
|
---|
109 | *
|
---|
110 | * <H2> Rules for using ASSERT() </H2>
|
---|
111 | *
|
---|
112 | * The rules for using ASSERT() are basically the same ones that can be used as guidlines for the
|
---|
113 | * standard assert() macro. So if you think you know those guidelines you can skip the following.
|
---|
114 | *
|
---|
115 | * <ul>
|
---|
116 | * <li> ASSERT() should be used only for problems that indicate a bug, i.e. problems that can be
|
---|
117 | * improved by rewriting parts of the program. ASSERT() should not be used to query problems that
|
---|
118 | * can go wrong during the normal execution of the program. For example ASSERT() should not be
|
---|
119 | * used to test whether a file could be opened, or memory could be reserved, as a failure of either
|
---|
120 | * of those tasks can not be improved upon by rewriting the code.
|
---|
121 | * <li> The condition in the ASSERT() macro should never contain any side-effects. Only call methods,
|
---|
122 | * when you are absolutely certain that these methods wont have any side-effects. Calling ASSERT()
|
---|
123 | * should in no way change the state of the program, because once the end-user version is produced
|
---|
124 | * using the NDEBUG flag all assertions are removed and so are the conditions. If the condition did
|
---|
125 | * cause a state transition, this state transition would be removed and the behavior of the end-user
|
---|
126 | * and the debug version might differ. Things you should watch out for are for example<br/>
|
---|
127 | * @code
|
---|
128 | * ASSERT(++i,"i was zero after incrementing");
|
---|
129 | * @endcode
|
---|
130 | * instead always do
|
---|
131 | * @code
|
---|
132 | * ++i;
|
---|
133 | * ASSERT(i,"i was zero after incrementing");
|
---|
134 | * @endcode
|
---|
135 | * <li> Give descriptive error messages. This one is a bit obvious but easy to do wrong, so I included
|
---|
136 | * it here. An
|
---|
137 | * @code
|
---|
138 | * ASSERT(ptr,"Pointer was zero");
|
---|
139 | * @endcode
|
---|
140 | * wont help anyone. If you do <br/>
|
---|
141 | * @code
|
---|
142 | * ASSERT(ptr,"Second argument of function foo should have pointed to an object of type bar, but was zero.");
|
---|
143 | * @endcode
|
---|
144 | * instead, people will almost immidiately know what to look for.
|
---|
145 | * </ul>
|
---|
146 | *
|
---|
147 | * <H2> Differences between ASSERT() and assert() </H2>
|
---|
148 | *
|
---|
149 | * This chapter is to explain why a custom ASSERT() macro was introduced and should be used in place
|
---|
150 | * of the standard assert(). Here are the main differences between ASSERT() and assert().
|
---|
151 | *
|
---|
152 | * <ul>
|
---|
153 | * <li> ASSERT() makes it easy to add a more verbose message about the nature of the failure. For
|
---|
154 | * assert() it has become customary to add messages using constructs like
|
---|
155 | * @code
|
---|
156 | * assert(c>0 && "Counter should be at least 1");
|
---|
157 | * @endcode in order to add descriptions. However both the syntax and the final output for this are
|
---|
158 | * a bit awkward. The custom ASSERT() handles messages in a much better way, as well as making them
|
---|
159 | * mandatory instead of optional.
|
---|
160 | * <li> ASSERT() leaves the user and the programmer a choice how to handle an assertion. While the
|
---|
161 | * assert() macro will always abort the program, the ASSERT() macro normally gives the user a choice on
|
---|
162 | * what to do. For debugging it might also be interesting how a broken assumption influences the rest
|
---|
163 | * of the program, so the assertion can also be ignored. Also the Exception mechanism allows
|
---|
164 | * assertions to be part of unittests, whereas they would always fail if the assert() macro was used.
|
---|
165 | * <li> ASSERT() does not unwind the stack (at least when compiled using gcc). The normal assert()
|
---|
166 | * exits the program, which unwinds the stack and destroys any hope for recovering a stack trace.
|
---|
167 | * ASSERT() on the other hand aborts the program using a special trap function, that leaves the
|
---|
168 | * stack intact. This way, when the program is run inside a debugger the stack is still available
|
---|
169 | * and can be inspected. This is the main reason, why it is safe to use ASSERT() to check function
|
---|
170 | * parameters, whereas assert() would give problems in such cases.
|
---|
171 | * <li> ASSERT() allows for hooks to be installed when the program exits. As mentioned above this
|
---|
172 | * makes it possible to produce coredumps, make sure all files are in a usable state or other tasks
|
---|
173 | * that have to be performed before killing the program.
|
---|
174 | * </ul>
|
---|
175 | *
|
---|
176 | * <H2> Tips and tricks and FAQ </H2>
|
---|
177 | *
|
---|
178 | * <ul>
|
---|
179 | * <li> <H4> ASSERT() is broken. When I abort the program it says something about an
|
---|
180 | * "Illegal instruction"</H4>
|
---|
181 | * The complaints about the illegal instruction after an abortion are no need to worry. This
|
---|
182 | * illegal instruction is part of the trap that is used to exit the program while leaving the stack
|
---|
183 | * intact. This illegal instruction can be detected by the debugger, which means it will give you the
|
---|
184 | * usual prompt once it is encountered. The illegal instruction is guaranteed not to mess up anything,
|
---|
185 | * so there is no need to worry about it.
|
---|
186 | * <li> <H4> When compiling the program with $NON_GCC_COMPILER and then debugging it, it will
|
---|
187 | * unwind the stack. I need the backtrace however to find the bug </H4>
|
---|
188 | * The mechanism to preserve the stack is compiler specific. For now only a mechanism that is supported
|
---|
189 | * by gcc is implemented, because this compiler is widely used. For other compilers the program
|
---|
190 | * is simply exited, and the stack is destroyed. If you need a backtrace and you cannot use gcc you
|
---|
191 | * have to figure out a way to have your compiler produce a trap instruction in the program. You might
|
---|
192 | * want to use google to find out how to get your compiler to do that. For many compilers a
|
---|
193 | * _asm {int 3} is said to work. Also for VC++ the instruction __debugbreak() might produce a trap.
|
---|
194 | * Also dividing by zero is a hack that could be used as a last hope if you don't find a way to produce
|
---|
195 | * traps with your compiler even after a longer search. If you found a way to handle the traps you can
|
---|
196 | * then add the macro DEBUG_BREAK for your compiler and the stack will be preserved.
|
---|
197 | * <li> <H4> I have a portion of the program that should never be executed. How can I assure this
|
---|
198 | * using assert.</H4>
|
---|
199 | * This is a common task for assertions. For example you might have an exhaustive switch/case where
|
---|
200 | * the default value indicates that something went wrong. Simply use the following construct:
|
---|
201 | * @code
|
---|
202 | * switch(foo){
|
---|
203 | * case Bar:
|
---|
204 | * ...
|
---|
205 | * break;
|
---|
206 | * case Baz:
|
---|
207 | * ...
|
---|
208 | * break;
|
---|
209 | * ...
|
---|
210 | * default:
|
---|
211 | * ASSERT(0,"This switch should always be exhaustive.\nDid somebody add values to the enum?");
|
---|
212 | * }
|
---|
213 | * @endcode
|
---|
214 | * </ul>
|
---|
215 | */
|
---|
216 |
|
---|
217 | namespace Assert{
|
---|
218 |
|
---|
219 | typedef void (*hook_t)(void);
|
---|
220 |
|
---|
221 |
|
---|
222 | enum Action {Ask,Abort,Throw,Ignore,MAX_ACTION};
|
---|
223 | extern const char ActionKeys[MAX_ACTION];
|
---|
224 | extern const char* ActionNames[MAX_ACTION];
|
---|
225 |
|
---|
226 | class AssertionFailure{
|
---|
227 | public:
|
---|
228 | AssertionFailure(std::string _condition, std::string _file, int _line, std::string _message);
|
---|
229 | std::string getFile();
|
---|
230 | int getLine();
|
---|
231 | std::string getMessage();
|
---|
232 |
|
---|
233 | std::ostream& operator<<(std::ostream&);
|
---|
234 | private:
|
---|
235 | std::string condition;
|
---|
236 | std::string file;
|
---|
237 | int line;
|
---|
238 | std::string message;
|
---|
239 | };
|
---|
240 | }
|
---|
241 |
|
---|
242 | #ifndef NDEBUG
|
---|
243 | #ifndef STRINGIFY
|
---|
244 | #define STRINGIFY(x) #x
|
---|
245 | #endif
|
---|
246 |
|
---|
247 | #ifdef __GNUC__
|
---|
248 | // on gcc we know how to exit to the Debugger
|
---|
249 | #define DEBUG_BREAK __builtin_trap()
|
---|
250 | #else
|
---|
251 | #define DEBUG_BREAK exit(1)
|
---|
252 | #endif
|
---|
253 |
|
---|
254 | #define ASSERT(condition,message) \
|
---|
255 | do{\
|
---|
256 | static bool ignore = false;\
|
---|
257 | if(!ignore){\
|
---|
258 | if(_my_assert::check((condition),STRINGIFY(condition),(message),__FILE__,__LINE__,ignore)){\
|
---|
259 | _my_assert::doHooks();\
|
---|
260 | DEBUG_BREAK;\
|
---|
261 | }\
|
---|
262 | } \
|
---|
263 | }while(0)
|
---|
264 |
|
---|
265 | #define ASSERT_NOCATCH(message) \
|
---|
266 | catch(Assert::AssertionFailure&){throw;}\
|
---|
267 | catch(...){\
|
---|
268 | static bool ignore = false; \
|
---|
269 | _my_assert::check(false,"Exception caught",(message),__FILE__,__LINE__,ignore);\
|
---|
270 | } do{(void)(0);}while(0)
|
---|
271 |
|
---|
272 | #define ASSERT_DO(action) do{_my_assert::setDefault(action);}while(0)
|
---|
273 | #define ASSERT_HOOK(hook) do{_my_assert::addHook(hook);}while(0)
|
---|
274 | #define ASSERT_UNHOOK(hook) do{_my_assert::removeHook(hook);}while(0)
|
---|
275 | #define ASSERT_DEFAULT (_myAssert::printDefault())
|
---|
276 | #else
|
---|
277 | // we need to do something, so this is the usual solution (e.g. assert.h)
|
---|
278 | #define ASSERT(condition,message) (void)(0)
|
---|
279 | #define ASSERT_NOCATCH(message) catch(...) {throw;} do{(void)(0);}while(0)
|
---|
280 | #define ASSERT_DO(action) (void)(0)
|
---|
281 | #define ASSERT_HOOK(hook) (void)(0)
|
---|
282 | #define ASSERT_UNHOOK(hook) (void)(0)
|
---|
283 | #define ASSERT_DEFAULT std::string("Deactivated")
|
---|
284 | #endif
|
---|
285 |
|
---|
286 | //! @cond
|
---|
287 | class _my_assert{
|
---|
288 | public:
|
---|
289 | static bool check(const bool res,
|
---|
290 | const char* condition,
|
---|
291 | const char* message,
|
---|
292 | const char* filename,
|
---|
293 | const int line,
|
---|
294 | bool& ignore);
|
---|
295 | static void addHook(Assert::hook_t hook);
|
---|
296 | static void removeHook(Assert::hook_t hook);
|
---|
297 | static void doHooks();
|
---|
298 | static void setDefault(Assert::Action);
|
---|
299 | static Assert::Action getDefault();
|
---|
300 | static std::string printDefault();
|
---|
301 | private:
|
---|
302 | static Assert::Action defaultAction;
|
---|
303 | static std::vector<Assert::hook_t> hooks;
|
---|
304 | };
|
---|
305 | //! @endcond
|
---|
306 |
|
---|
307 |
|
---|
308 | #endif /* ASSERT_HPP_ */
|
---|