MAME Coding Conventions: Difference between revisions
(New page: This page is WIP. Please don't edit it until this notice is removed. MAME is a project that has had many contributors from many different backgrounds. Throughout its history, there have n...) |
No edit summary |
||
Line 20: | Line 20: | ||
== Naming == | == Naming == | ||
* function and variable names are named using the <code>lower_under_convention</code> | * function and variable names are named using the <code>lower_under_convention</code> | ||
* macros are named using the <code>ALL_CAPS_UNDER_CONVENTION</code> | * macros and constants are named using the <code>ALL_CAPS_UNDER_CONVENTION</code> | ||
* constants which are part of a group should have a common prefix; example: <code>enum { ADDRESS_SPACE_PROGRAM, ADDRESS_SPACE_DATA, ADDRESS_SPACE_IO };</code> | |||
* prefer descriptive variable names (<code>sampnum</code>, <code>memoffset</code>) over single-letter names (<code>i</code>, <code>j</code>) | * prefer descriptive variable names (<code>sampnum</code>, <code>memoffset</code>) over single-letter names (<code>i</code>, <code>j</code>) | ||
* union and struct names should begin with a leading underscore, and should all be typed to an unprefixed name before their definition: | |||
typedef struct _mystruct mystruct; | |||
struct _mystruct { }; | |||
typedef union _myunion myunion; | |||
union _myunion { }; | |||
* named enum names should also begin with a leading underscore, but should be typed to an unprefixed name ''after'' their definition: | |||
enum _myenum { }; | |||
typedef enum _myenum myenum; | |||
== Comments == | == Comments == | ||
Line 39: | Line 48: | ||
* do not use parentheses with '''return''', it is not a function — example: <code>return 0;</code> | * do not use parentheses with '''return''', it is not a function — example: <code>return 0;</code> | ||
* do not overuse parentheses except to clarify a statement — example: <code>if (a >= 10 && a < 20)</code> | * do not overuse parentheses except to clarify a statement — example: <code>if (a >= 10 && a < 20)</code> | ||
* always use NULL (not 0) when working with pointers | |||
* make comparisons against NULL explicit: <code>if (ptr != NULL)</code> | * make comparisons against NULL explicit: <code>if (ptr != NULL)</code> | ||
* make comparisons against 0 explicit: <code>if (strcmp(string1, string2) == 0)</code> | * make comparisons against 0 explicit: <code>if (strcmp(string1, string2) == 0)</code> | ||
Line 45: | Line 55: | ||
== Language conventions == | == Language conventions == | ||
* use '''static''' and '''const''' keywords aggressively where appropriate | * use '''static''' and '''const''' keywords aggressively where appropriate | ||
* create typedefs for function pointers; example: <code>typedef void (*my_callback_func)(UINT32 param);</code> | |||
* make calls through function pointers explicit — example: <code>(*funcptr)(a, b)</code> | * make calls through function pointers explicit — example: <code>(*funcptr)(a, b)</code> | ||
* wherever possible, use '''enum''' instead of a macro | |||
* wherever possible, use '''INLINE''' functions instead of macros | * wherever possible, use '''INLINE''' functions instead of macros | ||
* macros that look like functions should be wrapped with <code>do { <macrobody> } while (0)</code> | * macros that look like functions should be wrapped with <code>do { <macrobody> } while (0)</code> | ||
Line 54: | Line 66: | ||
* use the MAME-defined types: <code>INT8</code>, <code>UINT8</code>, <code>INT16</code>, <code>UINT16</code>, <code>INT32</code>, <code>UINT32</code>, <code>INT64</code>, <code>UINT64</code> | * use the MAME-defined types: <code>INT8</code>, <code>UINT8</code>, <code>INT16</code>, <code>UINT16</code>, <code>INT32</code>, <code>UINT32</code>, <code>INT64</code>, <code>UINT64</code> | ||
order of | == Header Files == | ||
* the preferred order of definitions in a header file is: | |||
** standard header | |||
** reinclusion protection (see below) | |||
** includes | |||
** debugging flags | |||
** constants | |||
** type definitions | |||
** macros | |||
** global variables | |||
** function prototypes | |||
** and inline functions | |||
* function prototypes in header files generally do not use an '''extern''' qualifier | |||
* all header files should support reinclusion; this is done by adding the following to the top of each header | |||
#pragma once | |||
#ifndef __FILENAME_H__ | |||
#define __FILENAME_H__ | |||
and adding | |||
#endif /* __FILENAME__H__ */ | |||
to the end. Note that <code>#pragma once</code> is provided because it is generally faster when supported. Since not all compilers do support it, the <code>#ifndef/#define</code> methods are retained as a fallback. | |||
== Source Files == | |||
* the preferred order of code in a source file is: | |||
** standard header | |||
** includes | |||
** debugging flags | |||
** constants | |||
** type definitions | |||
** macros | |||
** global variables (both static and global) | |||
** internal function prototypes | |||
** inline functions | |||
** externally referenced functions | |||
** internal functions (in same order as prototyped) |
Revision as of 20:03, 31 March 2008
This page is WIP. Please don't edit it until this notice is removed.
MAME is a project that has had many contributors from many different backgrounds. Throughout its history, there have never really been any kind of formal coding conventions defined, although thanks to imitation, there is at least a glimmer of consistency.
In general, a codebase with consistent conventions is easier to understand than one with varying conventions. However, trying to impose a strict order on a project of this magnitude is certainly taking things too far.
If you categorize the MAME codebase, you can look at it like this:
- OS-specific code (OSD)
- MAME "core" code
- CPU cores
- Sound engines
- Game drivers
The first two pieces (the core and OSD) are in general only handled by a small group of developers, while the remaining pieces (drivers and CPU/sound cores) come from a much broader audience. Furthermore, the drivers and CPU/sound cores all interact to some degree with core and OSD pieces below them, so the most benefit from code clarity and consistency comes from making the core and OSD pieces consistent.
With that in mind, below is an outline some of the key coding conventions currently in use in the core and OSD layers. If you are modifying code in theses layers and wish to have it accepted upon submission, you would do well to keep to these guidelines. (In fact, if you are modifying any file in any project, you should adopt the conventions of that file/project, rather than just stuffing your own inconsistent style in the middle of something else. I can't believe how many people just ignore the existing styles and jam their own style in the middle.)
One more thing. Keep in mind that coding conventions are like religion: they are often strongly-held beliefs with little factual justification to back them up. You may disagree with them. Heck, even I disagree with a few of them. But they are the conventions that are used. Deal with it.
Naming
- function and variable names are named using the
lower_under_convention
- macros and constants are named using the
ALL_CAPS_UNDER_CONVENTION
- constants which are part of a group should have a common prefix; example:
enum { ADDRESS_SPACE_PROGRAM, ADDRESS_SPACE_DATA, ADDRESS_SPACE_IO };
- prefer descriptive variable names (
sampnum
,memoffset
) over single-letter names (i
,j
) - union and struct names should begin with a leading underscore, and should all be typed to an unprefixed name before their definition:
typedef struct _mystruct mystruct; struct _mystruct { }; typedef union _myunion myunion; union _myunion { };
- named enum names should also begin with a leading underscore, but should be typed to an unprefixed name after their definition:
enum _myenum { }; typedef enum _myenum myenum;
Comments
- comments in the non-OSD parts of the code should all be
/* C-style comments */
- comments in the OSD-specific parts of the code may be
// C++-style comments
(and are in the Windows code) - each function should have a comment preceding it that briefly describes what that function does
Spacing
- a space should be used between binary and trinary operators — example:
a + b / 2
- spaces should not be used around parentheses in expressions — example:
(((i + j) * k) >> m)
- spaces should not be used between a function and its parameters — example:
function(param1, param2)
- a space should be used between keywords (if, while, for) and their arguments — example:
for (x = 0; x < 10; x++)
- opening/closing braces should be on their own line, and should be indented to align with the start of the statement that introduces them
- two blank lines should separate the end of a function from the start of the next function
Expressions
- do not use parentheses with return, it is not a function — example:
return 0;
- do not overuse parentheses except to clarify a statement — example:
if (a >= 10 && a < 20)
- always use NULL (not 0) when working with pointers
- make comparisons against NULL explicit:
if (ptr != NULL)
- make comparisons against 0 explicit:
if (strcmp(string1, string2) == 0)
- don't make comparisons against boolean values explicit:
val = (a == b); if (val)...
Language conventions
- use static and const keywords aggressively where appropriate
- create typedefs for function pointers; example:
typedef void (*my_callback_func)(UINT32 param);
- make calls through function pointers explicit — example:
(*funcptr)(a, b)
- wherever possible, use enum instead of a macro
- wherever possible, use INLINE functions instead of macros
- macros that look like functions should be wrapped with
do { <macrobody> } while (0)
Variables
- avoid declaring static variables inside a function scope — these are really global variables and belong at the top of the module
- declare all global variables at the top of the file
- use the MAME-defined types:
INT8
,UINT8
,INT16
,UINT16
,INT32
,UINT32
,INT64
,UINT64
Header Files
- the preferred order of definitions in a header file is:
- standard header
- reinclusion protection (see below)
- includes
- debugging flags
- constants
- type definitions
- macros
- global variables
- function prototypes
- and inline functions
- function prototypes in header files generally do not use an extern qualifier
- all header files should support reinclusion; this is done by adding the following to the top of each header
#pragma once #ifndef __FILENAME_H__ #define __FILENAME_H__
and adding
#endif /* __FILENAME__H__ */
to the end. Note that #pragma once
is provided because it is generally faster when supported. Since not all compilers do support it, the #ifndef/#define
methods are retained as a fallback.
Source Files
- the preferred order of code in a source file is:
- standard header
- includes
- debugging flags
- constants
- type definitions
- macros
- global variables (both static and global)
- internal function prototypes
- inline functions
- externally referenced functions
- internal functions (in same order as prototyped)