MAME Coding Conventions: Difference between revisions

From MAMEDEV Wiki
Line 20: Line 20:
== Naming ==
== Naming ==
* function, method, and variable names are named using the <code>lower_under_convention</code>
* function, method, and variable names are named using the <code>lower_under_convention</code>
* static member functions should have a static_ prefix (e.g., static_timer_callback)
* member variables within a class should have a standard prefix, as follows:
* member variables within a class should have a standard prefix, as follows:
** normal variables should have an m_ prefix (e.g., m_device, m_config, etc)
** normal variables should have an m_ prefix (e.g., m_device, m_config, etc)

Revision as of 19:37, 21 May 2010

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, method, and variable names are named using the lower_under_convention
  • static member functions should have a static_ prefix (e.g., static_timer_callback)
  • member variables within a class should have a standard prefix, as follows:
    • normal 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)
    • static constant members should have a k_ prefix (e.g., k_maximum_items)
  • macros, enum items, and #defined constants should be 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)
  • never use the prefix "my" for anything; it's not "myobject", just use "object"

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)