aboutsummaryrefslogtreecommitdiffstats
path: root/CODINGSTYLE.md
blob: 4bd016efd58fe37041e379518160a1ef3a3612c5 (plain) (blame)
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
# Coding Style

Here are some of the basics that we are trying to enforce for our coding style
and conventions. The existing code (as of the commit that adds these lines) is
not yet fully consistent to these rules, but following these rules will make
sure that no one yells at you about your patches.

We have a script that can be used to reformat code to be reasonably close
to these rules; it's in scripts/whitespace.pl - this script requires
clang-format to be installed (which sadly isn't installed by default on
any of our platforms; even on Mac where clang is the default compiler).

At the end of this file are some ideas for your .emacs file (if that's
your editor of choice) as well as for QtCreator. If you have settings for
other editors that implement this coding style, please add them here.

## Basic rules

* all indentation is tabs (set to 8 char) with the exception of
  continuation lines that are aligned with tabs and then spaces

* all keywords followed by a '(' have a space in between
  ```
	if (condition)

	for (i = 0; i < 5; i++)
  ```

* function calls do NOT have a space between their name and argument
  ```
	i = some_function(argument);
  ```

* usually there is no space on the inside of parenthesis (see examples
  above)

* function / method implementations have their opening curly braces in
  column 1

* all other opening curly braces follow at the end of the line, with a
  space separating them:
  ```
	if (condition) {
		dosomething();
		dosomethingelse();
	}
  ```

* both sides of an if / else clause either use or do not use curly braces:
  ```
	if (condition)
		i = 4;
	else
		j = 6;

	if (condition) {
		i = 6;
	} else {
		i = 4;
		j = 6;
	}
  ```

* use space to make visual separation easier
  ```
	a = b + 3 + e / 4;
  ```

* continuation lines have the operator / comma at the end
  ```
	if (very_long_condition_1 ||
	    condition_2)

	b = a + (c + d +
		 f + z);
  ```

* in a C++ constructor initialization list, the colon is on the same line and
  continuation lines are aligned as the rule above:
  ```
	ClassName::ClassName() : x(1),
		y(2),
		z(3)
	{
	}
  ```

* unfortunate inconsistency
  - C code usually uses underscores to structure names
    ```
	variable_in_C
    ```
  - In contrast, C++ code usually uses camelCase
    ```
	variableInCPlusPlus
    ```
    for variable names and PascalCase
    ```
	ClassInCPlusPlus
    ```
    for names of classes and other types

  where the two meet, use your best judgment and go for best consistency
  (i.e., where does the name "originate")

* there is a strong preference for lower case file names; sometimes conventions
  or outside requirements make camelCase filenames the better (or only) choice,
  but absent such an outside reason all file names should be lower case
  This rule applies to new file names, existing file names should not be
  changed without a reason.

* cpp/h file names should usually be identical to the class name but in lower
  case. Where it seems appropriate, multiple, closely related classes can be
  in a single file with a more generic name.

* switch statements with blocks are a little bit special (to avoid indenting
  too far)
  ```
	switch (foo) {
	case FIRST:
		whatever();
		break;
	case SECOND: {
		int i;
		for (i = 0; i < 5; i++)
			do_something(i);
	}
	}
  ```

## Coding conventions

* variable declarations
  In C code we really like them to be at the beginning of a code block,
  not interspersed in the middle.
  in C++ we are a bit less strict about this - but still, try not to go
  crazy. Notably, in C++ the lifetime of a variable often coincides with the
  lifetime of a resource (e.g. file) and therefore the variable is defined
  at the place where the resource is needed.

* The `*`, `&` and `&&` declarators are grouped with the name, not the type
 (classical C-style) as in `char *string` instead of `char* string`. This
  reflects the precedence rules of the language: `int &i` means that the name
  `i` stands for a reference [to an object with type `int`], not that
  `i` stands for an object of the type [reference to `int`].
  Although this may seem like hairsplitting (both interpretations
  have the same effect) it is crucial in the
  definition of multiple variables, such
  as
	```
	struct dive *next, **pprev;
	```

