File: STANDARDS

package info (click to toggle)
sfs 1%3A0.8-0%2Bpre20041016.1-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 11,192 kB
  • ctags: 16,793
  • sloc: cpp: 94,493; ansic: 25,454; sh: 9,225; makefile: 871; perl: 676; yacc: 285; python: 146; lex: 100; sed: 67
file content (598 lines) | stat: -rw-r--r-- 20,559 bytes parent folder | download | duplicates (2)
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
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
$Id: STANDARDS,v 1.12 2004/05/14 23:16:36 dm Exp $


		 Coding Standards for the SFS Project


1. Indentation and formating

All code should basically be indented as described in the GNU coding
standards, with the single exception that opening braces should go on
the same line as an if, then, for, while, or do operator.  For the C
subset of C++, this should be identical to the result of running code
through GNU indent with the '-br' option.  Indentation should also
match the result of M-C-h in the C++ mode of a completely uncustomized
emacs.

The following are the most important indentation requirements for SFS
code.  They are an extension of the GNU coding standards.  Some of the
text and some examples are taken from there.

  * Tab stops are every 8 characters.  You don't have to use TAB
    characters, but if you do the code must look properly indented
    with 8 character tab stops.

  * Indentation increments by two spaces, as emacs does by default:

      if (!n)
        return NULL;
      else {
        try (n);
        tryagain (++n);
      }


  * Case statements and goto labels are leftwards indented by two
    spaces unless this would put them in the first column, in which
    case they are only leftwards indented by one space:

      int
      function ()
      {
	// ...
	switch (n) {
	case 3:
	  h = h << 8 | *p++;
	case 2:
	  h = h << 8 | *p++;
	case 1:
	  h = h << 8 | *p++;
	}
 
	if (!n)
	  goto leave;
	else {
	  if (n > 99)
	    goto dontleave;
	  goto leave;
	dontleave:
	  abort ();
	}
 
       leave:
        return 0;
      }


  * Function names and opening braces must always start in the
    leftmost column.  For example:

      static char *
      concat (char *s1, char *s2)
      {
     	...
      }
 
      int
      lots_of_args (int an_integer, long a_long, short a_short,
     		    double a_double, float a_float)
      {
     	...
      }

      int
      myclass::fn (int x)
      {
     	...
      }
 
      template<class T> inline T *
      function (const T &a)
      {
     	...
      }

      template<class T> template<class U> inline bool
      ref<T>::cmp (U &u)
      {
     	...
      }


  * Within parentheses, always indent continuation lines one space
    after the innermost opening paren for the curent scope.

      else if (very_long_function_name (&long_variable_name_x,
					&long_variable_name_y),
	       long_variable_name_x == long_variable_name_y)
        return;


  * For inline functions defined within classes, the starting brace
    should go on the same line as the function name.  As an exception,
    however, functions that fit on a single line can have both braces
    on that line.

      class blah {
        // ...
        bool short_function () const { return true; }

        template<class U> ref<U> medium_function (const ref<U> &r)
          { mytext = r->description (); }

        int long_function (const str &s) {
          if (int r = memcmp (cstr (), s.cstr (), min (len (), s.len ())))
            return r;
          return len () - s.len ();
        }
      };


  * Indent colons two spaces.  Lines in the scope of a colon should
    also be indented the normal two spaces:

      struct acallrpcobj {
        // ...
        acallrpcobj (u_int32_t prog, u_int32_t vers,
	             u_int32_t proc, xdrproc_t inxdr, void *inmem,
	             xdrproc_t outxdr, void *outmem,
	             aclnt_cb cb, AUTH *auth = NULL)
          : rpc2sin (prog, vers, IPPROTO_UDP),
            used (false), proc (proc),
            outxdr (outxdr), outmem (outmem), cb (cb), auth (auth)
          { setmsg (inxdr, inmem); }
      };

      class fookeeper
        : public ihash_core<qhash_slot<foo_number, foo>, &foo::link>,
          virtual public refcount {
        // ...
      };


  * Within template parameters, indent as best you can.  Emacs doesn't
    help much here.  It's fine just to indent two spaces and not match
    the opening '<'.  When breaking a line before '=', as for the
    kludge argument below, the '=' character must be manually indented
    in defiance of emacs.

      template<class K, class V, class H = hashfn<K>, class E = equals<K>,
	class R = qhash_lookup_return<V>,
	ihash_entry<qhash_slot<K, V> > qhash_slot<K,V>::*kludge
	      = &qhash_slot<K,V>::link>
      class qhash
	: public ihash_core<qhash_slot<K, V>, kludge> {
	// ...
      };


  * Put a space between a function name and function call parentheses.
    Put a space after any commas that don't end lines.  Put spaces
    around binary operators wherever possible.  Don't put spaces
    between unary operators and their operands.  Don't put spaces
    around brackets, '::', '.', '.*', '->', or '->*'.  Don't put
    spaces before a '<' character introducing a template argument.

      int res = function (arg1, arg2);
      x = *ip++;
      x = y * z + t[5];
      s = fp->select (1)->name;
      typedef callback<void, const char *>::ref mycb_t;
      ref<int> ri;
      ri = mkobj<int> ();


  * Use the same rules of operator spacing when declaring variables,
    types, or functions.  Put spaces between identifiers and following
    '*' or '&' characters, but not between two of these characters or
    between '*', '&', or 'type::*' and the following identifier.
    Never put a space between the reference operator '&' and the name
    of the reference.

      int *ip;
      char *a, *b;
      char **argv;
      char **&argvref = argv;
      int &i = *ip;
      int (*x)[5];
      void (myclass::*fn) (int &, char *);
      extern int &getreftoint ();
      extern int *getptrtoint ();


  * Never put an unnecessary space between the keyword 'operator' and
    the corresponding operator.

      class myclass {
        int operator++ (int);
        bool operator() (int x, int y) const;
        template<class U> myclass &operator= (const U &);
      };

      bool
      myclass::operator() (int x, int y) const
      {
        ...
        this->operator++ (0);
      }


  * Never, ever have more than 79 characters on a line (not including
    the final new line, but counting any actual TAB characters as
    advancing to the next multiple of 8 characters). Generated source
    code is obviously an exception to this, though where possible code
    generators should obey the other formatting rules.


  * Split lines before a binary operator other than comma, never
    after.  Split after the comma operator or any other use of comma
    such as in function call or template arguments.  Never split
    between a unary operator and its operand.  Never split between a
    function name, function object, or cast and the opening paren that
    follows, unless you absolutely, absolutely need to.

      if (foo_this_is_long && bar > win (x, y, z)
          && remaining_condition)
        do_it ();
      else if (very_long_function_name (&long_variable_name_x,
					&long_variable_name_y),
	       long_variable_name_x == long_variable_name_y)
        return;

      /* It's probably worth a typedef or temporary variable to avoid
       * this kind of monstrosity: */
      shmobject = reinterpret_cast<my_long_object_name::byte_type *>
        (mmap (NULL, implicit_cast<size_t> (shmlen), PROT_READ|PROT_WRITE,
               MAP_FILE|MAP_SHARED, shmfd, 0));


  * Avoid having two operators of different precedence at the same
    level of indentation.  For example, don't write this:

      mode = (inmode[j] == VOIDmode
              || GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j])
              ? outmode[j] : inmode[j]);

    Instead, use extra parentheses so that the indentation shows the
    nesting:

      mode = ((inmode[j] == VOIDmode
               || (GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j])))
              ? outmode[j] : inmode[j]);


  * Insert extra parentheses so that Emacs will indent code
    properly. For example, the following indentation looks nice if you
    do it by hand, but Emacs would mess it up:

     v = rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000
         + rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000;

    But adding a set of parentheses solves the problem:

     v = (rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000
          + rup->ru_stime.tv_sec*1000 + rup->ru_stime.tv_usec/1000);


