summary refs log tree commit diff
path: root/www/text.causal.agency/020-c-style.7
blob: 9816dbc39f54fc115b84ba14c5a557f94858c18b (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
.Dd March 16, 2021
.Dt C-STYLE 7
.Os "Causal Agency"
.
.Sh NAME
.Nm C Style
.Nd a rough description
.
.Sh DESCRIPTION
This is a rough description
of the style in which I write C,
since it's uncommon
but some people seem to like it.
I don't have any hard rules,
it just needs to look right.
.
.Ss Superficialities
I use tabs
and they're set to 4 characters wide
in my editor.
I keep my lines shorter than 80 columns,
which I enforce by
not resizing my terminal's width.
I use block indentation only,
meaning I write long function calls
like this:
.Bd -literal -offset indent
fprintf(
    imap.w, "%s UID THREAD %s UTF-8 %s\er\en",
    Atoms[thread], algo, search
);
.Ed
.Pp
Anything that can be sorted
should be sorted,
with trailing commas where possible.
This and block indentation
make for simpler diffs.
.Pp
I either write single-line ifs
or always use braces.
I put parentheses
around ternary expressions.
I use camelCase
for functions and variables,
and PascalCase for types and constants.
When an acronym appears
in an identifier,
it's in either all lower case
or all upper case.
The despicable SCREAMING_SNAKE_CASE
is reserved for macros.
I don't set globals or statics to zero
since that is already the default.
I don't compare against zero or NULL
unnecessarily.
.
.Ss \&No side-effects in control flow
I never write a function call
with side-effects
inside the condition of an if statement.
I find this makes following the
.Dq happy path
through functions
much easier.
I write things like this:
.Bd -literal -offset indent
pidFile = open(pidPath, O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
if (pidFile < 0) err(EX_CANTCREAT, "%s", pidPath);

error = flock(pidFile, LOCK_EX | LOCK_NB);
if (error && errno != EWOULDBLOCK) err(EX_IOERR, "%s", pidPath);
if (error) errx(EX_CANTCREAT, "%s: file is locked", pidPath);
.Ed
.Pp
I do write side-effects
inside for and while statement heads,
since that's generally expected.
For some reason
I like to write the constant first
if I'm comparing the result of an assignment
with a side-effect.
.Bd -literal -offset indent
for (ssize_t len; 0 <= (len = getline(&buf, &cap, file)); ++line)
.Ed
.
.Ss Paragraphs
I leave blank lines
between logical chunks of
.Dq things happening .
This is usually between side-effects
with their related error handling,
or between groups of closely related side-effects.
I try to keep variable declarations
glued to the top of the bit of code
they're used in.
.
.Ss Leading break
I've mentioned this previously.
I write my switch statement breaks
before each case label.
Doing this aligns nicely,
and being in the habit
means I always avoid
accidental fallthrough.
.Bd -literal -offset indent
switch (opt) {
    break; case 'a': append = 1;
    break; case 'd': delay = strtol(optarg, NULL, 10);
    break; case 'f': watch(kq, optarg);
    break; case 'i': init = 1;
    break; default: return EX_USAGE;
}
.Ed
.
.Ss Function type definitions
Function types are always typedef'd,
and it's the function type itself
that is defined,
not a function pointer type!
I put the typedef above any functions
that are supposed to be of that type
so it's clear what the pattern is.
.Bd -literal -offset indent
typedef void Action(struct Service *service);
Action *fn = NULL;
.Ed
.
.Ss Constants
I prefer enums over #defines
for integer constants,
and static const strings over #defines
unless I want to do concatenation.
.Bd -literal -offset indent
enum { Cap = 1024 };
.Ed
.Pp
I avoid the preprocessor
wherever possible,
with the notable exception of X macros,
which I've talked about previously.
Doing things in the actual language
makes for easier debugging.
.
.Ss Organization
I usually use only one header file
in each project.
The dependency is easy to declare
and the complete rebuild
when the header changes
isn't a problem for small projects.
Unless it's a single-file program,
I name the file which contains main
something generic,
since the name of the project
isn't relevant to its function.
I name functions like
.Ar nounVerb ,
and all the functions for a 
.Ar noun
are defined in
.Pa noun.c .
Not really to do with C,
but I always put a FILES section
in my README pages
to briefly describe
the layout of the code
for anyone looking to
read or make changes to it.
.
.Sh AUTHORS
.An june Aq Mt june@causal.agency