Coding Conventions
These coding conventions are guidelines for developers creating MoSync code. These conventions are largely inspired by and derived from existing ones in well-known programming environments such as Java and SDL. As such, they shouldn't come across as exotic to any moderately experienced programmer.
Header Files
The #define Guard
All header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be <PROJECT>_<PATH>_<FILE>_H_.
Header File Dependencies
Don't use an #include when a forward declaration would suffice.
Inline Functions
Define functions inline only when they are small, say, 10 lines or less.
Function Parameter Ordering
When defining a function, parameter order is: inputs, then outputs.
Names and Order of Includes
Use standard order for readability and to avoid hidden dependencies: C library, C++ library, other libraries' .h, your project's .h.
Scoping
Nonmember, Static Member, and Global Functions
Prefer nonmember functions within a namespace or static member functions to global functions; use completely global functions rarely.
Local Variables
Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
Static and Global Variables
Static or global variables of class type are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction.
Classes
Doing Work in Constructors
In general, constructors should merely set member variables to their initial values. Any complex initialization should go in an explicit Init() method.
Default Constructors
You must define a default constructor if your class defines member variables and has no other constructors. Otherwise the compiler will do it for you, badly.
Structs vs. Classes
Use a struct only for passive objects that carry data; everything else is a class.
Inheritance
Composition is often more appropriate than inheritance. When using inheritance, make it public.
Multiple Inheritance
Only very rarely is multiple implementation inheritance actually useful. We allow multiple inheritance only when at most one of the base classes has an implementation; all other base classes must be pure interface classes tagged with the Interface suffix.
Interfaces
Interfaces should be named using an I- prefix. Example: IMySexyInterface
Access Control
Make data members private, and provide access to them through accessor functions as needed. Typically a variable would be called mFoo and the accessor function getFoo(). You may also want a mutator function setFoo().
Declaration Order
Use the specified order of declarations within a class: public: before private:, methods before data members (variables), etc.
Write Short Functions
Prefer small and focused functions.
Other C++ Features
Reference Arguments
All parameters passed by reference must be labeled const.
Use of const
We strongly recommend that you use const whenever it makes sense to do so.
64-bit Portability
Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment.
Preprocessor Macros
Be very cautious with macros. Prefer inline functions, enums, and const variables to macros.
Naming
General Naming Rules
Function names, variable names, and filenames should be descriptive; eschew abbreviation. Types and variables should be nouns, while functions should be "command" verbs.
Type and Namespace Names
Type names start with a capital letter and have a capital letter for each new word, with no underscores: MyExcitingClass, MyExcitingEnum.
Variable Names
They start with a capital letter and have a capital letter for each new word, with no underscores. A m- prefix is used for member variables. A s- prefix is used for static variables. For instance: myExcitingLocalVariable, mMyExcitingMemberVariable, sMyExcitingMemberVariable
Constant Names
Use capitals and underscore as a separator: DAYS_IN_A_WEEK.
Function Names
Regular functions have mixed case; accessors and mutators match the name of the variable: myExcitingFunction(), myExcitingMethod(), getMyExcitingMemberVariable(), setMyExcitingMemberVariable().
Enumerator Names
Enumerators should be named either like constants or like macros: ENUM_NAME.
Macro Names
You're not really going to define a macro, are you? If you do, they're like this: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.
Comments
File Comments
Start each file with a copyright notice, followed by a description of the contents of the file.
Class Comments
Every class definition should have an /** accompanying comment */ that describes what it is for and how it should be used.
Function Comments
Every function should have a /** declaration comment */ which describes use of the function, using @param and @return syntax.
Variable Comments
In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required.
Implementation Comments
In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code.
Punctuation, Spelling and Grammar
Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.
Formatting
Line Length
Each line of text in your code should be at most 80 characters long.
Preprocessor Directives
Preprocessor directives should not be indented but should instead start at the beginning of the line.
Class Format
Sections in public, protected and private order, each indented one space.
Indents and spaces
Inconsistent use of indents, tabs and spaces is a problem in much the same way as inconsistent naming is. Most text editors have the necessary options. For example, in Visual Studio, go Tools > Options > Text Editor > C/C++ > Tabs.
The following should apply to all MoSync code: one level of indention equals one tab. A tab is defined as the ASCII character \t. The actual screen size of the indents can thus be set by and for each user with no ill effects for others.
Syscalls
Syscalls are named using a ma- prefix. Only actual Syscalls and IOCTLs are named this way. Examples:
void maDisposeLayer(Handle layer);
void maSetMap(Handle layer, Handle srcMapResource, int destX, int destY);
Library functions
Library functions that are directly associated with the manipulation of some complex datatype (such as a struct) should be prefixed with the name of the type followed by an underscore, followed by the function name in camel case. Examples:
MAVec3_normalize(MAVec3* v); MARect_containsPoint(const MAVec3* v, const MAPoint2d* p);
Library functions that are not directly and strongly related to a datatype should be prefixed with an identifying name for the library, which should always contain an initial 'MA'. Examples:
MAMath_solveLinearSystem(); MAInet_sendMail();
Design Patterns
Syscalls and extensions
Syscalls and extensions constitute the lowest-level APIs available in MoSync. They are typically directly mapped to underlying platform features and are also subject to a number of restrictions:
The number of parameters is limited to four. If more information must be passed, the stack or struct pointers can be used.
Parameters cannot be function pointers. If a notification mechanism is required, one is required to make use of (custom) events.
The only allowed parameter types are primitive IDL types (see IDL documentation) and IDL structs that actually are declared in the relevant IDL file.
The only allowed return types are primitive IDL types.
MoSync memory cannot be allocated within syscalls, other than in the specific case of built-in or custom events.
Syscalls that manipulate objects/resources
Allocation, manipulation and deallocation of native objects (resources) is typically implemented by means of handles.
Typically, there is an maCreateX() function, which takes a placeholder handle to represent the allocated native object. Subsequent access to this object is provided through other syscalls that accept handles as their first parameter.
Finally, all objects are destroyed using maDestroyObject(). As an example, these are some functions that create and manipulate images:
void maCreateImageFromData( in Handle placeholder, in Handle data, in int offset, in int size ); void maCreateImageRaw( in Handle placeholder, in MAAddress src, in Extent size, in int alpha ); void maDrawImage( in Handle image, in int left, in int top ); void maDrawImageRegion( in Handle image, in MARect srcRect, in MAPoint2d dstPoint, in int transformMode );
Asynchronous operations
Since MoSync doesn't support user-created threads, it is important to provide asynchronous interfaces to time-consuming native functionality such as network communication. Since syscalls can't accept function pointers, the central MoSync event system is used to provide progress notification. A few built-in event types are provided in the EVENT struct, but there's also a void* provided that can be used for custom events.
- Printer-friendly version
- Login or register to post comments