2. Coding style

  * All code must compile with gcc -Wall -Werror on OpenBSD and
    Linux.  On OpenBSD, code must additionally compile with -ansi
    -Wall -Werror.  (Unfortunately -ansi makes linux even less Posix
    conformant, so it is not required.)  -Wall implies -Wparentheses.
    That means the following valid code will be rejected.

      int x;
      if (x = function ())  // Need extra parentheses
        if (be_careful)
          return;
        else                // Need extra braces
          doit (x);

    You must rewrite this as follows:

      int x;
      if ((x = function ())) {
        if (be_careful)
          return;
        else
          doit (x);
      }

  * Beyond compliance with -Wall -Werror, and parentheses needed for
    emacs to indent code properly, do not add too many gratuitous
    parentheses and braces.  In particular, return doesn't need
    parentheses around the result you are returning, particularly if
    that result fits on one line of code.

  * Declare variables close to their use.  Don't declare int x at the
    top of the function when you only use it at the bottom.  (This is
    a no brainer in C++.)

  * Include variable names in function prototypes whenever they might
    be useful.  Someone familiar with a function who doesn't quite
    remember the order of the arguments should be able to figure it
    out from looking at the header file.  For example, this prototype:

      int tryit (int udpsock, int ntries, int timeout, int msgsize);

    is a lot more useful than this one:

      int tryit (int, int, int, int);

  * Always use "New" with a capital N for allocating objects through
    the default allocator.  This lets the debugging malloc be most
    useful.  Use "new" with a lower-case n when invoking placement new
    or other specialized new operators.

  * Try to avoid old-style C casts like:

      char *p = (char *) malloc (5);

    Use static_cast, const_cast, and reinterpret_cast instead.  You
    can also conceivably use dynamic_cast, but be aware that it can be
    surprisingly expensive (like 400 cycles compared to as little as 0
    for a static_cast).

  * Don't use old-style C++ constructor casts with anything but
    built-in types or types defined in your code.  For instance, don't
    say:

      if (size_t (n) > lim)
        panic ("overflow!\n");

    Autoconf replaces missing system types with #defines instead of
    typedefs.  Thus, even though the above code works on your system,
    it might on another system expand to the illegal code:

      if (unsigned int (n) > lim)
        panic ("overflow!\n");

    To save typing in cases like this, a C cast is probably
    reasonable, though implicit_cast would be better.

  * Don't cast to get an implicit conversion.  If one type can
    implicitly be converted to another, use the template function
    implicit_cast (defined in stllike.h) to trigger the conversion (if
    necessary).  This ensures your code will stop compiling if any
    source change breaks the implicit conversion.  For example:

      template<class T> void
      vector<T>::construct (vector<T>::ptr_type p)
      {
        new (implicit_cast<void *> (p)) T;
      }

    This code is guaranteed not to compile if ptr_type ever becomes
    something one cannot implicitly convert to void *.  Note for the
    record that the implicit cast is required because some overloaded
    ::operator new might do something quite unexpected on a
    vector<T>::ptr_type.

  * Avoid cast operators (member functions like "operator bool ()")
    wherever possible.  When you need implicit conversions, use
    constructors in the converted to class, not operator casts in the
    converted from class.  This sometimes means not being able to cast
    objects to built in types implicitly.  Given the complexity of
    casting interacting with built-in operators and conversions, this
    loss is usually worth it nontheless.

  * Never overload the unary operator&.

  * An assert can be far more useful than a comment, because one knows
    the assert is actually true.

  * Avoid cluttering up code with a lot of comments.  Try to make code
    self explanatory.  If things get hard to follow, break parts of a
    complicated function off into suggestively named inline functions.

  * Never put a comment in a function to explain what code does.  Here
    is a made up example of a worse than useless comment:

      struct dirent *dp = readdir (mydir);
      if (!dp)
        return;
      char *name = dp->d_name;;

      /* Null-terminate the file name if the kernel didn't */
      char namebuf[sizeof (dp->d_name) + 1];
      if (!memchr (dp->d_name, 0, sizeof (dp->d_name))) {
        memcpy (namebuf, dp->d_name, sizeof (dp->d_name));
	namebuf[sizeof (dp->d_name)] = '\0';
	name = namebuf;
      }
      else
        name = dp->d_name;
	
    Well no shit, Sherlock.  I can see you are null-terminating the
    string.  Just why the hell would you do that when the POSIX manual
    clearly states dp->d_name must already be null-terminated?

  * Do put comments in functions if you need to explain why code does
    what it does:

      /* Pre-POSIX systems (notably Ultrix) don't null-terminate
       * filenames of MAXNAMLEN characters. */
      char namebuf[sizeof (dp->d_name) + 1];
      if (dp->d_namlen == sizeof (dp->d_name)) {
        // ...
      }

    Ah, now I understand the code and I won't introduce a bug on
    Ultrix when I change things.  (Note:  This readdir example is
    completely made up.  I know nothing about pre-POSIX readdir.)

  * Sometimes you do want to describe what code does and possibly give
    high-level motivation for the structure of a large amount of code.
    Put such comments at the top of files, where you can go on at
    length about the design of the system, the reasoning behind it,
    and papers to consult for reference.  The point of such a comment
    is to save someone from having to read the code or to ensure they
    have the big picture before having to get into the details.
    Sprinkling such reasoning in comments throughout the code is
    therefore not helpful.

  * Put a comment after every '#else' stating the condition under
    which the subsequent code will be compiled.  Put a comment after
    every '#endif' stating the condition under which the preceeding
    code was compiled.  Thus:

      #ifdef HAVE_CLOCK_GETTIME
      /* ... */
      #endif /* HAVE_CLOCK_GETTIME */

      #ifndef _KERNEL
      /* ... */
      #else /* _KERNEL */
      /* ... */
      #endif /* _KERNEL */

      #ifdef USE_UVFS
      /* ... */
      #else /* !USE_UVFS */
      /* ... */
      #endif /* !USE_UVFS */

  * When ifdefs don't surround much code, indent other preprocessor
    directives to the level of nested conditionals.

      #if __GNUC__ == 2
      # ifdef __cplusplus
      #  if __GNUC_MINOR__ < 91 /* !egcs */
      #   define NO_TEMPLATE_FRIENDS 1
      #  endif /* !egcs */
      #  define PRIVDEST friend class stupid_gcc_disallows_private_destructors;
      # endif /* __cplusplus */
      #else /* !gcc2 */
      # ifdef HAVE_SYS_CDEFS_H
      #  include <sys/cdefs.h>
      # endif /* HAVE_SYS_CDEFS_H */
      # ifndef __attribute__
      #  define __attribute__(x)
      # endif /* !__attribute__ */
      # define PRIVDEST
      #endif /* !gcc 2 */

  * Guard header files against multiple inclusion.  Make each header
    file include whatever header files it needs to compile.


