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
|
1. General Style
Indentation level is 4. The indentation character is space, regardless of
the overall indentation level. IOW, even if nested indentation causes the
overall indentation level to be greater or equal to 8 use spaces only.
Don't leave trailing white space at the end of lines.
Line length is 80 columns. You need to break up longer lines to multiple
chunks at reasonable boundaries. What counts as a reasonable boundary
depends on what you are breaking up. There is very seldom, if ever, a
good enough reason to break the 80-column line rule.
2. Layout and Tabulation
2.1 Curly Braces
Opening curly braces are put last on the line, while closing curly braces
are on lines of their own. Generally this applies to all control-flow
blocks, IOW to if, for, while, do, and switch statements. The most notable
exceptions are the while in a do-while statement, if-else-if chains and
functions.
Some examples:
if (!condition) {
foo();
foobar();
}
else {
bar();
barfoo();
}
while (check) {
do_something();
and_something_else();
}
Exceptions:
do {
process();
and_more();
} while (need_to);
if (foo < bar) {
foo();
bar();
} else if (bar < foo) {
foobar();
barfoo();
} else {
frobnicate();
xyzzy();
}
In a function you put both the opening and the closing braces on lines
of their own:
int foo(int x)
{
if (x < 10)
return foobar(x);
else
return barfoo(x);
}
If you have a single statement in a block, you can omit the opening
and closing braces. However in an if-else it is preferred to keep the
braces unless both branches are single statements. Also, do not omit
the braces for do-while statements.
Examples:
if (x < 0)
foo(x);
else
bar(x);
Note however, that we prefer to keep braces here:
if (y < 10) {
foo(y);
}
else {
foobar(y);
barfoo(x);
}
do {
x = xyzzy();
} while (x < 10);
2.2 Indentation and Spaces
Individual cases and the default case is indented to align with the
switch statement itself:
switch (foo) {
case 1:
so_its_one();
break;
case 2:
uh_two();
break;
case 3:
oh_boy_three();
break;
default:
oh_no(foo);
}
If you have a more complex set of statements in the cases, it is preferable
to include an extra empty line for clarity before all but the first case and
the default branch.
switch (x) {
case 1:
if (y < 10)
horribly(x);
else if (y < 20)
complex(x);
else if (x + y > 200)
processing(x, y);
break;
case 2:
...
break;
...
default:
...
}
The general rule for space usage is the following:
- use one space after control-flow keywords (if, for, while, do,
switch, case)
- use one space around binary and and ternary operators (=, +, -,
*, /, %, |, &, ^, <, >, <=, >=, ==, !=, ? :)
- don't use space after unary operators (&, *, +, -, ~, !)
- don't use space after the following keywords: sizeof, typeof,
alignof, __attribute__
- don't use spaces before postincrement/postdecrement or pre-
increment/predecrement operators (++, --)
- don't use spaces around structure or union member operators (., ->)
- don't use spaces after the preprocessor directive 'defined'
Deviating from these basic preferences is okay if it helps increasing
readibility, for instance by resulting in more compact code and hence
more code per editor surface area. For example, one might choose an
alternative, more compact layout for a switch statement that consists
only of trivially simple case branches, like this:
switch (f->value.type) {
case SI_TYPE_INT16: f->value.i16 = va_arg(ap, int32_t); break;
case SI_TYPE_UINT16: f->value.u16 = va_arg(ap, uint32_t); break;
case SI_TYPE_INT32: f->value.i32 = va_arg(ap, int32_t); break;
case SI_TYPE_UINT32: f->value.u32 = va_arg(ap, uint32_t); break;
case SI_TYPE_INT64: f->value.i64 = va_arg(ap, int64_t); break;
case SI_TYPE_UINT64: f->value.u64 = va_arg(ap, uint64_t); break;
case SI_TYPE_BOOL: f->value.bln = va_arg(ap, int); break;
case SI_TYPE_DOUBLE: f->value.dbl = va_arg(ap, double); break;
default:
return FALSE;
}
2.3 Accepted Tabulation Schemes
There are two tabulation schemes you can choose from:
1) obsessive alignitis: the manic alignment of variable definitions,
structure and union member definitions, variable assignments within
a code block end so forth
2) traditional K&R-ish reductionist style, minimizing the amount of
white space while adhering to the rules of the above sections
But be consistent with yourself, do not try to mix these. Choose one
and stick with it within a component. Also if you are touching any
existing code obey the existing style.
2.4 Code Layout
Put licensing information to the beginning of every file.
Within a module put includes and macro definitions in the beginning
(of course). Then put type definitions, any necessary static function
prototypes and finally module-local variables before the actual
functions of the module.
Within a function, define variables in the beginning of the function.
You can also put variable definitions at the beginning of code blocks
with curly braces, eg. within if, else, for, while, and switch-constructs.
Please do not pull variables out of your arse^H^H^H^Hstack in the middle
of a function at any other place.
2.5 Error Handling
There are two acceptable error-indication mechanism from functions:
boolean style and libc style. It depends on your component which one makes
sense. If upon failure it is of no use to the caller to get more
information than the mere fact of failure use boolean style error
indication returning true on success and false otherwise. In this case,
include stdbool.h in your public header and use bool as the return type
of your function to indicate the boolean convention to your readers. If
you choose the libc-style, return 0 on success, return -1 on failure and
also set errno to some reasonable error code in this case. Never ever
clear errno on success. Naturally, functions that return objects should
return NULL on failure with both of these styles.
Whenever you have a non-trivial function with complex control-flow try
to organise your code so that there is a single common error cleanup
and return path. You can put this at the end of your function, label it
as 'fail', and use goto's to jump from the middle of the function to the
error-handling branch.
2.6 Function Prototypes
Use function prototypes with variable names in public header files.
3. Naming Conventions
The basic general naming rule is no CamelCase, use only lower-case
letters, use underscore ('_') as a word separator within symbol names
for functions and variables. Macros are all upper-case with the exception
of function-like macros that take arguments. Just like functions, these
can be all-lower case at will if this makes more sense. Typical cases
when this makes sense would be a macro that wraps a function call and
extends the argument list with additional parameters, or a guarding
convenience wrapper macro with extra error checks for calling a function
in some external component that does not handle some common error cases,
such as being called with a NULL pointer on cleanup code paths.
For Murphy core, all externally visible symbols must be prefixed with
mrp_ to avoid name-space collisions with other components.
3.1 Type Names
Externally visible types (at least structs, unions and enums) must be
typedef'd. The chosen type name must be prefixed consistently with the
other publicly visible types from the same component. All typedef'd
names are suffixed with _t. If you need to also have a non-typedef'd
struct, or union for internal use for some reason, suffix it with _s
or _u depending on its type.
3.2 Function and Variable Names
All externally visible functions and variables must be prefixed with
a component-specific prefix-. For Murphy core this is mrp_. For libraries
that can be easily re-used without Murphy, you can chose any prefix you
like, but keep it short, preferably up to 3 or 4 characters max.
For global variables always use fulle/long descriptive names, keeping in
mind the lower-case only and underscore restrictions. For local variables
and function arguments try to strike the right balance between compactness
and descriptiveness. On one hand too long names hurt readability and waste
a lot of precious real estate on our 80 character character terminals from
the 70's. On the other hand too short or unintuitively shortened names
fail to provide the right association about the usage of the variables for
the reader. So this is not that black and white and it is not easy to give
a set of strict rules that one could blindly follow. If someone familiar
with the subject your code is addressing gets constantly confused with
what your variables are for, you are probably overdoing the compactness
part. If the same person gets constantly frustrated/tired reading your
variable names, you are probably underdoing it...
Library global function and variable names, ie. symbols whose visibility
is limited to the scope of the library, should be prefixed with the module
prefix within the library they are defined in.
3.3 File Naming Conventions
Don't prefix your file names with a component name. Use dashes instead
of underscores in file and directory names.
4. Miscallanea
4.1 Use of Goto
There are cases when the careful use of gotos make the code both easier
to read/follow and less error-prone to modify/extend than with any of
the alternatives. Typical examples are consolidated bailout/cleanup code
on error paths from complex functions with nested blocks of if/for/while
statements. Also it is often cleaner to use directly gotos instead of
emulating them with an while-(0)-break non-loop construct.
Generally speaking, it would be beneficial to come up with a basic set
of rules that can also be expressed as a set of command line options to
indent (or any other available alternative). That would provide a canonical
way for people to check their code for indentation-conformance. Also we
could use it ourselves in git hooks to warn about code with non-conformant
style which otherwise might be difficult to avoid in the beginning.
|