Coding Style Guidelines
Note
On Internet Explorer 6, Windows XP, I've noticed that this document resizes to be slightly too wide when in the frame. It works fine on Internet Explorer 7 and Firefox on Windows XP. Any info on how it behaves in other browsers and on other operating systems would be great. Both the CSS and XHTML are fully W3C compliant, so if it doesn't work, it's your browser's fault, not mine.
Introduction
These guidelines cover how I'd like to see everything coded. I know you probably have your own favorite way of doing things, but I'd like to keep a consistent style, for a couple of reasons.
- It makes the project look more professional.
- It makes it easier for new developers to join the project.
Conventions used in all Auross documents
Fixed width text is used to represent code. It may be colored to indicate syntax.Bold text is used to represent files. These could be source files, data files, or even part of a file name (such as an extension)
Bold italic text is used to represent directories.
Italic text is used to represent programs and/or commands.
Boxed text is used to indicate that several items are connected, or to represent a large block of code.
Classes
In an attempt to keep the engine as object-oriented as possible, all effort should be made to keep everything in classes. Obviously, the main() function will be outside of a class, but it should be just about the only thing. Each class should have a source file (.cpp) and a header file (.h ). I don't like the use of .hpp for C++ headers. It looks ugly, and it messes with my brain.
Within each code directory, there should be two sub-directories: include and src. All header files should go in the include directory, and all source files should go in the src directory.
Prefixes
Each class gets a prefix based on where it is in the library heirarchy and/or what its purpose is. Additional prefixes can be added if necessary, but I'd really rather not do that if at all possible.
ac - Auross Core. Since most of the core library
classes fall under db or dt,
very few classes get this prefix. It still exists, though.
al - Auross Library. Most of the classes will end up
with this prefix.
ar - AuRoss. Things from the top-level engine get
this prefix.
db - Data Backbone. Any file- or network-handling
classes get this prefix.
dt - Data Type. Anything that needs to look like a
built-in type. This also includes container classes.
Header Files
Header files should contain the absolute minimum information for interaction with other classes to work. This means that the inclusion of other header files should be kept to a minimum. Any used classes should be declared, and the headers for those classes included in the source file.
For example, instead of:
...
...
#include "acObject.h"
#include "acCoord.h"
#include "dbFileHandler.h"
#include "dbDataManager.h"
namespace core {
...
class acWorldObject : public
acObject {
...
...
};
} // namespace core
...
You should do the following:
...
...
#include "acObject.h"
namespace core { class
dbFileHandler; }
namespace core { class
dbDataManager; }
namespace core { class
acCoord; }
namespace core {
...
class acWorldObject : public
acObject {
...
...
};
} // namespace core
...
Now, doesn't that look much nicer? What's especially great about this is that it gives a clear indication of which namespaces the different classes are in. It also helps speed up the preprocessor in some cases.
Header files should also be protected from being included more than once. It only takes three extra lines of code per file, and it's always worth it. Here's the preferred format:
#ifndef __ACWORLDOBJECT_H
#define __ACWORLDOBJECT_H
...
...
...
#endif
Easy, huh? That's two leading underscores, the class name in all uppercase, a single underscore, then the letter 'H'
Source Files
The other half of a class implementation is the source file. Source file styles are much simpler than header file styles. The following example should just about cover it:
#include "acWorldObject.h"
#include "dbFileHandler.h"
#include "dbDataManager.h"
#include "acCoord.h"
namespace core {
...
...
} // namespace core
That's it for the general format of source files.
Comments
Good commenting of the code is crucial to any open-source project, since others will be working on the code base, possibly without the original author to talk to. As such, I've set up a comment system that is both Doxygen-friendly, and easy to read when looking at the souce file. If you're not familiar with Doxygen, don't worry. I've got enough of it figured out that you just have to do what I do, and it should all come out fine.
Doxygen allows two types of descriptions: brief and detailed. Everything should have at least a brief description. Detailed descriptions should be added to explain intended use, implementation quirks, or when a one-line description just doesn't cover what needs to be covered. Very few things should go without detailed descriptions - the source code has some good examples of when detailed descriptions can be omitted.
Since Doxygen looks at both the source and header files, I've split the comments between them. Brief descriptions go in the header files, and detailed descriptions go in the source files. This doesn't always work, of course, since not everything in a header file also goes in a source file. If it doesn't get put in a source file, both the brief and detailed descriptions go in the header file.
...
namespace core {
/// Object with a coordinate system
/*!
Any object that has a specific location and/or orientation is based on this
class. Much like acObject, this type should never be directly created.
*/
class acWorldObject : public
acObject {
public:
/// Constructor
acWorldObject();
/// Destructor
virtual ~acWorldObject();
...
/// Sets the parent
void SetParent(acWorldObject * parent);
protected:
/// Flags
/*!
The only flag used by this class is 0x0001 - has_parent. Child classes can
define more flags, or create a new variable to hold their flags. It really
doesn't matter, as long as a child doesn't try to use the same bit as its
parent in the same variable.
*/
unsigned short _flags
...
/// The coordinate system for this object
acCoord * _coord;
};
} /// namespace core
...
The /// pattern tells Doxygen that the rest of the line is a brief description. The /*! and */ pairs indicate a detailed description. Functions shouldn't get detailed descriptions in the header file. Instead, add detailed descriptions to the source file. I'm not going to show an example of that, since it's pretty simple to figure out from the header file example above.
Use your judgment about detailed descriptions of member variables. Often, the brief description will be enough to fully explain what a variable is used for.
Member Visibility and Virtual Functions
Use your judgment on member visibility. The following are the guidelines I follow, but they aren't set in stone.
- If it's something that anyone or anything can/should/needs to access, make it public
- If it's something that child classes will need to see, make it protected
- If it's something that only the class and its friend classes will need to see, make it private
Virtual functions can be another tough one. They can make the code slightly slower, but they are a core part of object-oriented programming. Just about all of these rules will probably be proken at some point, so be aware that these are by far the most flexible of all rules.
- Destructors should be virtual. This helps prevent hard-to-find memory leaks.
- Functions that do any sort of advanced data-manipulation should be virtual.
- Functions that don't need a detailed description probably don't need to be virtual
- Functions that are designed to be overridden should, naturally, be virtual.
Other
I think the main stuff is explained at this point. There's lots of stuff to any coding style, but looking at the source code should answer most questions. If not, go ahead and eMail me. I'll be more than happy to answer any questions. All the remaining information is stylistic.
Naming
Functions and variables use different naming methods. It makes the code more readable.
- Functions use CapsOnFirstLetter format.
- Variables use lowercase_with_underscores format.
- private and protected members get a leading underscore.
That's it, really. Since most people these days have pretty good resolution screens, descriptive names are preferred to fitting things into a short line.
Indentation
Indentations should be two spaces. Tabs should never be used.
That's it. This document will grow and change as necessary. Any questions that get asked often will be added here. As will anything I see getting done wrong often. Happy Coding!