aboutsummaryrefslogtreecommitdiffstats
path: root/notes/coding_standard.txt
blob: b81847d9f3677c29a99c6579b045800eaf4968e7 (plain)
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
libmaple Coding Standards
=========================

Author: Marti Bolivar (mbolivar@leaflabs.com)

LeafLabs team members are required to follow these when producing new
code.  Community contributors to libmaple are strongly encouraged to
do so; following these rules will greatly increase the probability
that your patches will be folded in.

In general, do it like this unless there's a really good reason why
not.  You being lazy doesn't count as a good reason.  Most, if not
all, of these decisions are entirely arbitrary, but it's important for
readability that we be consistent.

The file .dir-locals.el in the libmaple root directory already ensures
that many of these standards are followed by default in Emacs (but not
on Windows, where it would need to be named _dir_locals.el, and no
way, man).  There's also some elisp scattered about this file which
will provide you additional help.

Vim customizations to do the same thing would be nice (hint, hint)!

License
-------

- Put an MIT license at the beginning of the file (look at any of our
  source files for an example).  Copyright should go either to you or
  to LeafLabs, LLC.

  Emacs: if you don't like seeing the license, you should use
  elide-head (which will hide it for you).  You can use the following:

    (require 'elide-head)
    (setq programming-mode-hooks '(c-mode-hook c++-mode-hook))
    (add-to-list 'elide-head-headers-to-hide
                 '("The MIT License" . "DEALINGS IN\n [*] THE SOFTWARE"))
    (add-to-list 'elide-head-headers-to-hide
                 '("The MIT License" . "DEALINGS IN THE\n...SOFTWARE"))
    (dolist (hook programming-mode-hooks)
      (add-hook hook (lambda () (elide-head))))

Whitespace/Indentation
----------------------

- 4 space indents.  [Set in .dir-locals.el]

- Unix newlines. [Some exceptions are currently grandfathered in;
  these will go away in time.]

- No tab characters. [Set in .dir-locals.el]

- No trailing whitespace.  For help getting this (and no tab
  characters) done automatically in Emacs, you can use this:

    http://github.com/mbolivar/code-fascism

  I hear tell you can get something similar in vim; ask around, I
  guess.

- Files end in exactly one newline. [The presence of a newline at EOF
  is already done by `c-require-final-newline' in recent versions of
  Emacs.]

- Exactly two newlines separate source paragraphs (you do separate
  your code into paragraphs, don't you?).

- The first line in a function is non-blank.

- Don't indent C code within a conditionally-compiled extern "C"
  block.  Emacs does this by default, which can be very annoying; you
  can turn this behavior off with

        (defun c-mode-inextern-lang-hook ()
          (setcdr (assq 'inextern-lang c-offsets-alist) '-))

        (add-hook 'c-mode-hook c-mode-inextern-lang-hook)

Comments
--------

- Multi-line comments are pretty flexible.  Any of these is fine:

    /* Comment starts here.
     * Continued lines have a '*' before them.
     * The comment can end after the last line.
     */

    /* Comment starts here.
     * The comment can end on the same line. */

    /*
     * You can also place a newline after the opening "/*".
     */

- Doxygen comments are multi-line comments that begin with /** instead.

- Single-line comments on the same line are // in C or C++.

- Single-line comments on their own source line should be /* */ in C,
  but can also be // in C++.  In Emacs, you can use M-;
  (comment-dwim), and it'll Do What You Mean.

Braces
------

- Mostly 1TBS:

      http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS

  The only difference is that the opening brace of a function's
  definition occurs exactly one space character after the closing
  parenthesis in that function's parameter list.  Example:

      void func(void) {
          ...
      }

Naming conventions
------------------

There's always a fight about upper and lower case vs. underscores.
We'll handle this as follows.

- First, Dammit_Dont_Mix_Like_This, because It_Looks_Really_Ugly, ok?
  [There's been some debate about this, and some exceptions are
  already grandfathered in, so in order to settle it, let's call this
  a "recommendation" instead of "requirement".]

- Variables: Use underscores to separate words in C identifiers:

    int some_example_name;

  It is strongly advised to do it this way in C++ too, but it's not
  [yet] mandatory.

- Classes: Pascal case.  So ThisIsAClassName, but thisIsNot,
  this_is_not, and like I said, Dont_You_DareTryANYTHING_STUPID.

- Functions: C functions are all lowercase, and words are separated by
  underscores.  C++ method names are camel cased (thisIsAnExample).

- Structs: pick a style from "Variables" or "Classes" depending on how
  you mean it (since it might be either a simple record type, in which
  case do like c variables, or you might be faking an object in c, in
  which case do like classes).  If it's in a typedef, don't feel
  obliged to put "_t" at the end of the name; we don't.

- Macros and constants: all caps, separated by underscores.  Variables
  with the "const" qualifier aren't considered "constants" for the
  purposes of this rule; i.e., case them according to the rules for
  variables.

- foo.h gets #ifdef'ed to _FOO_H_.

- Acronyms: The case of letters in an acronym is determined by the
  case of the first letter in the acronym, which is determined by
  following the above rules.  Examples:

      void usb_func() { ... }

      class SomethingUSB {
          void usbInit();
          void initUSB();
      };

  Never do this:

      class BadUsb { ... }; // say "GoodUSB" instead

Documentation
-------------

- Document your code!

- For complicated peripherals, it would be nice if you put longer-form
  comments into this directory (notes/), with a comment in the
  corresponding .h file referring to it.  See libmaple/dac.h for an
  example.  That lets us keep the source files relatively clean while
  still allowing new readers to have a starting point.

- At least put a Doxygen comment with a nonempty @brief for every .h
  file you add.  See the existing ones for examples.

- Doxygen comments generally just belong on types, functions,
  etc. that are part of the public user-facing API.  This generally
  means that if there's ReST documentation for it under docs/source/,
  it needs Doxygen comments, and that ReST should use Breathe to pull
  that Doxygen comment out. (For more info on this, see docs/README).

  There are some exceptions to this rule since Breathe isn't totally
  mature yet and Sphinx's C++ domain is still in flux.  In these
  cases, document the code "manually" in ReST.

  This should be avoided if at all possible, since it creates a
  maintenance burden of documenting things in two places at once, and
  makes it easier for documentation to go stale.

- For libmaple proper (the pure C library under libmaple/); the
  convention is to document any user-facing entity at the point where
  it is defined.  In particular, this means you should document an
  externally-linked function defined in a .c file in that .c file, not
  in the header file where it is declared to the user.

General Formatting
------------------

- Keep it 80-column clean.  That means Emacs says the largest column
  number=79.  If you haven't already, you should turn on column
  numbers to help you out:

    (column-number-mode 1)

  You can get more help from lineker-mode.  Download it here:

    http://www.helsinki.fi/~sjpaavol/programs/lineker.el

  Then put the file somewhere in your load-path, and:

    (require 'lineker)
    (dolist (hook '(c-mode-hook c++-mode-hook))
      (add-hook hook (lambda () (lineker-mode 1))))

Language Features and Compiler Extensions
-----------------------------------------

- In libmaple proper, aim for C99 compatibility.  Some GCC extensions
  are OK, but let's not go crazy.  

- If you'd like to get code into libmaple which uses a GCC extension
  not already in use elsewhere, ask a LeafLabs developer (or another
  one, if you are one) what they think about it first.

- Explicitly approved GCC extensions:

  * asm volatile:
    http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

  * Nested functions:
    http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html

- In wirish, generally be very conservative when using C++ features
  that aren't part of C.  We are forced to use C++ for Arduino
  compatibility (and the general Arduino style of pretending that an
  object is a library), but it's an angry beast, and we don't want to
  provoke it.  The mantra is "C with classes".

- Explicitly approved C++ features:

  * Initializers that aren't constant; e.g. the gpio_dev* values in
    PIN_MAPs.

  * Default arguments: e.g., the timeout argument defaulting to 0
    (meaning to wait forever) in waitForButtonPress().

- Explicitly forbidden C++ features:

  * Templates

- C++ features that are conditionally allowed, but require explicit
  approval from at least two libmaple developers (one of which may be
  yourself):

  * Operator overloading: Never allowed when it's just for style.
    Potentially allowed when you're implementing a class that models a
    mathematical structure, and you'd like to implement e.g. operator+().