aboutsummaryrefslogtreecommitdiffstats
path: root/source/libmaple/coding-standard.rst
blob: 9ed56cc76cd167e7856ce9f6c440d3b381c76507 (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
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
.. _libmaple-coding-standard:

Coding Standard
===============

This page documents the coding standard for :ref:`libmaple`.  It's
intended as a guide for how you should structure any code you would
like included into the LeafLabs releases of libmaple.

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, follow this guide unless there's a very good reason not
to.  Laziness 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.  (If you notice an inconsistency,
you should fix it).

Note that 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.

Configuration for other editors (vim, etc.) would be nice!

.. contents:: Contents
   :local:

License
-------

.. highlight:: scheme

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
----------

TextMate users may find `this bundle
<https://github.com/glennr/uber-glory-tmbundle>`_ useful for
automatically converting tabs to spaces and removing trailing
whitespace at save time.

- 4 space indents (set in ``.dir-locals.el``).

- Unix newlines.

- 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
  `code-fascism.el <https://github.com/mbolivar/code-fascism>`_.

- 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.

.. highlight:: cpp

- Exactly one space after ``if``, ``else``, ``for``, and ``while``,
  before the following ``{`` or ``(``.  One space before ``else``,
  after the preceding ``}``.  For example::

      // This is good; we like this:
      if (foo) {
          while (quux) {
              bar();
          }
      } else {
          baz();
      }

      // THIS IS BAD! DON'T DO THIS:
      if(foo){
          while(quux){
              bar();
          }
      }else{
          baz();
      }

- Exactly one space in between binary arithmetic, logical, and
  comparison operators and their operands.  Examples::

      // This is good:
      int x = a + b * (c - d);
      if (x != 0 && a > 7) {
          SerialUSB.println(x);
      }

      // THIS IS BAD!
      int x = a+b*(c-d);
      if (x!=0 && a>7) {
          SerialUSB.println(x);
      }

      // This is good:
      uint32 adc_data = ADC1_BASE->DR;
      SerialUSB.println(adc_data);

      // THIS IS BAD!
      uint32 adc_data = ADC1_BASE -> DR;
      SerialUSB . println(adc_data);

- No space between a unary operator and its operand.  Examples::

      // Good:
      x++;

      // BAD!
      x ++;

      // Good:
      y = -x;

      // BAD!
      y = - x;

- If you need to break up a long line:

  * Prefer to break up long expressions after a binary operator.  Example::

      // Good:
      if (some_really_long_conditional_wow_this_really_goes_on_forever ||
          maybe_something_else_could_happen_too) {
          ...
      }

      // BAD!
      if (some_really_long_conditional_wow_this_really_goes_on_forever
          || maybe_something_else_could_happen_too) {
          ...
      }

  * When breaking up a function's arguments over multiple lines, align
    the arguments on subsequent lines with the first argument.
    Example::

      // Good:
      return_type value_i_got = function_with_a_really_long_name(argument1,
                                                                 argument2,
                                                                 argument3);

      // BAD!
      return_type value_i_got = function_with_a_really_long_name(argument1,
          argument2,
          argument3);

      // BAD!
      return_type value_i_got = function_with_a_really_long_name(argument1,
                                                                    argument2,
                                                                    argument3);

- In function invocations, no space in between the function name and
  the opening parenthesis.  Example::

      // Good:
      SerialUSB.println("Hello, world!");

      // BAD!
      SerialUSB.println ("Hello, world!");

- Don't indent C code within a conditionally-compiled ``extern "C"``
  block.  Example::

      // Good:
      #ifdef __cplusplus
      extern "C"{
      #endif

      void some_c_function(void);

      #ifdef __cplusplus
      } // extern "C"
      #endif

      // BAD!
      #ifdef __cplusplus
      extern "C"{
      #endif

          void some_c_function(void);

      #ifdef __cplusplus
      } // extern "C"
      #endif

  Emacs does the "bad" behavior by default, which can be very
  annoying.  You can turn this 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
--------

.. highlight:: c++

- 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 are up to you.

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
------------------

We'll handle the usual casing/underscore debate as follows.

- First, ``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;

  User-facing C++ variables should be camel cased
  (``thisIsAnExample``, ``boardPWMPins``, etc.), for consistency with
  the Arduino style.  It's probably a good idea for you to case
  non-user facing C++ variables in the C style; this will help
  disambiguate what's part of the Wirish API and what's not.

- Classes: Pascal case.  So ``ThisIsAClassName``, but ``thisIsNot``,
  ``this_is_not``, and ``Dont_You_DareTryANYTHING_STUPID``.

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

- Structs: Usually like variables (``adc_dev``, ``adc_reg_map``,
  etc.), but it's not crucial.  Don't feel obliged to put ``_t`` at
  the end of the type name; we don't.

- Macros and constants: all caps, separated by underscores.  C++
  variables with the ``const`` qualifier generally aren't considered
  "constants" for the purposes of this rule; i.e., they are cased
  according to the rules for variables.  We make an exception for
  ``PIN_MAP``, because it's the central Wirish data structure.

- 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::

      // Good:
      void usb_func() { ... }
      void frob_usb_disc() { ... }
      class SomethingUSB {
          void usbInit();
          void initUSB();
      };

      // BAD:
      class BadUsb { ... }; // say "GoodUSB" instead
      void swizzle_USB_disc() { ... } // say "swizzle_usb_disc" instead

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

- Doxygen comments on every user-facing function and type.
  Additionally, individually document the fields and enumerator values
  of nontrivial user-facing structs and enums.  See any register map
  type's definition for an example.

- For libmaple proper, you don't need comments for each register bit
  definition, since that's just repeating information better obtained
  by reading ST RM0008.

- Doxygen comments generally only belong on types, functions,
  etc. that are part of the public user-facing API.

  This essentially means that if what you're writing is going to be
  documented under http://leaflabs.com/docs/ (i.e., if there's `Sphinx
  documentation <http://sphinx.pocoo.org/>`_ for it in the
  `leaflabs-docs <https://github.com/leaflabs/leaflabs-docs>`_
  repository), then you need to write Doxygen comments.  Further,
  those Sphinx docs should use Breathe to pull the Doxygen out.  (For
  more information on this, see the `leaflabs-docs README
  <https://raw.github.com/leaflabs/leaflabs-docs/master/README>`_).

  Because Breathe isn't totally mature yet, you won't always be able
  to do this.  In these cases, document the code "manually" using the
  Sphinx `C and C++ domains
  <http://sphinx.pocoo.org/domains.html#the-c-domain>`_.  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.

  If you do have to document something manually, put a comment in the
  source file informing future maintainers about it, so they'll pay
  extra attention when making changes.

- When adding peripheral support, it would be nice if you put
  longer-form comments into the libmaple ``notes/`` directory, with a
  comment in the corresponding .h file referring to it.  See the
  :ref:`dac.h <libmaple-dac>` source for an example.

  This lets us keep the source files relatively free of "introductory"
  material, while allowing new readers a convenient starting point.
  These longer-form notes also have a habit of turning into official,
  user-facing documentation (or `wiki <http://wiki.leaflabs.com>`_
  pages).

- **For libmaple proper**, the convention is to document any
  user-facing function 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.

  **For Wirish**, the convention is to put the documentation in the
  header file where the function is declared.

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

.. highlight:: scheme

- Keep it 80-column clean.

  Emacs users: this means that the largest column number is 79.  You
  should turn on column number mode to help you out::

    (column-number-mode 1)

  You can get more help from `lineker-mode
  <http://www.helsinki.fi/~sjpaavol/programs/lineker.el>`_.  Just put
  lineker.el somewhere in your load-path, and::

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

.. highlight:: cpp

Language Features
-----------------

In libmaple proper, aim for C99 compatibility.  Some GCC extensions
are OK, but `don't get crazy <http://www.youtube.com/watch?v=jZkdcYlOn5M>`_.

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 conflating objects and libraries),
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 a ``PIN_MAP``.

  * Default arguments: e.g., the timeout argument in
    :ref:`lang-waitforbuttonpress`.

Explicitly forbidden C++ features:

  * Templates

Conditionally allowed C++ features:

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