Log in | Back to darenet.org

Development Team/Coding Standards

m (Separation)
 
(11 intermediate revisions not shown)
Line 1: Line 1:
-
{{Header|1 = <h2>'''[[Development Team|DareNET Development Wiki]]''' - Coding Standards</h2>}}
+
This document gives coding conventions we use for the C code comprising many of DareNET's development projects.
-
The following is a guide to explain the programming style used for ircd-darenet and services-darenet, and to advise future developers, creators or patches, or other maintainers in regards to desirable programming practices. Since these programs are based on the works of others (outside of the DareNET development team), it's reasonable to assume not all code will follow them. This, of course, will be rectified with time.
+
-
== Comments ==
+
Rules are made to be broken. Two good reasons to break a particular rule:
-
----
+
-
Comments can add immensely to the readability of a program, but used heavily or poorly placed they can render good code completely incomprehensible.
+
-
That being said, good places to put comments are:
+
# When applying the rule would make the code less readable, even for someone who is used to reading the code that follows the rules.
 +
# To be consistent with surrounding code that also breaks it (maybe for historic reasons); although, this is also a good opportunity to clean up someone else's mess.
-
* a broad overview at the beginning of a module
+
== C dialect ==
-
* data structure definitions
+
-
* global variable definition
+
-
* at the beginning of a function
+
-
* tricky steps within a function
+
-
You should also strive to doxify your comments, where suitable. Rambles, rants, and personal complaints do not provide any benefit to understanding code, please do not add them.
+
* While you may use C99, keep in mind that variable length arrays and macros with variable number of parameters are not well supported among most compilers. If you use these features, ensure you account for this.
-
=== Multi Line ===
+
* GCC is the compiler we use for most, if not all, of our testing. And while use of GCC extensions is not prohibited, we don't necessarily encourage their use either.
-
Multi line comments should follow the C-style comment, for example:
+
* Never use C++ style (//) single line comments.
-
<c>
+
-
/**
+
-
* This is a multi line comment.
+
-
* - SecretAgent
+
-
*/</c>
+
-
=== Single Line ===
+
== Indentation ==
-
Single line comments should also be in the C style, for example:
+
Tabs, tabs, ONLY tabs.
-
<source lang="c" line start=1 >
+
-
/**< This is a one-line comment. - SecretAgent */</source>
+
-
Please avoid using C++-style single line comments (e.g. <code>// Something here</code>).
+
Use one tab for each level of indentation. Spaces should ONLY be used for alignment, not indentation.
-
Additionally, the opening / of all comments should be indented to the same level as the code to which it applies, for example:
+
Preference: Tabs are 8 characters, and thus indentations are also 8 characters. There are heretic movements that try to make indentations 4 (or even 2) characters deep, and that is akin to trying to define the value of PI to be 3.
-
<c>if (fubar()) {
+
Rationale: The whole idea behind indentation is to clearly define where a block of control starts and ends. Especially when you've been looking at your screen for 20 straight hours, you'll find it a lot easier to see how the indentation works if you have large indentations.
-
/*
+
-
* Fouled up beyond all recognition. Print a nastygram
+
-
* and attempt to clean up. If that doesn't work,
+
-
* die horribly, and try to crash the system while
+
-
* we're at it.
+
-
*/
+
-
...
+
-
}</c>
+
-
== Character Sets ==
+
Now, some people will claim that having 8-character indentations makes the code move too far to the right, and makes it hard to read on a 80-character terminal screen. The answer to that is that if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. Also, the entire point of the hard tab character \t is that the actual horizontal space visible from printing it is not set in stone. You can set it to whatever you like.
-
The ASCII character set (plain text, 7-bit characters) should be used in all source code comments, text documents, and other contexts (note, this doesn't apply to services-darenet help files). Also, it is okay to use non-ASCII characters to represent proper names of contributors in change logs and credits; however, these are the only exceptions. In general, you can't mix encodings reliably.
+
In short, 8-char indents make things easier to read, and have the added benefit of warning you when you're nesting your functions too deep. Heed that warning.
-
== Source/Header Files Organization ==
+
== Braces ==
-
----
+
-
Use the following organization for source files:
+
-
* includes of system headers
+
Another issue that always arises with C styling is the placement of braces. Unlike the indent size, there are few technical reasons to choose one placement strategy over the other, but the preferred way, as shown to us by the prophets Kernighan and Ritchie, is to put the opening brace last on the line, and put the closing brace first, so:
-
* includes of local headers
+
-
* global variables
+
-
* functions
+
-
Within each section, order your functions in a 'bottom up' manner - defining functions before their use. The benefit of avoiding redundant (hence error prone) forward declarations outweighs the minor irritation of having to jump to the bottom of the file to find the main functions.
+
<c>if (x is true) {
 +
        we do y
 +
}</c>
-
In header files, use the following organization:
+
However, there is one special case, namely functions: they have the opening brace at the beginning of the next line, thus:
-
* type and constant definitions
+
<c>int function(int x)
-
* external object declarations
+
{
-
* external function declarations
+
        body of function
 +
}</c>
-
'''Avoid having nested includes, ever.''' If you've ever tried to track a bug through the SunOS /usr/include maze, you'll understand why. Consider using the makedepend tools to help maintain your source file dependencies in your Makefile.
+
Heretic people all over the world have claimed that this inconsistency is ... well ... inconsistent, but all right-thinking people know that (a) K&R are right and (b) K&R are right. Besides, functions are special anyway (you can't nest them in C).
-
== File Naming Conventions ==
+
Note that the closing brace is empty on a line of its own, except in the cases where it is followed by a continuation of the same statement, i.e. a "while" in a do-statement or an "else" in an if-statement, like this:
-
----
+
-
File names are made up of a base name, peiod and suffix. The first character of the name should be a letter and all characters (except the period) should be lower-case letters and numbers. Please try to keep the base name short. These rules apply to both program files and default files used and produced by the program. Browse the current repos for an idea of how things are currently done. Ask if you're unsure!
+
-
=== Suffix Conventions ===
+
<c>do {
 +
        body of do-loop
 +
} while (condition);</c>
-
* C source files should end in ''.c''
+
and
-
* Python source files should end in ''.py''
+
-
The following conventions are universally followed:
+
<c>if (x == y) {
 +
        ..
 +
} else if (x > y) {
 +
        ...
 +
} else {
 +
        ....
 +
}</c>
-
* Relocatable object files end in ''.o''
+
Rationale: K&R.
-
* Include header files end in ''.h''
+
-
* Yacc source files end in ''.y''
+
-
* Lex source files end in ''.l''
+
-
In addition, it is conventional to use "Makefile" (not "makefile") for the control file for make (for systems that support it) and "README" for a summary of the contents of the directory or directory tree.
+
Also, note that this brace-placement also minimizes the number of empty (or almost empty) lines, without any loss of readability. Thus, as the supply of new-lines on your screen is not a renewable resource (think 25-line terminal screens here), you have more empty lines to put comments on.
-
== Indentation ==
+
== Naming ==
-
----
+
-
Tabs. Tabs. ONLY TABS.
+
-
Use a single tab for each level of indentation, for example:
+
C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable <code>tmp</code>, which is much easier to write, and not the least more difficult to understand.
-
<c>
+
-
int main()
+
-
{
+
-
  if (condition) {
+
-
    code
+
-
  }
+
-
}</c>
+
-
== Separation ==
+
However, while mixed-case names are frowned upon, descriptive names for global variables are a must. To call a global function <code>foo</code> is a shooting offense.
-
----
+
-
Always put a space in between a keyword like if/while and the condition, for example:
+
-
<c>if (foo == bar)</c>
+
Global variables (to be used only if you really need them) need to have descriptive names, as do global functions. If you have a function that counts the number of active users, you should call that <code>count_active_users()</code> or similar, you should not call it <code>cntusr()</code>.
-
NOT
+
Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged - the compiler knows the types anyway and can check those, and it only confuses the programmer.
-
<c>if(foo == bar)</c>
+
Local variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called <code>i</code> or <code>ii</code> ''(easier to search for)''. Calling it <code>loop_counter</code> is counter-productive, if there is no chance of it being mis-understood. Similarly, <code>tmp</code> can be just about any type of variable that is used to hold a temporary value.
-
== Braces ==
+
If you are afraid to mix up your local variable names, you have another problem, which is called the function-growth-hormone-imbalance syndrome.
-
----
+
-
We follow the [http://en.wikipedia.org/wiki/Indent_style#K.26R_style K&R] style of indentation. That means, opening braces should go on the same line as the control statement, while closing braces should go on a line of its own at the same indentation level as the control statement. However, functions should be braced distinctly from statements; an opening function brace is placed on the line following the declaration, at the same indentation level as the declaration.
+
-
For example, place braces like this:
+
== Functions ==
-
<source lang="c" line start=1 >
+
-
if (!(bp = malloc(sizeof (Buffer)))) {
+
-
  perror("malloc");
+
-
  abort();
+
-
}
+
-
</source>
+
-
and NOT:
+
Functions should be short and sweet, and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80×24, as we all know), and do one thing and do that well.
-
<source lang="c" line start=1 >
+
-
if (!(bp = malloc(sizeof (Buffer))))
+
-
{
+
-
  perror("malloc");
+
-
  abort();
+
-
}</source>
+
-
== Variable naming ==
+
The maximum length of a function is inversely proportional to the complexity and indentation level of that function. So, if you have a conceptually simple function that is just one long (but simple) case-statement, where you have to do lots of small things for a lot of different cases, it's ok to have a longer function.
-
----
+
-
Length is not a virtue in a name; clarity of expression is. A global variable rarely used may deserve a long name, ''maxphysaddr'' say. An array index used on every line of a loop needn't be named any more elaborately than i. Saying index or elementnumber is more to type (or calls upon your text editor) and obscures the details of the computation. When the variable names are huge, it's harder to see what's going on.
+
-
Struct names should be in camel case with a leading capital letter, for example, "MyBagOfBones". Variable names can be either in camel case with a leading capital letter or alternatively all lower case, so long as the same naming convention is adhered to throughout. Constants and enum values should always be completely in CAPITALS and underscores may be used.
+
However, if you have a complex function, and you suspect that a less-than-gifted first year high-school student might not even understand what the function is all about, you should adhere to the maximum limits all the more closely. Use helper functions with descriptive names (you can ask the compiler to in-line them if you think it's performance critical, and it will probably do a better job of it that you would have done).
-
== Use of char pointers ==
+
Another measure of the function is the number of local variables. They shouldn't exceed 5-10, or you're doing something wrong. Re-think the function, and split it into smaller pieces. A human brain can generally easily keep track of about 7 different things, anything more and it gets confused. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now.
-
----
+
-
Whenever you use char pointers (char*, char**) try to use const equivalents. This is much safer and avoids ugly and dangerous casts.
+
-
== Macros ==
+
== Comments ==
-
----
+
-
Macros should only be used to simplify a piece of code that has complicated or repeated expressions. We also advise that you #undef a macro when you're done with it. Over using them can, and will, slow a program down, not to mention make the compiled code become rather large. If you are writing a piece of code that is going to be available from multiple parts of the program, it should be a function, not a macro.
+
-
== Functions ==
+
C-style comments only.
-
----
+
-
Functions should be short and sweet. If a function won't fit on a single screen, it's probably too long. Don't be afraid to break functions down into smaller helper functions. If they are static to the module an optimizing compiler can inline them again, if necessary. Helper functions can also be reused by other functions.
+
-
However, we do realize that it is sometimes hard to break things down. Since functions don't nest, variables have to be communicated through function arguments or global variables. Don't create huge interfaces to enable a decomposition that is just not meant to be.
+
Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it's much better to write the code so that the working is obvious, and it's a waste of time to explain badly written code.
-
== Linefeeds ==
+
Generally, you want your comments to tell WHAT your code does, not HOW. Also, try to avoid putting comments inside a function body: if the function is so complex that you need to separately comment parts of it, you should probably go back to section 4 for a while. You can make small comments to note or warn about something particularly clever (or ugly), but try to avoid excess. Instead, put the comments at the head of the function, telling people what it does, and possibly WHY it does it.
-
----
+
-
Unix linefeeds only please. We do not like to see our screens covered in ^M.
+
-
Additionally, the maximum line width is 80 characters. If you are writing a longer line, try to break it at a logical point and continue on the next line with the same indenting. Use of backslash is okay; however, multi-line literals might cause less confusion if they are defined before the function start.
+
== Character Sets ==
-
== Portability ==
+
In general, encodings cannot be mixed reliably; therefore, the ASCII character set (plain text, 7-bit) should be used in all source code comments and text documents. It is okay to use non-ASCII characters in help files, and to represent proper names of contributors in change log entries.
-
----
+
-
Always make sure your code is portable to all supported operating systems. Don't write code that only works on Linux. Test your code on all platforms or ask for help from other developers who have the platforms you want to test on.
+
-
We currently test all major releases on Gentoo and FreeBSD. If you have another platform in mind, let us know. We may just be willing to add it to our list.
+
== Integers ==
 +
 
 +
Use unsigned ints instead of ints if you know the value will never be negative. Some processors can handle unsigned integer arithmetic considerably faster than signed. This is also good practice, and helps make for self-documenting code.
 +
 
 +
== Macros ==
-
== External Dependencies ==
+
Avoid them, if possible.  
-
----
+
-
You must not use any dependencies that are not available as standard on all supported operating systems beyond libc, and whatever else is currently needed to build the ircd/services. If you absolutely must do so, you will need approval from the Development Manager first.
+
-
== Profiling and Performance ==
+
We prefer that (inline) functions be used instead, as they provide better type checking and generally make code easier to read and debug.
-
----
+
-
It is one thing to assume that code performs bad, it is another thing to prove that it actually is. A lot of experienced programmers talk about 'premature optimization', and here is what it means: if you have a piece of code called once on start up that takes 10 seconds instead of one second to run, and a piece of code that takes 0.05 seconds to run when it should take 0.01, and it is called once per second, the second piece of code is the priority.
+
-
In other words, make sure that what you think is slow, and a performance problem in ircd-*/services-* actually is.
+
However, macros are fine for small, simple, often-repeated, run-to-completion code segments. For example, if there is a small external function being called thousands of times in a tight loop, replacing it with a macro to perform the same job will remove the overhead of all those function calls. That said, they shouldn't be longer than six or so lines and they should never contain a return statement, or otherwise directly affect control flow of the calling function.
-
more to come, possibly...
+
Also, remember that the text of a macro argument is "pasted in"; therefore, you should surround its arguments in parentheses to ensure they're evaluated before the rest of the macro body. This should also be done for the body itself to prevent the surrounding context from affecting the macro body.

Current revision as of 22:37, 11 September 2011

This document gives coding conventions we use for the C code comprising many of DareNET's development projects.

Rules are made to be broken. Two good reasons to break a particular rule:

  1. When applying the rule would make the code less readable, even for someone who is used to reading the code that follows the rules.
  2. To be consistent with surrounding code that also breaks it (maybe for historic reasons); although, this is also a good opportunity to clean up someone else's mess.

In This Guide:

C dialect

  • While you may use C99, keep in mind that variable length arrays and macros with variable number of parameters are not well supported among most compilers. If you use these features, ensure you account for this.
  • GCC is the compiler we use for most, if not all, of our testing. And while use of GCC extensions is not prohibited, we don't necessarily encourage their use either.
  • Never use C++ style (//) single line comments.

Indentation

Tabs, tabs, ONLY tabs.

Use one tab for each level of indentation. Spaces should ONLY be used for alignment, not indentation.

Preference: Tabs are 8 characters, and thus indentations are also 8 characters. There are heretic movements that try to make indentations 4 (or even 2) characters deep, and that is akin to trying to define the value of PI to be 3.

Rationale: The whole idea behind indentation is to clearly define where a block of control starts and ends. Especially when you've been looking at your screen for 20 straight hours, you'll find it a lot easier to see how the indentation works if you have large indentations.

Now, some people will claim that having 8-character indentations makes the code move too far to the right, and makes it hard to read on a 80-character terminal screen. The answer to that is that if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. Also, the entire point of the hard tab character \t is that the actual horizontal space visible from printing it is not set in stone. You can set it to whatever you like.

In short, 8-char indents make things easier to read, and have the added benefit of warning you when you're nesting your functions too deep. Heed that warning.

Braces

Another issue that always arises with C styling is the placement of braces. Unlike the indent size, there are few technical reasons to choose one placement strategy over the other, but the preferred way, as shown to us by the prophets Kernighan and Ritchie, is to put the opening brace last on the line, and put the closing brace first, so:

if (x is true) {
        we do y
}

However, there is one special case, namely functions: they have the opening brace at the beginning of the next line, thus:

int function(int x)
{
        body of function
}

Heretic people all over the world have claimed that this inconsistency is ... well ... inconsistent, but all right-thinking people know that (a) K&R are right and (b) K&R are right. Besides, functions are special anyway (you can't nest them in C).

Note that the closing brace is empty on a line of its own, except in the cases where it is followed by a continuation of the same statement, i.e. a "while" in a do-statement or an "else" in an if-statement, like this:

do {
        body of do-loop
} while (condition);

and

if (x == y) {
        ..
} else if (x > y) {
        ...
} else {
        ....
}

Rationale: K&R.

Also, note that this brace-placement also minimizes the number of empty (or almost empty) lines, without any loss of readability. Thus, as the supply of new-lines on your screen is not a renewable resource (think 25-line terminal screens here), you have more empty lines to put comments on.

Naming

C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable tmp, which is much easier to write, and not the least more difficult to understand.

However, while mixed-case names are frowned upon, descriptive names for global variables are a must. To call a global function foo is a shooting offense.

Global variables (to be used only if you really need them) need to have descriptive names, as do global functions. If you have a function that counts the number of active users, you should call that count_active_users() or similar, you should not call it cntusr().

Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged - the compiler knows the types anyway and can check those, and it only confuses the programmer.

Local variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called i or ii (easier to search for). Calling it loop_counter is counter-productive, if there is no chance of it being mis-understood. Similarly, tmp can be just about any type of variable that is used to hold a temporary value.

If you are afraid to mix up your local variable names, you have another problem, which is called the function-growth-hormone-imbalance syndrome.

Functions

Functions should be short and sweet, and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80×24, as we all know), and do one thing and do that well.

The maximum length of a function is inversely proportional to the complexity and indentation level of that function. So, if you have a conceptually simple function that is just one long (but simple) case-statement, where you have to do lots of small things for a lot of different cases, it's ok to have a longer function.

However, if you have a complex function, and you suspect that a less-than-gifted first year high-school student might not even understand what the function is all about, you should adhere to the maximum limits all the more closely. Use helper functions with descriptive names (you can ask the compiler to in-line them if you think it's performance critical, and it will probably do a better job of it that you would have done).

Another measure of the function is the number of local variables. They shouldn't exceed 5-10, or you're doing something wrong. Re-think the function, and split it into smaller pieces. A human brain can generally easily keep track of about 7 different things, anything more and it gets confused. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now.

Comments

C-style comments only.

Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it's much better to write the code so that the working is obvious, and it's a waste of time to explain badly written code.

Generally, you want your comments to tell WHAT your code does, not HOW. Also, try to avoid putting comments inside a function body: if the function is so complex that you need to separately comment parts of it, you should probably go back to section 4 for a while. You can make small comments to note or warn about something particularly clever (or ugly), but try to avoid excess. Instead, put the comments at the head of the function, telling people what it does, and possibly WHY it does it.

Character Sets

In general, encodings cannot be mixed reliably; therefore, the ASCII character set (plain text, 7-bit) should be used in all source code comments and text documents. It is okay to use non-ASCII characters in help files, and to represent proper names of contributors in change log entries.

Integers

Use unsigned ints instead of ints if you know the value will never be negative. Some processors can handle unsigned integer arithmetic considerably faster than signed. This is also good practice, and helps make for self-documenting code.

Macros

Avoid them, if possible.

We prefer that (inline) functions be used instead, as they provide better type checking and generally make code easier to read and debug.

However, macros are fine for small, simple, often-repeated, run-to-completion code segments. For example, if there is a small external function being called thousands of times in a tight loop, replacing it with a macro to perform the same job will remove the overhead of all those function calls. That said, they shouldn't be longer than six or so lines and they should never contain a return statement, or otherwise directly affect control flow of the calling function.

Also, remember that the text of a macro argument is "pasted in"; therefore, you should surround its arguments in parentheses to ensure they're evaluated before the rest of the macro body. This should also be done for the body itself to prevent the surrounding context from affecting the macro body.