* In C++ code, we generally use explicit types in variable declarations for clarity.
  Use `auto` sparingly and only in cases where code readability improves.
  Two classical examples are:
  - Iterators, whose type names often are verbose:
	```
	auto it = m_trackers.find(when);
	```
  	is not only distinctly shorter than
	```
	QMap<qint64, gpsTracker>::iterator it = m_trackers.find(when);
	```
  	it will also continue working if a different data structure is chosen.
  - If the type is given in the same line anyway. Thus,
	```
	auto service = qobject_cast<QLowEnergyService*>(sender());
	```
  	is easier to read than and conveys the same information as
	```
	QLowEnergyService *service = qobject_cast<QLowEnergyService*>(sender());
	```
  - If the variable is a container that is only assigned to a local variable to
	be able to use it in a range-based for loop
	```
	const auto l = device.serviceUuids();
	for (QBluetoothUuid id: serviceUuids) {
	```
	The variable has also to be const to avoid that Qt containers will do a
	deep copy when the range bases for loop will call the begin() method
	internally.

* text strings
  The default language of subsurface is US English so please use US English
  spelling and terminology.
  User-visible strings should be passed to the tr() function to enable
  translation into other languages.
  - like this
    ```
	QString msgTitle = tr("Check for updates.");
    ```
  - rather than
    ```
	QString msgTitle = "Check for updates.";
    ```

  This works by default in classes (indirectly) derived from QObject. Each
  string to be translated is associated with a context, which corresponds
  to the class name. Classes that are not derived from QObject can generate
  the tr() functions by using the `Q_DECLARE_TR_FUNCTIONS` macro:
  ```
	#include <QCoreApplication>

	class myClass {
		Q_DECLARE_TR_FUNCTIONS(gettextfromC)
		...
	};
  ```

  As an alternative, which also works outside of class context, the tr()
  function of a different class can be called. This avoids creating multiple
  translations for the same string:
  ```
	gettextFromC::tr("%1km")
  ```

  The gettextFromC class in the above example was created as a catch-all
  context for translations accessed in C code. But it can also be used
  from C++ helper functions. To use it from C, include the "core/gettext.h"
  header and invoke the translate() macro:
  ```
	#include "core/gettext.h"

	report_error(translate("gettextFromC", "Remote storage and local data diverged"));
  ```
  It is crucial to pass "gettextFromC" as a first macro argument so that Qt
  is able to associate the string with the correct context.
  The translate macro returns a cached C-style string, which is generated at runtime
  when the particular translation string is encountered for the first time.
  It remains valid during the whole application's life time.

  Outside of function context, the `QT_TRANSLATE_NOOP` macro can be used as in
  ```
  struct ws_info_t ws_info[100] = {
	{ QT_TRANSLATE_NOOP("gettextFromC", "integrated"), 0 },
	{ QT_TRANSLATE_NOOP("gettextFromC", "belt"), 0 },
	{ QT_TRANSLATE_NOOP("gettextFromC", "ankle"), 0 },
	{ QT_TRANSLATE_NOOP("gettextFromC", "backplate"), 0 },
	{ QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), 0 },
  };
  ```
  Note that here, the texts will be scheduled for translation with the "gettextFromC"
  context, but the array is only initialized with the original text. The actual
  translation has to be performed later in code. For C-code, the `QT_TRANSLATE_NOOP`
  macro is defined in the "core/gettext.h" header.

* UI text style
  These guidelines are designed to ensure consistency in presentation within
  Subsurface.
  Only the first word of multi-word text strings should be capitalized unless
  a word would normally be capitalized mid-sentence, like Africa. This applies
  to all UI text including menus, menu items, tool-tips, button text and label
  text etc. e.g. "Check for updates" rather than "Check for Updates".
  We also capitalize Subsurface (NOTE: not SubSurface) when referring to the
  application itself.
  Abbreviations should end with a period, e.g. "temp." not "temp" for
  temperature
  Numerals in chemical formulae should use subscript characters e.g. O₂ not O2
  Partial pressures in Subsurface are, by convention, abbreviated with a single
  "p" rather than 2, as in pO₂ not ppO₂
  Where more than one term exists for something, please choose the one already
  in use within Subsurface e.g. Cylinder vs. Tank.


* string manipulation
 * user interface
    In UI part of the code use of QString methods is preferred, see this pretty
    good guide in [QString documentation][1]
 * core components
    In the core part of the code, C-string should be used.
    C-string manipulation is not always straightforward specifically when
    it comes to memory allocation, a set of helper functions has been developed
    to help with this. Documentation and usage examples can be found in
    [core/membuffer.h][2]

## Sample Settings

### Emacs

These lines in your .emacs file should get you fairly close when it comes
to indentation - many of the other rules you have to follow manually

