1 |
_Introduction_ |
2 |
|
3 |
It's very important that you put debugging statements into your code - |
4 |
even if your code seems to work now, it can fail in future and someone |
5 |
else will have to come along and try to work out what's going wrong. |
6 |
|
7 |
Just because a program compiles doesn't mean that it works perfectly - |
8 |
in fact, C++ compilers don't even check that functions compile |
9 |
completely unless the function is called somewhere in a program. If |
10 |
at all possible, you should write a complete test suite for your part |
11 |
of Globulation 2 and put it with the others in testing/. That makes |
12 |
sure your code compiles, runs, and gives correct results, but it also |
13 |
keeps others from getting into too much trouble when they come along |
14 |
later and try fiddle with your code because they think they can do it |
15 |
better. |
16 |
|
17 |
The key to writing a good test suite is to make sure every line of |
18 |
code you've written is run at least once, and that a good selection of |
19 |
interesting inputs (such as minimum and maximum allowed values) |
20 |
produce the outputs you expect. |
21 |
|
22 |
Even if you only debug your code in an ad-hoc way instead of writing a |
23 |
complete test suite, you can do a lot to make your life easier when |
24 |
you come to maintain it in future. It's very tempting to write tests |
25 |
and reports into your code during debugging, then remove or comment |
26 |
them out when you're done - Globulation 2's debugging system is |
27 |
designed so that you can disable them as a block, then re-enable them |
28 |
all again when it comes time to maintain the code. |
29 |
|
30 |
_Background_ |
31 |
|
32 |
Glob2 allows the user to set a debug level at run-time, where |
33 |
increasing the debug level causes the program to do more tests and |
34 |
print more verbose output. The interface for the user hasn't been |
35 |
written yet, but will probably be a command-line option. |
36 |
|
37 |
The debugging interface for programmers is based on a template class |
38 |
Debug<logLevel, testLevel>. Tests and messages will be ignored unless |
39 |
the user-specified debug level is greater than or equal to the |
40 |
template parameter. For example, if the debug level is set to 8 and |
41 |
you use the class Debug<7, 9> in your code, your messages will be |
42 |
displayed to the user (because 8 >= 7), but tests will be ignored |
43 |
(because 8 < 9). |
44 |
|
45 |
You should normally set the template parameters for the Debug<> class |
46 |
in the range 0..127. Messages and tests with level 127 are guaranteed |
47 |
to be ignored, messages with level 0 are guaranteed to be displayed, |
48 |
|
49 |
Tests with level 0 will only be disabled in stable releases of the |
50 |
game, messages with level 0 are guaranteed to be displayed, tests and |
51 |
messages with level 127 are ignored and completely compiled out of the |
52 |
program, anything in between depends on the current debugging level. |
53 |
Negative levels are treated like level 0, so you can temporarily |
54 |
enable a set of tests by putting a minus sign in front of the levels |
55 |
in the associated class. |
56 |
|
57 |
The user will probably be presented with debug levels in the range |
58 |
0..9 or 0..99, which will have to be mapped across - so, for example, |
59 |
0->0, 1->14, 2->28, and so on up to 9->126, with 127 reserved for |
60 |
completely disabled tests. |
61 |
|
62 |
_Interface_ |
63 |
|
64 |
Debugging is done with the Debug<logLevel, testLevel> class. The |
65 |
previous section explains these levels in detail, but basically the |
66 |
higher the level, the less likely your test is to be performed. The |
67 |
template parameters can be left with their default values - Debug<> |
68 |
will typically be set to do more or less debugging depending on |
69 |
whether the code is currently in CVS, alpha, or stable release. |
70 |
|
71 |
The class contains the following static member functions: |
72 |
|
73 |
* invariant(bool) |
74 |
An invariant is a test that should always be true. This |
75 |
function returns false if debugging is enabled for the current |
76 |
class and the specified test is false. |
77 |
|
78 |
* info(DEBUG_INFO) |
79 |
* info() |
80 |
This function returns an output stream (or at least something |
81 |
that you can "<<" to), which sends your output to the standard |
82 |
error, a log file, message box, bit bucket, or wherever the |
83 |
program decides is appropriate. The argument "DEBUG_INFO" is |
84 |
a macro that expands to information about the current file, |
85 |
line, and function. The exact values depend on the compiler |
86 |
you're using, and will probably be printed out along with the |
87 |
message. Obviously, the version of the function with no |
88 |
argument will not print this information. |
89 |
|
90 |
* error(DEBUG_INFO) |
91 |
* error() |
92 |
Like info(), but is also guaranteed to inform the user that |
93 |
something bad has happened. |
94 |
|
95 |
* terminate(ErrorClass) |
96 |
* terminate() |
97 |
Throw ErrorClass (or std::exception if no class is specified), |
98 |
informing the user if possible. |
99 |
|
100 |
_Examples_ |
101 |
|
102 |
Typically, you should create a typedef like this near the start of |
103 |
your class: |
104 |
|
105 |
typedef Debug<7, 9> debug; |
106 |
|
107 |
While writing a new class, you might prefer to use two debugging |
108 |
typedefs: |
109 |
|
110 |
typedef Debug<0, 0> dbg; |
111 |
typedef Debug<> debug; |
112 |
|
113 |
Then you can enable all debugging and checking for a new function as |
114 |
you write it, and control all your other functions as a group by |
115 |
editing the general debug class. |
116 |
|
117 |
Tests are typicall defined like this: |
118 |
|
119 |
if (!debug::invariant(myValue <= maxValue)) |
120 |
debug::error(DEBUG_INFO) |
121 |
<< "\tmyValue=" << myValue << '\n' |
122 |
<< debug::terminate(); |
123 |
|
124 |
If an invariant fails to hold, this will print a nicely formatted |
125 |
message and exit. If you don't need to do a separate test, you could |
126 |
use a system like this: |
127 |
|
128 |
switch (myCase) |
129 |
{ |
130 |
case 1: // handle case 1 |
131 |
case 2: // handle case 2 |
132 |
default: // error: can't happen |
133 |
debug::terminate(); |
134 |
}; |
135 |
|
136 |
Which will just kill the program outright. |