-
Notifications
You must be signed in to change notification settings - Fork 64
/
CODINGSTYLE
449 lines (341 loc) · 15.4 KB
/
CODINGSTYLE
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
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
This is the CODINGSTYLE file for the Rivendell package.
OVERVIEW:
This file, CODINGSTYLE, describes the coding style guidelines for writing
new code, how to submit patches to be incorporated into the official
Rivendell Git repository, and other code related information. General info
on the Rivendell project can be found at the http://www.rivendellaudio.org/
web page and also in the 'README' and 'INSTALL' files.
The code style used for Rivendell is a somewhat idiosyncratic mixture of
the style generally used for Qt C++ programs combined with the classic UNIX
C style. Some of the specifics include:
LINE LENGTH:
Should not be longer than 78 characters unless doing so would severely
compromise the readability of the code. Where it is necessary to break a
line, it should be done at a point that preserves maximum clarity and ease
of reading.
Good:
*report+=QString(" ")+
logLine(i)->startTime(RDLogLine::Logged).toString("hh:mm:ss")+
QString::asprintf(" - cart %06d [",logLine(i)->cartNumber())+
q->value(1).toString()+"] "+QObject::tr("is not playable")+"\n";
Bad:
*report+=QString(" ")+logLine(i)->startTime(RDLogLine::Logged).toString("hh:mm:ss")+QString::asprintf(" - cart %06d [",logLine(i)->cartNumber())+q->value(1).toString()+"] "+QObject::tr("is not playable")+"\n";
INDENTATION:
Should be two spaces per level. This helps to keep the line length down.
Good:
if(to_line<0) {
to_line=size();
for(int i=from_line;i<size();i++) {
if(logLine(i)->timeType()==RDLogLine::Hard) {
to_line=i;
i=size();
if(sched_time!=NULL) {
*sched_time=logLine(i)->startTime(RDLogLine::Logged);
}
}
}
}
Bad:
if(to_line<0) {
to_line=size();
for(int i=from_line;i<size();i++) {
if(logLine(i)->timeType()==RDLogLine::Hard) {
to_line=i;
i=size();
if(sched_time!=NULL) {
*sched_time=logLine(i)->startTime(RDLogLine::Logged);
}
}
}
}
CURLY BRACES:
Conditional statements (such as 'if' and 'for') should *always* use curly
braces, even where the affected block is but one line long. The opening
brace should be on the same line as the conditional and the closing one on
a line by itself. This style greatly facilitates debugging, allowing a
single line to be provisionally commented out or additional lines to be
provisionally added without making the enclosing conditional syntactically
invalid.
Good:
if(i==12) {
printf("i is equal to twelve!\n");
}
Bad:
if(i==12)
printf("i is equal to twelve!\n");
PADDING WHITESPACE:
Wherever possible, there should be no whitespace between constants/variables
and operators. This helps to keep the line length down.
Good:
for(int i=from_line;i<size();i++) {
if(logLine(i)->timeType()==RDLogLine::Hard) {
to_line=i;
}
}
Bad:
for(int i = from_line; i < size(); i++) {
if(logLine(i)->timeType() == RDLogLine::Hard) {
to_line = i;
}
}
CLASS NAMES:
Should have the initial letter of each word capitalized, e.g. 'ThisIsMyClass'.
If the class is part of the librd convenience library (in 'lib/'), the name
should be prefaced with the uppercase letters 'RD', e.g. 'RDThisIsMyClass'.
METHOD NAMES:
Public method names as well as signal and slot method names should follow
the general style used by Qt. A special convention for Rivendell is to
reserve names beginning with an uppercase letter for private methods only.
Good:
class RDLogPlay
{
Q_OBJECT
public:
RDLogPlay(int id,RDEventPlayer *player,QObject *parent=0);
QString serviceName() const;
void setServiceName(const QString &svcname);
private slots:
void transTimerData();
void graceTimerData();
signals:
void renamed();
void reloaded();
void transportChanged();
private:
bool StartEvent(int line,RDLogLine::TransType trans_type,int trans_length,
RDLogLine::StartSource src,int mport=-1,int duck_length=0);
bool StartAudioEvent(int line);
};
Bad:
class RDLogPlay
{
Q_OBJECT
public:
RDLogPlay(int id,RDEventPlayer *player,QObject *parent=0);
QString servicename() const;
void set_service_name(const QString &svcname);
private slots:
void TransTimerData();
void grace_timer_data();
signals:
void RENAMED();
void Reloaded();
void transport_changed();
private:
bool startEvent(int line,RDLogLine::TransType trans_type,int trans_length,
RDLogLine::StartSource src,int mport=-1,int duck_length=0);
bool startAudioEvent(int line);
};
VARIABLE NAMES:
*All* variables should be lowercase only, with uppercase being reserved for
class and method names. Words should be separated by underscores.
Good:
int log_position_number=1;
Bad:
int logPositionNumnber=1;
Class variables should be prefaced with a short base name that is common to
all, followed by an underscore. For example, the class 'MyClass' might use
'myclass_', as in 'myclass_foo1', 'myclass_foo2', etc. Local variables
(including function parameter names) should be kept short, preferably a
single word.
NULL POINTERS
Null pointers should always be represented by using the 'NULL' keyword,
rather than 0.
Good:
int *foo=NULL;
if(foo==NULL) {
printf("The foo variable is null!\n");
}
Bad:
int *foo=0;
if(foo==0) {
printf("The foo variable is null!\n");
}
SQL STATEMENTS:
When embedding SQL statements in code, the following guidelines should be
followed:
1) All identifier fields should be enclosed in backticks (ASCII 0x2C)
characters (NOT to be confused with apostrophes, see below).
Good:
sql="select `FIELD1`,`FIELD2` from `MY_TABLE` where `ID`=2";
Bad:
Good:
sql="select FIELD1,FIELD2 from MY_TABLE where ID=2";
2) All string literals should be delimited with the apostrophe character,
*not* quotes. The previous use of quotes is a MySQL-ism that is now strongly
discouraged.
Good:
sql="select `FIELD1` from `MY_TABLE` where `FIELD2`='foobar'";
Bad:
sql="select `FIELD1` from `MY_TABLE` where `FIELD2`="foobar";
3) All identifiers are uppercase-only, while SQL operators
should be all lowercase. This helps improve readability.
Good:
sql="select `FIELD1`,`FIELD2` from `MY_TABLE` where `ID`=2";
Bad:
sql="SELECT `FIELD1`,`FIELD2` FROM `MY_TABLE` WHERE `ID`=2";
4) Long or complex SQL statements should be broken into multiple lines in
a manner to enhance the readability of both C++ and SQL. For 'select'
queries that return more than two fields per row, each field should be
commented with its ordinal number to assist in determining the
appropriate value to give to 'RDSqlQuery::value()' for referencing
the fields.
Good:
sql=QString("select ")+
"`CART`.`TITLE`,"+ // 00
"`CART`.`ARTIST`,"+ // 01
"`CART`.`PUBLISHER`,"+ // 02
"`CART`.`COMPOSER`,"+ // 03
"`CART`.`USAGE_CODE`,"+ // 04
"`CUTS`.`ISRC`,"+ // 05
"`CART`.`ALBUM`,"+ // 06
"`CART`.`LABEL`,"+ // 07
"`CUTS`.`ISCI`,"+ // 08
"`CART`.`CONDUCTOR`,"+ // 09
"`CART`.`USER_DEFINED`,"+ // 10
"`CART`.`SONG_ID`,"+ // 11
"`CUTS`.`DESCRIPTION`,"+ // 12
"`CUTS`.`OUTCUE` "+ // 13
"from `CART` left join `CUTS` "+
"on `CART`.`NUMBER`=`CUTS`.`CART_NUMBER` where "+
"`CUTS`.`CUT_NAME`='"+RDEscapeString(button->cutName())+"'";
Bad:
sql="select `CART`.`TITLE`,`CART`.`ARTIST`,`CART`.`PUBLISHER`,`CART`.`COMPOSER`,`CART`.`USAGE_CODE`,`CUTS.ISRC`,`CART.ALBUM`,`CART.LABEL`,`CUTS`.`ISCI`,`CART`.`CONDUCTOR`,`CART`.`USER_DEFINED`,`CART`.`SONG_ID`,`CUTS`.`DESCRIPTION`,`CUTS.OUTCUE` from `CART` left join `CUTS` on `CART`.`NUMBER`=`CUTS`.`CART_NUMBER` where `CUTS`.`CUT_NAME`='"+RDEscapeString(button->cutName())+"'";
SCHEMA CHANGES:
Changes that alter the schema of the database must include:
A) Incrementation of the 'RD_VERSION_DATABASE' value in 'lib/dbversion.h'.
B) Code that implements the schema change, in the
'utils/rddbmgr/updateschema.cpp' file.
C) Code that implements an exact, perfect reversal of the schema change,
in the 'utils/rddbmgr/revertschema.cpp' file.
D) Updating of the schema map in the 'MainObject::InitializeSchemaMap()'
method in the 'utils/rddbmgr/schemamap.cpp' file. (See the comment at
the top of the method definition for details).
E) Update the table documentation appropriately in 'docs/tables/'.
F) [VERY IMPORTANT] - Document the changes to the schema in your
'ChangeLog' entry, including the new value of 'RD_VERSION_DATABASE'.
FONT MANGEMENT:
All fonts used in Rivendell are managed by the RDFontEngine class, which
provides them on the basis of abstract "roles" so as to provide consistency
across the entire set of modules. See the file 'docs/apis/fonts.pdf' for
a detailed description of this sub-system, including screenshot examples.
WRITING TO THE SYSLOG:
Rivendell makes extensive use of the syslog(3) system found on all
POSIX-compliant systems. Sending messages to the syslog should almost
always be done by means of the following method in 'RDApplication':
void RDApplication::syslog(int priority,const char *format,...)
The exception to the above is when the application context does not
include a global RDApplication object --e.g. in caed(8). For those
cases, the following static RDApplication method should be used:
void RDApplication::syslog(RDConfig *c,int priority,const char *format,...)
For a discussion of the parameters of these methods, see the syslog(3) man
page. The 'priority' parameter should be one of the following values:
LOG_ERR - Indicates that a fatal error has occurred; 'fatal' meaning
that the program is unable to continue and will now exit.
LOG_WARNING - Indicates that a non-fatal error has occured; meaning
that the program will continue to operate, but with
possibly significant operational degradation. This would be
appropriate for things like failure to connect to an
external switcher or other device.
LOG_INFO - Information useful for tracking operational state --e.g.
a cart play-out has begun, a new Rivendell log has been
loaded, etc.
LOG_DEBUG - Information useful for tracking or verifying Rivendell
software internal state. These messages will not normally
be seen by users, but can be made visible to allow for
program debugging.
NOTE: A 'facility' value should *not* be included in the 'priority'
argument. The appropriate 'facility' value, as determined by the
'SyslogFacility=' directive in rd.conf(5), will be added automatically.
DATE/TIME FORMATS:
The formats of date/time values presented in the Rivendell's GUI modules
are user-specifiable in RDAdmin->SystemSettings. Thus, when handling such,
the following facilities are available:
Value Inputs. The following classes are available:
RDDateTimeEdit - Subclass of QDateTimeEdit
RDDateEdit - Subclass of QDateEdit
RDTimeEdit - Subclass of QTimeEdit.
While the underlying Qt 'setDisplayFormat()' method remains available
in these subclasses, *it should never be used*, as the underlying display
formats are automatically derived and applied. In addition, these classes
have a number of additional methods for handling Rivendell-specific
situations (times to tenths-of-second precision, read-only states, etc).
See the specific headers in 'lib/' for details.
Value Outputs. The following methods are available for easily rendering
properly formatted date/time strings in GUI module contexts:
RDCoreApplication::timeString(const QTime &time,
const QString &padding="") const;
RDCoreApplication::tenthsTimeString(const QTime &time,
const QString &padding="") const;
RDCoreApplication::shortDateString(const QDate &date) const;
RDCoreApplication::longDateString(const Date &date) const;
The methods above that involve time all include support for optionally
specifying times with tenth-second precision. At present, the only place in
Rivendell where this facility is used is when specifying the 'Start Time' and
'Grace Time' parameters in Rivendell logs. All other contexts should present
times to single-second precision.
These methods can be easily accessed in GUI module contexts via the global
'rda' pointer, as illustrated in the following code snippet:
*** snip snip ***
#include <rdapplication.h>
[..]
QDate my_date(2021,8,13); // 13 August 2021
QString my_string=rda->shortDateString(my_date);
*** snip snip ***
N.B. - These date/time formats are for use when interacting with users in
a GUI context. They should *not* be used in other contexts, including
(but not necessarily limited to):
1) SQL Statements. Standard SQL date-time formats apply, and should
be hard-coded appropriately.
2) Command-line Utilities. In the interest of providing stable interfaces
for scripting purposes, date/time values in Rivendell command-line
tools generally use ISO8601-compliant formats. Where this varies
(due to application requirements), it should be documented in the
appropriate manpage.
3) Standardized APIs. Data interfaces for external systems (JSON, XML,
etc) should continue to use the formats appropriate for those
contexts.
4) Syslog Messages. Any date/time references sent to syslog(3) should
use ISO8601-compliant formats.
CONTRIBUTING CHANGES:
The primary code repository for Rivendell resides at GitHub, and can be
found at:
https://github.com/ElvishArtisan/rivendell
Changes should be submitted in the form of a pull request [PR] against
the default branch ('v4' as of this writing). Information about drafting and
submitting PRs can be found at:
https://help.github.com/en/articles/about-pull-requests
PULL REQUEST CHECKLIST:
Before submitting a pull request, the following guidelines should be
completed:
1) The code should compile without errors or warnings [the '-Werrors' switch
for gcc(1) is your friend here!].
2) Add an entry to the 'ChangeLog' file at the base of the Rivendell source
code tree, describing your changes. The format of the ChangeLog file has
the most recent changes at the bottom of the file. Entries start with a
date stamp and have a format like:
YYYY-MM-DD NAME <EMAIL>
* Description of change
For example:
2007-02-23 John Coder <[email protected]>
* Modified the code in 'lib/rdimport_audio.cpp' to use the
'RDCart::setMetadata()' and 'RDCut::setMetadata()' methods.
3) If your changes add, remove or change the behavior of one or more
configuration entries in rd.conf(5), update the default configuration
file ('conf/rd.conf-sample') accordingly. Similiarly, for PyPAD scripts,
be sure to update the corresponding '<plugin-name>-exemplar' files.
4) If your changes alter any user-visible aspect (UI or behavior), update
the user documentation appropriately. The documentation is written
in DocBook 5 markup, and can be found at the following locations in
the source tree:
Operations Guide - 'docs/opsguide/'
Manual pages - 'docs/manpages/'
Internal and public APIs - 'docs/apis/'.
5) If you wish your work to be mentioned in the 'Credits' list displayed in
rdadmin(1), add or modify the appropriate entry in the 'AUTHORS' file.
Entries should be sorted by surname, then christian name of the author.
QUESTIONS:
Questions about coding style, or indeed any aspect of Rivendell development,
are welcomed on the Rivendell-prog mailing list. Subscription information
and list archives are available at:
http://caspian.paravelsystems.com/mailman/listinfo/rivendell-prog