```
;; indentation
(defun c-lineup-arglist-tabs-only (ignored)
  "Line up argument lists by tabs, not spaces"
  (let* ((anchor (c-langelem-pos c-syntactic-element))
         (column (c-langelem-2nd-pos c-syntactic-element))
         (offset (- (1+ column) anchor))
         (steps (floor offset c-basic-offset)))
    (* (max steps 1)
       c-basic-offset)))

(add-hook 'c-mode-common-hook
          (lambda ()
            ;; Add kernel style
            (c-add-style
             "linux-tabs-only"
             '("linux" (c-offsets-alist
                        (arglist-cont-nonempty
                         c-lineup-gcc-asm-reg
                         c-lineup-arglist-tabs-only))))))

(add-hook 'c-mode-hook
          (lambda ()
            (let ((filename (buffer-file-name)))
              ;; Enable kernel mode for the appropriate files
                (setq indent-tabs-mode t)
                (c-set-style "linux-tabs-only"))))

(add-hook 'c++-mode-hook
          (lambda ()
            (let ((filename (buffer-file-name)))
              ;; Enable kernel mode for the appropriate files
                (setq indent-tabs-mode t)
                (c-set-style "linux-tabs-only"))))
```

### QtCreator

These settings seem to get indentation right in QtCreator. Making TAB
always adjust indent makes it hard to add hard tabs before '\' when
creating continuing lines. Copying a tab with your mouse / ctrl-C and
inserting it with ctrl-V seems to work around that problem (use Command
instead of ctrl on your Mac)
Save this XML code below to a file, open Preferences (or Tools->Options)
in QtCreator, pick C++ in the left column and then click on Import...
to open the file you just created. Now you should have a "Subsurface"
style that you can select which should work well for our coding style.

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorCodeStyle>
<!-- Written by QtCreator 3.0.0, 2014-02-27T07:52:57. -->
<qtcreator>
 <data>
  <variable>CodeStyleData</variable>
  <valuemap type="QVariantMap">
   <value type="bool" key="AlignAssignments">false</value>
   <value type="bool" key="AutoSpacesForTabs">false</value>
   <value type="bool" key="BindStarToIdentifier">true</value>
   <value type="bool" key="BindStarToLeftSpecifier">false</value>
   <value type="bool" key="BindStarToRightSpecifier">false</value>
   <value type="bool" key="BindStarToTypeName">false</value>
   <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">false</value>
   <value type="bool" key="IndentAccessSpecifiers">false</value>
   <value type="bool" key="IndentBlockBody">true</value>
   <value type="bool" key="IndentBlockBraces">false</value>
   <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value>
   <value type="bool" key="IndentClassBraces">false</value>
   <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
   <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
   <value type="bool" key="IndentEnumBraces">false</value>
   <value type="bool" key="IndentFunctionBody">true</value>
   <value type="bool" key="IndentFunctionBraces">false</value>
   <value type="bool" key="IndentNamespaceBody">false</value>
   <value type="bool" key="IndentNamespaceBraces">false</value>
   <value type="int" key="IndentSize">8</value>
   <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
   <value type="bool" key="IndentSwitchLabels">false</value>
   <value type="int" key="PaddingMode">2</value>
   <value type="bool" key="SpacesForTabs">false</value>
   <value type="int" key="TabSize">8</value>
  </valuemap>
 </data>
 <data>
  <variable>DisplayName</variable>
  <value type="QString">Subsurface</value>
 </data>
</qtcreator>
```

### Vim

As everybody knows vim is a way better editor than emacs and thus needs to be
in this file too. Put this into your .vimrc and this should produce something
close to our coding standards.

```
" Subsurface coding style
filetype plugin indent on
filetype detect
set cindent tabstop=8 shiftwidth=8 cinoptions=l1,:0,(0,g0
" TODO: extern "C" gets indented

" And some sane defaults, optional, but quite nice
set nocompatible
syntax on
colorscheme default
set hls
set is

" The default blue is just impossible to see on a black terminal
highlight Comment ctermfg=Brown

" clearly point out when someone have trailing spaces
highlight ExtraWhitespace ctermbg=red guibg=red

" Show trailing whitespace and spaces before a tab:
match ExtraWhitespace /\s\+$\| \+\ze\t/
```

[1]: http://doc.qt.io/qt-5/qstring.html#manipulating-string-data
[2]: https://github.com/Subsurface/subsurface/blob/master/core/membuffer.h