3. Robustness and quality control

  * All code must be tested with dmalloc.  Developers should normally
    configure --with-dmalloc.

  * Design your code so that simple mistakes and typos cause
    compilation errors.  Use the fact that duplicate case labels are
    illegal in C/C++ to check for errors.  For example inline
    functions can check constraints at compile time:

      #define N 2
      // ...
      inline void
      check_n_is_even ()
      {
        switch (0) case (N&1): case 1:;
      }

  * Never write boring or repetitive code.  Not only are you far more
    likely to introduce bugs when writing such code, the bugs will be
    harder to find as they will occur less frequently.  Use templates
    to make code more concise.  If necessary, use perl or write a
    little language to generate repetitive code--even if developing
    code-generating code seems like more work.  Try to ensure that any
    possible bug will show up in every possible case.

  * Try to avoid complex manipulations of raw bytes--such code is
    error-prone and hard to audit.  Use the str, suio, and rxx classes
    wherever possible to try to avoid such manipulations.  Sometimes
    you really have to do something horrible like manipulate a region
    of memory full of variable length streams of unaligned doubles.
    Build a nice abstraction around such code, put it in an
    appropriate library, and write a regression test for it.  The
    regression test will probably take as much effort to write as the
    code.


4. C++ portability and language features

  * Use bzero rather than memset to clear memory, since bzero can be
    implemented in terms of memset but not vice versa.

  * Use memcpy and memmove rather than bcopy.  (Recall that memmove
    handles overlapping regions, while memcpy does not.)  Except for
    bzero, always use ANSI functions rather than old BSD ones---use
    memcmp, strchr, strrchr rather than bcmp, index, and rindex.

  * Do not use exceptions under any circumstances.

  * Do not use template template parameters.  These should not be
    confused with class template arguments which are themselves
    template classes.  Those are okay.  In other words, avoid code
    like this:

      template<class E, template<class C> T> class supervec { ... };

    But you can use template classes as class arguments to templates:

      template<class E> class vector { ... };
      vector<vector<int> > vvi;

  * Do not use the 'export' keyword.

  * Do not use anything from the standard template library.  You may
    use the <new>, <typeinfo>, and <exception> headers, however, as
    these are more part of the compiler than the standard library.
    All programs should link properly without libstdc++.

  * Do not use namespaces or 'using' declarations.  It's okay,
    however, to use the std:: prefix to get symbols out of the few
    permitted standard header files--for instance std::type_info.

  * Do not use static variables in inline functions.  They don't work
    under egcs on all platforms.  For instance, the following code
    will compile and sometimes do the wrong thing:

      template<class T> struct typectr {
        static int &ctr () { static int v; return v; }
      };

  * All code must compile under G++ 2.95 and 3.3 at optimization levels -O0
    and -O2.

  * All code should compile with the KAI KCC compiler.  You don't
    have to test this if you don't have access to the compiler, but
    you must avoid using any GNU extensions.  In particular, code like
    this is not valid C++ code, even if g++ -ansi accepts it without
    warning (c.f. section 14.5.4 of the standard):

      template <int I, int J> struct A {};
      template <int I> struct A<I+5, I*2> {};

  * Do not return void expressions, as KCC cannot handle this.  The
    following valid C++ code will break KCC.

      void f ();
      void
      g ()
      {
        return f ();
      }

  * On a 200 MHz Pentium Pro with 128 Megabytes, any file in the
    source tree must compile (and assemble) in under 10 minutes at g++
    optimization level -O2.  Sometimes this means putting a big effort
    into optimizing compilation time.  It's unfortunate, but we have
    to draw the line somewhere, and 10 minutes is already pushing it.


5. Peculiar requirements of SFS

  * Don't use any floating point in SFS.  SFS doesn't need floating
    point arithmetic.  It needs fast context switch times.

  * Scrub (bzero) any memory that might contain cleartext private key
    material as soon as you are done with it.  Use the function
    str2wstr in wmstr.h.  It modifies a string so that its contents
    will be bzeroed before freeing the data when the reference count
    goes to zero.