Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
yam/STYLEGUIDE
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
736 lines (543 sloc)
23.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*************************************************************************** | |
YAM - Yet Another Mailer | |
Copyright (C) 1995-2000 by Marcel Beck <mbeck@yam.ch> | |
Copyright (C) 2000-2022 YAM Open Source Team | |
This program is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation; either version 2 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
YAM Official Support Site : http://www.yam.ch/ | |
YAM OpenSource project : http://sourceforge.net/projects/yamos/ | |
$Id$ | |
***************************************************************************/ | |
This document is meant to be a general coding styleguide for development on | |
YAM. It carries information on how to structure and layout the source code | |
during development. All registered developers of YAM should at least have | |
read once through this guide and stick to the particular rules and | |
suggestions on how to structure particular code passages. That way future | |
developers may more easily find a way into the complex source code of YAM | |
and take it as a reference on how to find out the YAM developers expect | |
their source code to look like. | |
So please, any YAM developer, try to stick to the rules listed in this guide | |
as much as possible so that the code will stay readable even if more than | |
one developer is working on a certain section. | |
YAM Development Team | |
March 2006 | |
############################################################################ | |
1. Indentation - Tabulator use (TAB) | |
------------------------------------ | |
In the past we had too much trouble with excessive tabulator use in the | |
source code of YAM. Therefore, any use of tabulator characters (0x09) to | |
structure the source code should be considered FORBIDDEN. Instead, we agree | |
that for indentation of source code we use TWO (2) spaces (0x20) instead: | |
Example: | |
win = OpenWindowTags(NULL, | |
WA_Title, "Test", | |
WA_InnerWidth, width, | |
WA_InnerHeight, height, | |
TAG_DONE); | |
^^^^^^^^^^^^^^^^^^^^^^^^ - 24 (12x2) spaces / *NO* tabs | |
############################################################################ | |
2. Blocks | |
--------- | |
Blocks start on a new line, with the same indentation as the previous line, | |
and end likewise. Example: | |
for(i=0; i < 10; i++) | |
{ | |
...code... | |
} | |
The braces can be omitted for one-line blocks. E.g. | |
for(i=0; i < 10; i++) | |
array[i] += 30; | |
One exception from this rule is the do-while. These should be formatted like | |
this: | |
do | |
{ | |
...code... | |
} | |
while(a < 10); | |
############################################################################ | |
3. if/then | |
---------- | |
Normal block rules apply. However, if both branches of an if-else statement | |
are one line blocks, then they should be formatted like this: | |
if(a < 20) | |
Foo(); | |
else | |
Bar(); | |
If-then-else-if should be merged like this: | |
if(a < 20) | |
{ | |
...code... | |
} | |
else if(a < 40) | |
{ | |
...code... | |
} | |
All blocks in a merged if-then-else-if sequence should share the same style | |
when it comes to have their braces or not, where the latter is then only | |
possible when all branches are one line blocks. | |
############################################################################ | |
4. Tuples | |
--------- | |
A tuple is one parenthesis immediately followed by first member. If more | |
members are present, they should be separated with ", ". Example: | |
(unsigned long first, unsigned long second, unsigned long last) | |
^ ^ | |
+---------------------+- note the spaces here ! | |
############################################################################ | |
5. Functions | |
------------ | |
Functions are always proper case, meaning the first letter is a capital | |
letter. If the function name is made up of several words, they should | |
also start with a capital letter. | |
There is one space after the return type, unless this is a pointer | |
(marked by an asterisk), then the space is between the type and the | |
asterisk. There is NO space between the function name and the opening | |
pharatesis | |
There is exactly one blank line between functions. | |
Sample functions: | |
int AllocateBuffer(unsigned long size) | |
{ | |
...code... | |
} | |
or | |
char *AllocateArray(unsigned long size) | |
{ | |
...code... | |
} | |
############################################################################ | |
6. return | |
--------- | |
A function should only have AN exit point at the end of that function. | |
An exception to this rule is resource allocation or similar sanity checks | |
placed at the beginning of the function (at outermost scope). Here one can | |
immediately return a failure. Example: | |
BOOL IsLegal(char *str) | |
{ | |
if(str == NULL) | |
return FALSE; | |
...code... | |
return res; | |
} | |
The rule says »...*AN* exit point at the end« rather than *one*. This allows | |
to make a function with several exit points all placed at the end, like this: | |
BOOL IsWhitespace(char *character) | |
{ | |
if(character == ' ' || ...) | |
return TRUE; | |
else | |
return FALSE; | |
} | |
Just for the record: return is a flow-control-directive similar to case, | |
break, continue and goto, and therefore the value provided should not be | |
enclosed between parenthesis. | |
############################################################################ | |
7. No "goto" statements | |
----------------------- | |
Because general goto usage tend to obfuscate source code and end up in | |
"spaghetti code" goto statements/use YAM should generally be avoided. Even | |
if that ends up in more deep if() arrangements, we believe it is a better | |
practise to restructure your source code instead of being lazy and entering | |
a goto statments together with a label. | |
############################################################################ | |
8. No "typedef" statements | |
-------------------------- | |
As a general "typedef" usage tends to obfuscate source code and make it | |
unreadable for the highly trained developer, we believe we should avoid | |
using typedef's at all. There are plenty of other ways to achieve the very | |
same (espcially with enums). So please consider NOT using any own typedef | |
statements in your code and keep on using the native types. | |
############################################################################ | |
9. Use "enum" types wherever possible | |
------------------------------------- | |
Instead of using a plain "int" type variable, enumeration types (enum) | |
should be preferred. Even for local status variables enums should be | |
preferred over plain "int" status variables. So, by no means you should use | |
an integer to keep the status of a certain operation. | |
So, DON'T do something like: | |
BOOL IsRed(int color) | |
{ | |
if(color == 0) | |
return TRUE; | |
} | |
A proper version of the same function would be: | |
BOOL IsRed(enum Color color) | |
{ | |
if(color == CL_Red) | |
return TRUE; | |
} | |
In addition you should also use enum types in function definitions and don't | |
simply define them as an "int" variables. | |
So, DON'T do something like: | |
BOOL IsRed(int color) | |
{ | |
if(color == CL_Red) | |
return TRUE; | |
} | |
But use "enum Color color" as shown in the second example above. By using | |
enum types in your functions you make it more easy for a developer to | |
understand and track the meaning of the variable. In addition, by using | |
enum types in functions you will allow the compiler to warn you in case | |
you may have forgotten to catch a use case (e.g. in switch statements). | |
############################################################################ | |
10. switch/case | |
--------------- | |
A switch case looks like this: | |
switch(value) | |
{ | |
case ONE: | |
{ | |
...code... | |
} | |
break; | |
case TWO: | |
{ | |
...code... | |
} | |
// continue | |
default: | |
{ | |
...code... | |
} | |
break; | |
} | |
If a case needs to fall through to the next one, then a comment should be | |
put instead of the break, as in the example above. The text within this | |
comment is situation-specific, but generally 'continue' will do. | |
The case follows normal block rules, but extends the rule about braces | |
being optional for one-line blocks. | |
Whenever possible, a switch-case should use symbolic names rather than | |
numbers. This also applies to state-machines implemented through | |
if-then-else-if, even if there are only two states. | |
If a set of cases exists with only one command, then one can write a | |
block of compact cases. The colons and semi-colons should be aligned | |
with one space after each and one space in front of each on the longest | |
line. It is allowed to have a compact block of cases within a normal | |
switch/case sequence. Example: | |
switch(value) | |
{ | |
case ONE: number = "one"; break; | |
case TWO: number = "two"; break; | |
case THREE: number = "three"; break; | |
case FOUR: number = "four"; break; | |
case FIVE: number = "five"; break; | |
case FOURTYTWO: | |
{ | |
...code... | |
} | |
break; | |
} | |
############################################################################ | |
11. Variables | |
------------- | |
Variables should generally have as meaningful names as possible. Avoid | |
custom abbreviations but do use common ones (like scr, win, obj etc.). | |
Do not use negating words like 'no' in a boolean value but try instead | |
to use something like 'is', as in "isUppercase". This applies both to | |
boolean values and functions which return a boolean. | |
Local variables and function arguments are either completely lowercase or | |
camelon notation, where the first letter is lowercase and the first | |
letter of the following words is uppercase, as shown in the previous | |
example. | |
Defines (both constants and macros) are completely uppercase. However, if | |
the defines are such as like "#define isUppercase" then a mixed case | |
writing is allowed. | |
Multiple variable definitions on a single line should be avoided and better | |
be expanded to multiple lines for better readability. | |
Example: | |
struct CustomEntry | |
{ | |
char *Name; | |
char *Address; | |
}; | |
rather than | |
struct CustomEntry | |
{ | |
char *Name, *Address; | |
}; | |
Enumerations, global variables and structure names and members are proper | |
case as defined under "Functions". Generally static variables should also | |
comply with this rule (when they are really meant as global, but local not | |
to clutter the global namespace). | |
As we want to introduce multithreading to YAM sooner or later please avoid | |
using static Variables. Instead of that use our "struct Global" which can | |
be easily locked by sempahores. | |
Abbreviations which normally appear in uppercase (e.g. HTML) should still | |
be uppercase despite the above rules. | |
Some examples: | |
#define ABS(x) ((x) < 0 ? -(x) : (x)) | |
#define PI 3.14159265358979323846 | |
Object *GlobalConfig = DataspaceObject, ..., End; | |
struct CustomEntry *CreateEntry(char *name, char *address) | |
{ | |
struct CustomEntry *res; | |
if((res = malloc(sizeof(struct CustomEntry))) | |
{ | |
res->Name = name; | |
res->Address = address; | |
} | |
return res; | |
} | |
############################################################################ | |
12. Use "const" on variables whereever possible | |
----------------------------------------------- | |
By using "const" on variable definitions you give the compiler an additional | |
hint on the usage of these variables. For modern compilers, this allows to | |
tune the optimization and not only make faster code but also provides | |
a more secure environment. While this will allow the compiler to warn you | |
(as the developer) on cases where you might have accidently accessed the | |
variable in write mode, it will also allow the compiler to move the data | |
section of that variable into the read-only part of the executable and thus | |
make it secure for external modifications. | |
Therefore, whereever possible you should use "const" on variables from which | |
you know will never change their content. | |
So, instead of doing something like: | |
BOOL IsRed(enum Color color) | |
{ | |
if(color == CL_Red) | |
return TRUE; | |
} | |
You should define the function like: | |
BOOL IsRed(const enum Color color) | |
{ | |
if(color == CL_Red) | |
return TRUE; | |
} | |
This will allow the compiler to warn you in case you are trying to | |
do some local modifications to the "color" variable. | |
############################################################################ | |
13. Custom Class naming | |
----------------------- | |
Since we manage our own MUI custom classes in the 'src/classes' directory | |
of YAM, we try to make things clear as much as possible. One thing is the | |
naming of the YAM internal custom classes. | |
We have the convention that internal MUI classes of YAM should be put into | |
the 'classes' subdirectory and together with the file naming, the internal | |
class should use the same 'camelon notation' like we use for variables. | |
Example: "TextEditor" instead of "Texteditor". | |
In addition, we believe the class/file-name itself should clearly show from | |
which superclass a particular class was inheriented. For example, if you | |
generate a new class which is inheriented from MUIC_Group, we highly suggest | |
to use a name ending with "Group". | |
Example: "ReadMailGroup" instead of "ReadMailClass" | |
############################################################################ | |
14. Types | |
--------- | |
We mostly use standard C-types wherever possible. This is 'char *' for | |
STRPTR, 'char' for TEXT, 'void' for VOID and 'void *' for APTR. However, | |
for certain Amiga specific tasks we stick to the special Amiga types. For | |
Example, for defining MUI objects we use "Object *" rather than 'void *', | |
but also rather than APTR. | |
Further, although YAM is currently ANSI-C, one should always use proper | |
types, so that we may later compile the project with a C++ compiler. | |
This means that if you e.g. have an IntuiMessage pointer that must be | |
supplied to 'ReplyMsg()', you should use the format | |
'ReplyMsg(&imsg->ExecMessage)'. | |
For strings embedded in structures, pass a pointer to first character, | |
as this also serves as extra information for the person who reads the | |
source. For example, | |
struct Person | |
{ | |
char Address[SIZE_ADDRESS]; | |
char RealName[SIZE_REALNAME]; | |
}; | |
... | |
struct Person *pe = SomePerson(); | |
printf("Letter came from %s\n", &pe->Address[0]); | |
as opposed to: | |
printf("Letter came from %s\n", pe); | |
############################################################################ | |
15. Taglists | |
------------ | |
When providing taglists, one can either put all tags on the same line | |
(if only a few tags are supplied), or put one tag on each line, which | |
is then indented with as many spaces until the opening pharatesis of the | |
taglist function. When putting one tag on its own line, the first line that | |
contain the 'receiver' should NOT contain a tag. | |
The comma should come immediately after the tag ID, and at least one | |
space should follow. More spaces are allowed for padding (but no tabs - | |
see rule about Tabs, which btw also contains a taglist example). For | |
example: | |
SetAttrs(obj, | |
MUIA_Window_LeftEdge, left, | |
MUIA_Window_TopEdge, top, | |
MUIA_Window_Width, _width(msg->other), | |
TAG_DONE); | |
^^^^^^^^^^^ - 11 spaces - NO tabs | |
############################################################################ | |
16. ANSI-C vs. AmigaOS | |
---------------------- | |
As a general rule: Use the ANSI-C functions like memcpy, malloc etc. | |
The exception is when the Amiga versions handle locale specific things | |
which the ANSI-C functions lack. As of this writing, only ToUpper, | |
ToLower, Stricmp and Strnicmp are cases where the Amiga versions should | |
be used. The same applies to variable types as mentioned in section 9 of | |
this styleguide. | |
############################################################################ | |
17. Comments | |
------------ | |
A general rule for commenting is: Comment as much as possible while you | |
are developing your algorithms! | |
For comment style, we highly prefer using the C++ single line commenting | |
variant. Even for multiples lines we prefer to use the C++ style (//) | |
comment characters as we than can more easily comment out things temporarly | |
with the "/* ... */" comments. So please use the "//" C++ style comments | |
wherever possible. | |
############################################################################ | |
18. Debugging statements | |
------------------------ | |
As we recently implemented a very flexible runtime debugging functions in | |
YAM, all developers are adviced to make use of the provided debugging macros | |
as much as possible! Especially for the function entry and exit positions | |
the special macros 'ENTER()', 'LEAVE()' and 'RETURN()' should be considered | |
mandatory for every new function introduced in YAM. | |
Example: | |
BOOL Function(char *text) | |
{ | |
BOOL result = FALSE; | |
ENTER(); | |
... code ... | |
RETURN(result); | |
return result; | |
} | |
Another mandatory/important debugging macro is the so-called 'ASSERT()'. | |
It allows to check for a certain condition and if that conditition isn't true | |
anymore, it will immediately output an error string and exit the application. | |
It should be used whenever possible and where ultimative conditions should | |
be checked. | |
Example: | |
BOOL Function(char *text) | |
{ | |
BOOL result = FALSE; | |
ENTER(); | |
ASSERT(text != NULL); | |
text[10] = 'h'; // if text == NULL a crash would occur! | |
... code ... | |
RETURN(result); | |
return result; | |
} | |
In addition, the macros like 'SHOWVALUE()' and 'SHOWSTRING()' also can come | |
handy during debugging a certain situation. So please check the "debug.h" file | |
in the 'src' directory of YAM. It contains all currently supported debugging | |
macros including the printf-like macros "D()", "W()" and "E()" for outputting | |
variable texts for debugging, warning or even error conditions. | |
Please note that in case YAM will be compiled without the "DEBUG" define, all | |
code normally emitted by these macros will be omitted. They are really plainly | |
meant to be usefull during debugging. | |
############################################################################ | |
19. Obsolete string functions | |
----------------------------- | |
A simple rules is: In any event, try to avoid to use one of the following | |
functions as they are known to cause buffer overflows or other known issues. | |
Use their replacement functions listed here instead: | |
OBSOLETE REPLACEMENT | |
-------- ----------- | |
strcpy strlcpy | |
strncpy strlcpy | |
strcat strlcat | |
strncat strlcat | |
stccpy strlcpy | |
sprintf snprintf | |
vsprintf vsnprintf | |
strtok strpbrk | |
So please try to avoid the use of one of the above listed functions as their | |
use should be really considered obsolete within YAM. And if we try to stick | |
to their much safer variants listed here, we really hope to be a bit more | |
on the safe side and highly reduce the probablity of unwanted crashes due to | |
buffer overflows. | |
############################################################################ | |
20. Avoid the use of MUIA_ShowMe | |
-------------------------------- | |
It is known, that the use of MUIA_ShowMe may cause trouble with older MUI | |
versions (<= 3.8) but also may screw up the window layout with newer MUI | |
versions, under certain conditions. See bug #1716466 at http://bugs.yam.ch/ | |
for more info on a common MUIA_ShowMe issue. | |
Whereas we don't want to generally ban the use of MUIA_ShowMe | |
in the sources of YAM, we highly suggest to try to avoid it. Whereever | |
easily possible, a MUIA_ShowMe use should be replaced by the corresponding | |
MUIM_Group_InitChange/ExitChange combo so that an object will be dynamically | |
removed (hided) and added (shown) by using there Group method calls instead | |
############################################################################ | |
21. Do NOT use GetAttr() or SetAttrs() | |
--------------------------------------- | |
A common mistake when programming BOOPSI classes is to either forget to | |
properly terminate a SetAttrs() call or that a GetAttr() call might end up | |
in an uninitialized variables. Both of these problems should, however, be | |
solved by using the more convienent and secure xget() and xset() macros. | |
These macros will make sure that the GetAttr() and SetAttrs() calls are | |
properly terminated and that a GetAttr() call will always return 0 in | |
case an attribute isn't available. | |
So to be on the safe side, the direct use of GetAttrs() or SetAttrs() | |
should be considered obsolete and all calls should be replaced by their | |
equivalent xget/xset calls. | |
############################################################################ | |
22. Prefer AllocSysObject(), AllocDosObject() like functions | |
------------------------------------------------------------ | |
As with newer operating system versions certain new functionality regarding | |
resource tracking has been added (especially AmigaOS4), the use of the | |
AllocSysObject() and AllocDosObject() functions should be considered the | |
preferred way to allocate system resources. | |
That means, e.g. instead of doing things like CreateIORequest() you are | |
requested to use AllocSysObject() to create an IORequest. The same applies | |
for all kind of system structures where these structures are normally | |
allocated via AllocVec(). For such structures you are adviced to use the | |
corresponding AllocXXXXObject() functions if they exist and do the same | |
job! | |
############################################################################ | |
23. Use forward declarations - keep the header files (*.h) clean! | |
----------------------------------------------------------------- | |
To keep the header files - and especially the way '#include' statements are | |
performed - clean, the use of forward declarations should considered | |
mandatory. To clear things up, the following rules should apply: | |
- If a variable in a header file (*.h) is to be defined as a pointer and | |
its type is a custom type (e.g. struct), then a forward declaration | |
of that type should be put at the top of the header file rather than | |
including the header where this type is defined. | |
- No forward declarations for 'enum' types should be defined. While some | |
compilers might support it, it is not part of any standard. | |
- put your forward declarations at the top of each header file, right | |
after the last #include statement. They should always look like: | |
--- cut here --- | |
#include <intuition/classusr.h> // Object | |
// forward declarations | |
struct Mail; | |
--- cut here --- | |
whereas "struct Mail" is the forward declaration. | |
The main reason why we prefer the use of forward declarations rather than | |
including every necessary header file is, because it keeps the header files | |
clean and reduces the possibility of circular include statements. Even more | |
important, it keeps the dependency list (which is generated via 'make | |
depend') clean and speed up compilation as well. | |
############################################################################ | |
24. Comment your structure elements | |
----------------------------------- | |
When implementing a new structure via "struct XXXX" it always helps if the | |
individual structure elements are commented at the point where the structure | |
will be defined. This should also be done if the element name itself might | |
be self-explanatory. In practice, this means instead of doing something | |
like: | |
struct Person | |
{ | |
char firstname[10]; | |
char lastname[10]; | |
time_t birthday; | |
}; | |
you SHOULD do something like: | |
struct Person | |
{ | |
char firstname[10]; // the first name of the person | |
char lastname[10]; // the last name (surname) of the person | |
time_t birthday; // the birthday in days from 1.1.1970 | |
}; | |
By adding line comments (//) to each structure element you help developers | |
to understand what the individual elements stands for. In addition, you can | |
give additional information about implications or warnings. | |
############################################################################ |