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.
All header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be <PROJECT>_<PATH>_<FILE>_H_.
Don't use an #include when a forward declaration would suffice.
Define functions inline only when they are small, say, 10 lines or less.
When defining a function, parameter order is: inputs, then outputs.
Use standard order for readability and to avoid hidden dependencies: C library, C++ library, other libraries' .h, your project's .h.
Prefer nonmember functions within a namespace or static member functions to global functions; use completely global functions rarely.
Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
Static or global variables of class type are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction.
In general, constructors should merely set member variables to their initial values. Any complex initialization should go in an explicit Init() method.
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.
Use a struct only for passive objects that carry data; everything else is a class.
Composition is often more appropriate than inheritance. When using inheritance, make it public.
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 should be named using an I- prefix. Example: IMySexyInterface
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().
Use the specified order of declarations within a class: public: before private:, methods before data members (variables), etc.
Prefer small and focused functions.
All parameters passed by reference must be labeled const.
We strongly recommend that you use const whenever it makes sense to do so.
Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment.
Be very cautious with macros. Prefer inline functions, enums, and const variables to macros.
Function names, variable names, and filenames should be descriptive; eschew abbreviation. Types and variables should be nouns, while functions should be "command" verbs.
Type names start with a capital letter and have a capital letter for each new word, with no underscores: MyExcitingClass, MyExcitingEnum.
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
Use capitals and underscore as a separator: DAYS_IN_A_WEEK.
Regular functions have mixed case; accessors and mutators match the name of the variable: myExcitingFunction(), myExcitingMethod(), getMyExcitingMemberVariable(), setMyExcitingMemberVariable().
Enumerators should be named either like constants or like macros: ENUM_NAME.
You're not really going to define a macro, are you? If you do, they're like this: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.
Start each file with a copyright notice, followed by a description of the contents of the file.
Every class definition should have an /** accompanying comment */ that describes what it is for and how it should be used.
Every function should have a /** declaration comment */ which describes use of the function, using @param and @return syntax.
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.
In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code.
Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.
Each line of text in your code should be at most 80 characters long.
Preprocessor directives should not be indented but should instead start at the beginning of the line.
Sections in public, protected and private order, each indented one space.
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 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 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:
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.
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 );
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.