-
Notifications
You must be signed in to change notification settings - Fork 1
/
CodingConventions.txt
426 lines (311 loc) · 16.7 KB
/
CodingConventions.txt
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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
$Id: CodingConventions.txt 2405 2007-04-10 01:09:36Z flaterco $
Preface
-------
These coding conventions exist in the following context, the world as
it is in late 2006:
1. The current version of standard C++ is ISO/IEC 14882:2003.
2. Slackware Linux is shipping GCC 3.4.6 as its default compiler.
The current version of GCC is 4.1.1. GCC 4.1 includes most or all of
the nonbinding TR1 ("Technical Report 1") extensions to ISO/IEC
14882:2003. GCC 3.4.6 doesn't.
3. TR1 adds shared_ptr, tuples, and unordered associative containers
(a.k.a. hash-maps), but nothing else of particular relevance to XTide
2.9. The only significant use of shared_ptr in XTide would be as a
workaround for the inability to store auto_ptrs in STL containers.
This application of shared_ptr would in turn be obsoleted by the Boost
pointer container library, which solves the problem more elegantly.
4. The current version of the Boost C++ Libraries is 1.33.1 (December
2005). The Boost libraries contain many extensions not included in
TR1. The pointer container library would be applicable in XTide 2.9
to eliminate most remaining cases of explicit memory management.
Boost's date_time library has some overlap with XTide's temporal
classes but is not a substitute for them.
As much as practical, XTide deliberately refrains from depending upon
any extensions to ISO/IEC 14882:2003 (even those built into GCC) and
minimizes its dependencies on external packages that would need to be
installed. Following are the only extensions known to be used in the
default configuration.
- The long long int data type, without which it is impractical to
handle times outside of the 1970-2037 epoch.
- The #warning preprocessor directive. If not supported, the warnings
become errors, which is OK.
- The SCNx8 (probably "hhx") scanf format macro, without which the
code for parsing RGB color specifications is intolerably obfuscated.
XTide Cleanup2006 coding conventions
------------------------------------
I. Conformance
1. Adherence to coding conventions is more important for public
methods, functions and variables than for protected methods and
variables.
2. Adherence to coding conventions is more important for protected
methods and variables than for functions and variables with static
or local visibility.
3. Adherence to coding conventions is least important for the
internals of third-party code and incorporated legacy code.
II. Naming
1. The names of classes, namespaces, and enums begin with a capital
letter. The names of methods, functions, variables, and enum values
begin with a lowercase letter. To distinguish words within names,
CamelCase is preferred.
1a. Exception: If a name contains within it a proper name or
acronym, fudge as needed to preserve the correct capitalization
(e.g., print_iCalendar instead of printIcalendar).
1b. Exception: To avoid clashes with names defined by X11, all
X11-related classes shall be prefixed by "xx" in lieu of "X".
2. If a public name clashes with a protected name, the protected
name shall be prefixed by an underscore.
3. If a public parameter name clashes with a public variable, the
parameter name shall be suffixed by an underscore.
4. If a parameter is used only to contain a return value and its
value on invocation is irrelevant, its name may be suffixed by _out
to clarify this behavior.
5. Scalar typedefs either shall be named something_t consistent
with the libc convention or shall begin with a lowercase letter and
use CamelCase. Typedefs of classes shall begin with a capital
letter and use CamelCase.
6. Where XTide code interfaces with third-party and/or legacy code
that does not adhere to these conventions, naming shall adhere to
the principle of least astonishment.
III. Parameter passing protocol
1. Instances of simple structs and classes that behave like scalar
values may be passed by value. Examples include Amplitude, Angle,
CurrentBearing, Date, Interval, NullableInterval,
NullablePredictionValue, PredictionValue, Speed, Timestamp and Year.
2. Other structs and classes should be passed by const reference or
pointer unless a value is specifically needed.
3. When there are multiple returns and no particular reason to
choose any one of them over any other to be the return value,
declare the method void and use _out parameters for all returns. If
there is one that deserves to be the return value above all others
it is probably a status indicator (success or failure).
4. _out parameters should be at the beginning or end of the
parameter list, not buried in the middle.
IV. Visibility
1. XTide is largely maintained by a single author. Consequently,
it is not necessary to prevent every possible misuse of a class.
Full accessorization to protect class variables from external
modification is done where convenient and where extra protection is
warranted, but need not be done in cases where it would merely
produce more code.
2. Methods that do not depend on the state of an instance in any
way should instead be functions, preferably static functions.
3. Global variables, functions, and enums should be declared within
a namespace.
3a. Exception: Data type operators like < and ==.
3b. Exception: Variables and functions that must be in the
default namespace for integration with third-party and legacy
code.
4. Do not define meaningless classes just to avoid having global
variables and functions. It just creates extra code to always have
an instance of that class around.
V. Choice of scalar data types
1. Float is considered harmful because problems caused by
inadequate precision are difficult to diagnose. Always use double.
2. For general use, default word length integers (int and unsigned
int) are preferred, even if a shorter integer type would suffice.
(Note: At some point in the future, this usage might migrate to
int_fastX_t. For now, it is assumed that plain old ints are more
portable.)
3. In contexts where 16 bits might be insufficient, long ints
should be used. (Note: At some point in the future, this usage
might migrate to int_fast32_t. For now, it is assumed that plain
old long ints are more portable.)
4. Variables that should never hold a negative value (i.e., most
counters and indices) should be declared unsigned if possible.
4a. Exception: Unsigned values should not be used in cases where
losing the sign would mask a failure.
4b. Exception: Unsigned values should not be used if it would
necessitate a special case for 0 (e.g., if logic within a loop
refers to element i-1).
4c. Exception: When interfacing with external functions that use
signed values for no apparent reason, signed values may be used
consistently to avoid mixed-mode computation.
5. For consistency with libc, char is preferred to either signed or
unsigned char when working with text.
VI. Choice of nonscalar data types
1. All homebrew data structures that involve some kind of
self-referential node (linked lists, trees, and every possible
variant) should be reimplemented using standard templates.
2. "Spaghetti monster" data structures for which there is no
standard substitute should be refactored. Usually they are trying
to do several jobs at once that would be more cleanly performed by
several separate data structures.
3. Use of the Standard Template Library is encouraged in general,
but exceptions are noted below.
4. std::vector implements dangerous, unchecked C-language semantics
on operator []. Use SafeVector instead.
The at() method throws std::out_of_range if index >= size(). It
does not appear in the venerable and freely available SGI
Standard Template Library Programmer's Guide (1994?), but it's
there in ISO/IEC 14882:1998, supplementing rather than replacing
the dangerous unchecked operator[].
IMO there is no good reason for operator[] to be unsafe and thus
no reason for at(). If you want C, you know where to find it.
5. Never use std::string. Always use Dstr, or (in rare cases)
SafeVector<char>.
6. std::map is problematic because there is no const [] operator.
Use BetterMap instead.
The STL included with g++ 4.1.0 provides an at() method in both
const and non-const versions that throws std::out_of_range if the
key is not matched. However, at() is not in ISO/IEC 14882:2003
and it's not in the STL provided with g++ 3.4.6.
VII. #defines
1. #defines are a fact of life in a C/C++ environment. Standard
headers are already full of them. Nevertheless, the addition of new
#defines should be approached with caution.
2. #defines do have their uses.
2a. #defines are an appropriate mechanism to receive input from
the configure script, including versioning information and
--enable-X and --with-X configure options.
2b. #defines of actual code macros, as opposed to constant
values, are occasionally useful to condense overly wordy or
repetitive code.
2c. It is sometimes necessary to use a #define instead of a const
variable because the value is needed at compile time. Const
variables can't be initialized in a header file unless they are of
integral or enumeration type. #defines can expand to literals of
any type and can even be stringified (turned into string literals)
at compile time.
2d. The typelessness of #defines is occasionally useful when it
is desirable to apply the default literal typing rules in
different contexts.
3. In other cases, const variables are generally preferable.
4. Global #defines should not use short names that are liable to
collide with other names throughout the program.
VIII. Coding practices
1. Use const.
1a. For input parameters, err or the side of constness.
1b. For return values that are pointers or references, err on the
side of mutability. Returning const references everywhere
complicates integration with standard library functions whose
parameters should be declared const but aren't. As XTide is
largely maintained by a single author, the risk of improper access
to private class variables is not worrisome.
1c. For return values that are just values, use const. This rule
requires extended explanation; see the Appendix below.
1d. A method that changes the state of an instance is not const,
even if said changes are completely invisible outside of the
instance.
1e. Be aware that each level of indirection might require another
const. E.g., char const * const * const argv is not unreasonable.
2. Never cast away const.
3. Namespaces should be specified explicitly, never "use"d, because
qualifiers like Global:: and Error:: are an aid to understanding.
4. Prefix ++ and -- are preferred to postfix ++ and -- because the
postfix operators are believed to create unnecessary temporaries.
5. Explicit memory management (especially the need to free and
delete things) should be avoided as much as possible.
5a. Never new or malloc an array when you can use an auto
SafeVector instead. ISO 14882:2003 Section 23.2.4 states that the
elements of a vector are stored contiguously, so in a pinch they
are convertible to C arrays.
5b. Use std::auto_ptr where appropriate to handle deletion of
objects that are allocated with "new." (It's no good for arrays.)
5c. Exception: Currently there is no good way to manage
collections of non-assignable objects. Standardization of
something equivalent to the Boost pointer container library will
solve this issue.
5d. Exception: Currently there is no good way to manage char**
constructs expected by legacy library functions.
6. Don't make all operators return a reference to "this" just
because that's what everyone always does. If the references are
never used, let the operators return void.
7. Parameters for which the supplied value is always the same
global variable should be eliminated.
8. Temporaries and implicit conversions are not necessarily evil;
i.e., it is not always necessary to provide a method that works on
const char* if you already have a reasonably efficient one that
takes const Dstr&.
9. Where there is the temptation to create an improper inheritance
relationship, implicit conversion should be used instead.
10. Never use C++ streams. Use only C I/O. (This rule would be
inappropriate for many applications, but XTide has been implemented
with streams and without, and it was simpler to get the desired
results from printf than from <<. Moreover, the original streams
library got deprecated soon after I built to it.)
11. Use of "friend" should be minimized.
12. STL algorithms and the associated infrastructure of functors,
adaptors, and so on have many productive uses, but replacing every
simple for-loop with an algorithm call isn't one of them. Function
objects are obfuscatory--a price worth paying in those cases where
an STL algorithm is really needed, but needless overhead where a
simple for-loop would do. This issue goes away when you add lambda
functions, but standard C++ doesn't have those yet. (Boost does.)
IX. Formatting
1. Styling and formatting shall be automated as soon as somebody
writes a version of GNU indent that does something reasonable with
C++.
2. Until then,
2a. Brace style is K&R with the exception that opening function
braces are not placed on a separate line.
2b. Indentation is by 2 characters.
2c. Nominal line length is 80 characters.
2d. Two blank lines between function and method definitions.
X. Grandfathered legacy code
1. Values of Error::TideError are all capital letters with
underscores between words. (These were originally patterned after
VAX/VMS error codes.)
2. The XML parser uses nonstandard data structures. In fact the
XML parser has no right to exist since XML parsing libraries are now
ubiquitous.
3. Dstr is shared by many projects and it will not be changing just
for XTide's coding conventions.
4. Skycal and wvsrtv are large chunks of third-party code and I'm
not going to rewrite them.
XI. Optimization
1. Optimization comes last.
2. Do not create inline methods during development. Wait until the
end, do a performance profile, and then (and only then) inline
*only* those methods that are truly getting hammered. (N.B., in
some cases, circular header file dependencies can make inlining
impossible.)
3. Optimizing header file inclusions to speed up compiles is a nice
concept, but if you have more than a few circular dependencies it's
a waste of time to even try.
Appendix: Why Return Values Are Const
--------------------------------------
Rule VIII.1c papers over an astonishing inconsistency in the C++
language. Consider the following program, which gets its result
through the dubious process of modifying a temporary.
Year currentYear () { return 2007; }
int main (int argc, char **argv) {
printf ("Next year is %u\n", unsigned(++currentYear()));
return 0;
}
If Year is defined as a builtin type (as below), the program will not
compile.
typedef unsigned Year;
// Results in "error: non-lvalue in increment"
However, if Year is instead a class with equivalent behavior (as
below), the program will compile, run, and give the expected output of
2008.
class Year {
public:
Year (unsigned year): _year(year) {}
operator unsigned () { return _year; }
Year operator++ () { ++_year; return *this; }
protected:
unsigned _year;
};
It hardly ever makes sense to modify a temporary. Declaring return
values const prevents it. Unfortunately, consting return values is
sure to confuse the reader regardless how it is implemented.
If return values are made const only when the underlying type is a
class, the resulting interface looks wrong.
typedef double Multiplier;
[...]
const Interval timeAdd() const;
const PredictionValue levelAdd() const;
Multiplier levelMultiply() const;
// ^^^ Multipliers are mutable, but the others are not???
OTOH, consting returns that use builtin types causes astonishment in
those cases where there are no object-returning methods nearby for
comparison.
virtual const bool isBanner() const;
// ^^^^^ Pointless! What idiot wrote this?
XTide 2.9 follows the liberal approach and uses const even in those
cases where it is redundant. The consting of builtin return types
will initially astonish the reader, but a consistent rule simplifies
maintenance. E.g., one need not change the signatures of methods all
over the place when the underlying representation of Year changes from
object to unsigned or vice-versa.