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...) |
(→Naming) |
||
(12 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
MAME source code should be viewed and edited with your editor set to use four spaces per tab. Tabs are used for initial indentation of lines, with one tab used per indentation level. Spaces are used for other alignment within a line. | |||
Some parts of the code follow [https://en.wikipedia.org/wiki/Indent_style#Allman_style Allman style]; some parts of the code follow [https://en.wikipedia.org/wiki/Indent_style#K.26R_style K&R style] — mostly depending on who wrote the original version. '''Above all else, be consistent with what you modify, and keep whitespace changes to a minimum when modifying existing source.''' For new code, the majority tends to prefer Allman style, so if you don't care much, use that. | |||
All contributors need to either add a standard header for license info (on new files) or inform us of their wishes regarding which of the following licenses they would like their code to be made available under: the [http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause] license, the [http://opensource.org/licenses/LGPL-2.1 LGPL-2.1], or the [http://opensource.org/licenses/GPL-2.0 GPL-2.0]. | |||
The guidelines below are not hard-and-fast rules, but more a set of guidelines describing conventions used in MAME source. | |||
The | |||
== Naming == | == Naming == | ||
* function and variable names | * class, function, method, and variable names use the <code>lower_under_convention</code> | ||
* macros | * not everyone uses scope prefixes, but if you're going to, please follow this convention: | ||
** instance member variables should have an m_ prefix (e.g., <code>m_device</code>, <code>m_config</code>, etc.) | |||
** static members should have an s_ prefix (e.g., <code>s_device_table</code>, etc.) | |||
** global variables should have a g_ prefix (e.g., <code>g_profiler</code>, etc.) | |||
** file scope variables (file statics or anonymous namespace) should have an f_ prefix (e.g., <code>f_ntfs_offset</code>, etc.) | |||
** static member functions may have a static_ prefix (e.g., <code>static_timer_callback</code>) | |||
* macros, enum items, and #defined constants should be named using the <code>ALL_CAPS_UNDER_CONVENTION</code> | |||
**static constant members should be treated as constants and should be in ALL_CAPS_UNDER_FORMAT (e.g., <code>MAXIMUM_ITEMS</code>) | |||
* template parameters should be named using the <code>PascalCaseConvention</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>) | ||
== Comments == | == Comments == | ||
* comments | * single-line comments should use <code>// C++ line comment style</code> | ||
* comments | * larger block comments should use <code>/* C comment style */</code> to make editing and reformatting easier | ||
* | * interface class and function declarations should ideally have [https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html Doxygen]-format comments | ||
== Spacing == | == Spacing == | ||
* a space | * indenting should follow scope blocks, one level of indent per scope level | ||
* | * use extra indent for statement continuation to make it easily distinguishable from a scope block | ||
* spaces | * no space between the function name and opening parenthesis of argument list: <code>std::strlen(param[1])</code> | ||
* | * space between flow control keyword and opening parenthesis of expression: <code>for (x = 0; x < 10; x++)</code> | ||
* | * no space between unary unary operators and operands: <code>*it++ = -value</code> | ||
* spaces between binary/ternary operators and operands: <code>flag ? (a + 1) : b</code> | |||
* no space between parenthesis/bracket and the inner expression: <code>(((i + j) * k) >> m)</code> | |||
* use blank lines to visually separate logical blocks; use more consecutive blank lines to separate bigger logical blocks | |||
== | == Scoping == | ||
* | * in general, keep scoping as tight as possible — tighter scope makes code easier to analyse | ||
* | * keep members private to protected if possible and provide accessors for externally visible state | ||
* | * use protected inheritance if the base class/struct isn't part of the interface | ||
* | * declare constants inside the public section of classes they're relevant to | ||
* ' | * restrict local variable scope to parts of the function where they're needed needed | ||
* use anonymous namespaces for things that shouldn't be visible outside a single source file | |||
== Language conventions == | == Language conventions == | ||
* prefer references over pointers, using pointers primarily in situations where '''nullptr''' is a valid option | |||
* use '''static''' and '''const''' keywords aggressively where appropriate | * use '''static''' and '''const''' keywords aggressively where appropriate | ||
* | * use <code>std::function</code> for callbacks when performance isn't critical, or MAME delegates when it is | ||
* wherever possible, use ''' | * wherever possible, use '''enum''' instead of a macro | ||
* used scoped '''enum''' (<code>enum class</code>) where a closed set of values are valid | |||
* wherever possible, use '''inline''' functions instead of macros | |||
* wherever possible, use templates 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> | ||
* use rvalue references and universal references when it could help performance | |||
* ensure non-POD types used in vectors/deques are movable to avoid performance issues | |||
* don't abuse '''auto''' type — it is useful for template argument dependent types, iterators and proxies, but it can be dangerous and doesn't help readability/maintainability for locals where you know the type | |||
* use anonymous namespaces to suppress symbol export — it works on all types of symbols including class members, it encourages grouping of non-exported classes/functions/globals, it avoids visual clutter of the <code>static</code> prefix on each declaration/definition, and it reduces overloading of the meaning of the <code>static</code> keyword | |||
== Variables == | == Variables == | ||
* avoid declaring static variables inside a function scope — these are really global variables and belong at the top of the module | * 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 | * declare all global variables at the top of the file | ||
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 | |||
#ifndef MAME_MODULE_FILENAME_H | |||
#define MAME_MODULE_FILENAME_H | |||
#pragma once | |||
and adding | |||
#endif // MAME_MODULE_FILENAME_H | |||
to the end. Note that <code>#pragma once</code> is provided because it may 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) | |||
Back to [[How MAME Works]] |
Latest revision as of 12:06, 24 July 2017
MAME source code should be viewed and edited with your editor set to use four spaces per tab. Tabs are used for initial indentation of lines, with one tab used per indentation level. Spaces are used for other alignment within a line.
Some parts of the code follow Allman style; some parts of the code follow K&R style — mostly depending on who wrote the original version. Above all else, be consistent with what you modify, and keep whitespace changes to a minimum when modifying existing source. For new code, the majority tends to prefer Allman style, so if you don't care much, use that.
All contributors need to either add a standard header for license info (on new files) or inform us of their wishes regarding which of the following licenses they would like their code to be made available under: the BSD-3-Clause license, the LGPL-2.1, or the GPL-2.0.
The guidelines below are not hard-and-fast rules, but more a set of guidelines describing conventions used in MAME source.
Naming
- class, function, method, and variable names use the
lower_under_convention
- not everyone uses scope prefixes, but if you're going to, please follow this convention:
- instance member variables should have an m_ prefix (e.g.,
m_device
,m_config
, etc.) - static members should have an s_ prefix (e.g.,
s_device_table
, etc.) - global variables should have a g_ prefix (e.g.,
g_profiler
, etc.) - file scope variables (file statics or anonymous namespace) should have an f_ prefix (e.g.,
f_ntfs_offset
, etc.) - static member functions may have a static_ prefix (e.g.,
static_timer_callback
)
- instance member variables should have an m_ prefix (e.g.,
- macros, enum items, and #defined constants should be named using the
ALL_CAPS_UNDER_CONVENTION
- static constant members should be treated as constants and should be in ALL_CAPS_UNDER_FORMAT (e.g.,
MAXIMUM_ITEMS
)
- static constant members should be treated as constants and should be in ALL_CAPS_UNDER_FORMAT (e.g.,
- template parameters should be named using the
PascalCaseConvention
- 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
)
Comments
- single-line comments should use
// C++ line comment style
- larger block comments should use
/* C comment style */
to make editing and reformatting easier - interface class and function declarations should ideally have Doxygen-format comments
Spacing
- indenting should follow scope blocks, one level of indent per scope level
- use extra indent for statement continuation to make it easily distinguishable from a scope block
- no space between the function name and opening parenthesis of argument list:
std::strlen(param[1])
- space between flow control keyword and opening parenthesis of expression:
for (x = 0; x < 10; x++)
- no space between unary unary operators and operands:
*it++ = -value
- spaces between binary/ternary operators and operands:
flag ? (a + 1) : b
- no space between parenthesis/bracket and the inner expression:
(((i + j) * k) >> m)
- use blank lines to visually separate logical blocks; use more consecutive blank lines to separate bigger logical blocks
Scoping
- in general, keep scoping as tight as possible — tighter scope makes code easier to analyse
- keep members private to protected if possible and provide accessors for externally visible state
- use protected inheritance if the base class/struct isn't part of the interface
- declare constants inside the public section of classes they're relevant to
- restrict local variable scope to parts of the function where they're needed needed
- use anonymous namespaces for things that shouldn't be visible outside a single source file
Language conventions
- prefer references over pointers, using pointers primarily in situations where nullptr is a valid option
- use static and const keywords aggressively where appropriate
- use
std::function
for callbacks when performance isn't critical, or MAME delegates when it is - wherever possible, use enum instead of a macro
- used scoped enum (
enum class
) where a closed set of values are valid - wherever possible, use inline functions instead of macros
- wherever possible, use templates instead of macros
- macros that look like functions should be wrapped with
do { <macrobody> } while (0)
- use rvalue references and universal references when it could help performance
- ensure non-POD types used in vectors/deques are movable to avoid performance issues
- don't abuse auto type — it is useful for template argument dependent types, iterators and proxies, but it can be dangerous and doesn't help readability/maintainability for locals where you know the type
- use anonymous namespaces to suppress symbol export — it works on all types of symbols including class members, it encourages grouping of non-exported classes/functions/globals, it avoids visual clutter of the
static
prefix on each declaration/definition, and it reduces overloading of the meaning of thestatic
keyword
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
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
#ifndef MAME_MODULE_FILENAME_H #define MAME_MODULE_FILENAME_H #pragma once
and adding
#endif // MAME_MODULE_FILENAME_H
to the end. Note that #pragma once
is provided because it may 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)
Back to How MAME Works