Code Standard

From Hero of Allacrost Wiki
Revision as of 03:40, 24 July 2015 by Roots (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

The purpose of this code standard is to set some ground rules for Allacrost programmers. These rules are created to prevent bugs, improve code readbility, and to avoid frustration from dealing with different naming schematics. The following sections are written to be as concise as possible to keep the verbosity of this document at a minimum.

Note: You are not expected to memorize all of these rules. In general, you can meet these requirements simply by following the same conventions and formats that you see in existing code.

Files and Directories[edit]

How to name and write headers for C++ and Lua files.

  • C++ header files must have a .h suffix.
  • C++ source files must have a .cpp suffix.
  • Lua files must have a .lua suffix.
  • Do not put spaces in your filenames. Use underscores to separate words.
  • Filenames should be simple and typically contain only one or two words. For example, audio.cpp or map_sprites.h.
  • All C++ header and source files should go in src/. However, build system files are allowed to reside in the main directory.
  • All Lua files should go in lua/.
  • It is recommended, though not required, to use a file name prefix for all files in a related group. For example, map.h and map_objects.h.
  • To include an Allacrost header file, simply add #include "file.h". You do not need to specify a pathname.
  • The following must be placed at the top of every C++ Allacrost header and source file.
    • Lua files may have a similar header included, but it is not required as many Lua files are simply collections of data.
//            Copyright (C) 2004-2015 by The Allacrost Project
//                         All Rights Reserved
// This code is licensed under the GNU GPL version 2. It is free software 
// and you may modify it and/or redistribute it under the terms of this license.
// See for details.
  • Immediately below the copyright notice, all C++ files should have a doxygen comment block to describe the purpose of the file. An example is below.
/** ****************************************************************************
*** \file audio.h
*** \author Tyler Olsen -
*** \author Aaron Smith -
*** \brief Header file for audio engine interface.
*** This code provides an easy-to-use API for managing all music and sounds used
*** in the game.
*** \note This code uses the OpenAL audio library. See
*** ***************************************************************************/ 

Naming Conventions[edit]

The rules for naming of classes, functions, variables, members, and constants.

  • Classes should contain only lowercase and uppercase letters. The first letter of each word in a class name shall be capitalized (CamelCase).
    class GameSettings() { ... };
  • Constants should consist of all uppercase letters, underscores, and numbers.
    const uint32 MY_GAME_CONSTANT0 = 0;
  • Variables and class members should be all lowercase letters, underscores, and numbers.
    uint32 tile_rows = 50; MapSprite main_character;
    • All private class members must be prefixed with an underscore.
      bool MapMode::_random_encounters;
  • Enums should consist of all uppercase letters, underscores, and numbers (both the enum type and its values). There should also be an invalid member (equal to -1) and a total member (used for iterating through all possible assignment valuse). All enum values need to be specifically assigned a value as well.
  • Functions should consist only of lowercase and uppercase letters, where the first letter of each word in the function name is capitalized (CamelCase).
    bool GameInput::ConfirmState();
    There are three addendums to this rule:
    • All private class functions are prefixed with an underscore.
      void MapMode::_GetDrawInfo();
    • A function used strictly for debugging purposes must begin with DEBUG_, and be further prefixed with an underscore if it is a private class function.
      void GameModeManager::DEBUG_PrintStack();
    • A temporary function that will eventually become defunct must begin with TEMP_, and be further prefixed with an underscore if it is a private class function.
      void MapMode::TEMP_CreateRandomMap();
  • Class member accessors (methods that only set or return the value of a class member) must follow these additional conventions.
    • Method names should be prefixed with Get or Set> accordingly.
      float GameAudio::GetMusicVolume();
      void GameAudio::SetMusicVolume(float volume);
      • The only exception is when returning a boolean value, in which case the function should be prefixed with Is
        bool GameVideo::IsStatic();
    • Method names should be written as a CamelCase conversion of the class member in question, removing the prefixing underscore of class members if necessary. For example:
class GameAudio {
  float _music_volume;
  float GetMusicVolume() { return _music_volume; }


How to properly name, define, and use namespaces.

  • All header and source files must contain all of their code and constructs within a namespace.
    • The only file that is an exception to this rule is main.cpp.
  • The using namespace directive may only be used in source files, not header files.
  • Namespaces must match the filename and be prefixed with hoa_. For example, audio.h has the namespace hoa_audio.
    • Code that is split into multiple files should share the same namepace (ie, map.h and map_objects.h use namespace hoa_map).
  • Additionally, each hoa_ namespace may have an embedded private namespace that is to be used only within the code of the base namespace and not by other namespaces.
    • The private namespace name shall be called private_ followed by the base namespace name. For example, audio.h has namespace private_audio embedded in namespace hoa_audio.
  • Constants that are defined inside the base namespace (and thus are intended to be used by other parts of the code) must be prefixed with the file name. Example: constants in the file audio.h and inside namespace hoa_audio are prefixed by AUDIO_.
    • Constants defined in the private embedded namespace, however, do not need to follow the above convention.

Style and Conventions[edit]

How to properly indent and otherwise write source code.

  • Do not use spaces for indentation. Please use tabs only. It is recommended that you configure your editor to display tabs as four spaces long.
  • Judicious use of encapsulation to hide class members from external code is strongly recommended. Very few class members should be made public. Write accessor methods to allow external access.
  • Use const wherever possible in function calls to ensure that data that should not be modified remains immutable.
  • When assigning floats, specify the f suffix on the number. For example: float temp = 25.3f;
  • Do not put a space between function names and parenthesis. However, do put a space in between block logic (if, switch, while, etc.) and parentheses.
    if (sprite == NULL) {
  • Other than the rules above, you are free to style your code however you feel most comfortable. But please make your code style consistent throughout the code you write and any code in the same file that surrounds it.

Forbidden Constructs[edit]

C++ constructs that are either strictly forbidden or not typically seen in Allacrost source code.

  • Do not use int, unsigned char, etcetera. You may only use the following integer types, which are guaranteed to be the same size across platforms.
    • int32
    • uint32
    • int16
    • uint16
    • int8
    • uint8
  • Do not use doubles. That amount of precision should not be required. Use floating-point types instead.
  • Do not use structs. Instead, use classes with all public members.
  • Do not use multiple inheritance, unless you have a very good reason to.
  • Try to avoid template classes, except where you feel they are absolutely necessary.
  • Do not use #define for constants. Use const instead.

Error Handling[edit]

Implementation standard for communicating detected errors with other parts of the system.

  • All functions that load data should return a boolean value to indicate success (true) or failure (false).
  • Except as stated above, no other functions should return values to indicate success or failure.
  • Each class that performs error checking (other than only loading errors) must retain some data to retain error codes.
    • The error data must either be private.
    • Typically, an unsigned integer is kept with each bit representing a different error code.
    • The specific bit-codes should be accessible outside of the class via a series of constants made available in the class' namespace.
  • Classes with error code data must provide a member access function named CheckErrors() that returns the error data.
  • The CheckErrors() function resets the error code data to a no-error condition when it is called.

C++ Exceptions[edit]

Rules for using exceptions in C++ code.

  1. All exceptions must be of type hoa_utils::Exception or its derived classes.
  2. Direct usage of hoa_utils::Exception is not recommended (nor is it forbidden). Use derived exceptions such as VideoException, AudioException whenever possible or if they're not available, go create a new exception class.
  3. Higher level exception handlers and catch-blocks are allowed to take plain hoa_utils::Exceptions as their parameters.
  4. When catching, always use references to Exceptions as it guarantees support for derived exceptions as well.
  5. Never use catch(...) as it does not guarantee the type of exception to be a hoa_utils::Exception.
  6. Always initialize the parameters that indicate file, line and function when exception is thrown. You can use __FILE__, __LINE__ and __FUNCTION__ macros as they're all cross-platform.
  7. Do not use exception specification lists. Instead, use doxygen comments such as // @exception outOfMemory to document the exceptions.
  8. Do not use exceptions in performance-critical areas of code. Throwing exceptions is more expensive than a try-block.
  9. Do not attempt to throw an exception inside an another one.
  10. Use exceptions only in exceptional places i.e. when something went wrong, but you cannot handle the situation in that scope of code.
  11. Never throw exceptions in destructors. Throwing in constructors is perfectly valid though.
  12. Avoid throwing by value or by pointers and avoid catching by pointer, unless an existing library requires so.


Strict guidelines on how to satisfactorly comment both header and source files.

Note: This guide does not go into detail about doxygen or how to use it. Please see the Doxygen homepage and manual for more information.

Header Files[edit]

  1. Header files must be commented with doxygen style comments. These comments are used to automatically generate reference documentation for your code. (Note, however, that you may still have to provide self-written documentation, depending on the code in question.)
    • The only doxygen comment required for source files is the file descriptor comment, as explained below.
  2. A file descriptor comment must be placed at the top of every header and source file. An example file descriptor follows below. The verbose description and notes are optional, but encouraged to be used where viable. Typically only header files need these extra comments, and you do not have to replicate the same information in the header's corresponding source file.
*** \file    audio.h
*** \author  Tyler Olsen,
*** \brief   Header file for audio engine interface.
*** This code provides an easy-to-use API for managing all music and sounds data
*** used throughout the game.
*** \note This code uses the following libraries:
***   -# OpenAL,
***   -# ALUT,
***   -# Ogg Vorbis,
  1. All namespaces, constants, functions, classes, and class members, must be commented. There are a few exceptions to this rule though.
    • Constructors, destructors, and overloaded operators do not need to be commented. If they serve some special purpose or are otherwise unusual, however, you should comment them to avoid confusion.
    • Macros do not need to be commented.
    • TEMP_ and DEBUG_ functions may be commented when appropriate, but it is not a requirement to do so.
    • Items that share the same properties can be placed in groups instead of commented individually (details on groups will follow shortly).
  2. Provide links to specific members in your comments whenever possible. For example:
/** When music is loaded, the internal MusicDescriptor#id argument is the same 
*** value as the MusicItem#id argument in the MusicItem class.

Here, we have linked to the id argument in both the MusicDescriptor and MusicItem classes, as well as the MusicItem class itself. (Typing a class name in a comment automatically creates a link to it).

  1. When commenting a function, please use the param and return tags to comment all parameters and the return value (if any), unless it is blantantly obvious from the function name or its description. Example:
/** \brief Determines whether an object may be placed on a tile.
*** \param &tcheck A reference used to check the properties of the tile in question.
*** \return True if an object may move to the tile, false otherwise.
bool _TileMoveable(const private_map::TileCheck& tcheck);
  1. To avoid tedious and repetitive comments, structures that are very similar in purpose may be placed into groups, and only a single comment is required for the entire group. This is very useful for class member access functions and constants, which often share similar properties. It is still possible, however, to comment individual members in a group. Refer to the example below:
/** \name Input state member access functions
*** \brief Returns true if the named input event key/button is currently being held down
bool UpState() { return _up_state; }
bool DownState() { return _down_state; }
bool LeftState() { return _left_state; }
bool RightState() { return _right_state; }
//! This is a comment for the ConfirmState function only
bool ConfirmState() { return _confirm_state; }
bool CancelState() { return _cancel_state; }
bool MenuState() { return _menu_state; }
bool SwapState() { return _swap_state; }
bool LeftSelectState() { return _left_select_state; }
bool RightSelectState() { return _right_select_state; }
  1. The best way to get familiar with our header commenting style is to actually look at some of the Allacrost header files and mimic what you see there.

Source Files[edit]

  • The only doxygen commenting required for source files is for the file descriptor. The only tags required are: file, author, date, and brief. Do not include a verbose description or notes, as those should already be stated in the corresponding header file. Here is an example of a source file descriptor.
*** \file    audio.cpp
*** \author  Tyler Olsen,
*** \brief   Source file for audio engine interface.
  • With the exception of the file descriptor, source file commenting is very lax and developers are free to comment their code anyway they see fit. Here are some useful guidelines to follow.
    1. Like header files, comment the last bracket of long namespaces, functions, or other blocks of code so it is clear when the construct terminates.
    2. Before each function, put a small but meaningful comment reminding the reader what the function does.
    3. Comment all local variables in functions to explain what they are used for, if its not intuitive.
    4. Do not over-comment. For example, count++; // increment count is not a good comment.
    5. Do not under-comment. Others need to be able to read your code and understand what it does without having to figure it out themselves.
    6. Try commenting a long section code in a series of segments and summarize what each segement does (see below for an example).

void MapMode::Update(uint32 new_time_elapsed) {
// ********* (1) Update the tile animation frames
// ********* (2) Update the map based on what state it is in
// ********* (3) Sort objects so they are in the correct draw order
} // MapMode::Update(uint32 new_time_elapsed)

    1. Arrange your functions in a logical order so they are easy to find.
      1. Sort the source file by class definitions. That is, put all functions of class A at the top, followed by class B, etc. Do not mix class A functions with class B functions because it makes it very difficult on the reader.
      2. For a game mode implementation, it is recommended you put general functions (including constructors/destructors) at the top, update functions afterwards, and draw functions at the bottom.

Lua Coding Style[edit]

A small standard that pertains only to Lua code, not C++.

  • The following must be placed at the top of every Allacrost Lua file.
--[[ --------------------------------------------------------------------------
--            Copyright (C) 2004-2013 by The Allacrost Project
--                         All Rights Reserved
-- This code is licensed under the GNU GPL version 2. It is free software 
-- and you may modify it and/or redistribute it under the terms of this license.
-- See for details.
--]] --------------------------------------------------------------------------
  • Use tabs for indentation
  • Use the local identifier for all variables tables, and functions that only need to be visible in the immediate scope.


Verbosely explains the reasoning behind many of the code standards set in previous sections.

This section describes the reasoning behind some of the less intutive code standard rules given above. It exists for those that are interested in why we formulated these policies, and is not required reading. ***This section is currently incomplete and will be fleshed out more fully at a later time.

  • II.C - Enum Values

Ordinarily, enumerators will start at 0 for the first symbolic constant, 1 for the next, and so on. However, an enumeration type can acquire an integer value that may not be any of the enumerators listed in or implied by the enumeration definition. Assigning enumerators specific values and defining INVALID and TOTAL symbolic constants prevents the programmer from committing mistakes.

  • II.D.1 and II.E.1 - Underscores for Private Class Members

It makes it much easier to discern what members and functions of a class are intended for internal use and what is available for external use by prefixing these private constructs with an underscore. Presumably, the majority of the people reading this documentation wish to actually use the engine and don't care about the inner details. With this convention, they know exactly what class members and functions they can safely ignore.

  • III.E. and III.E.1 - Namespace Constant Prefixing

The purpose for having a special prefix for each constant related to the namespace it comes from serves two purposes. First, it makes it really easy to know where a constant is defined when it is used elsewhere in the code. Second, it avoids naming conflicts. If I wanted to set the coordinates of the screen to be a grid of tiles in two different game mode implementations, I might accidentally name them the same TILE_ROW and TILE_COLUMNS (causing a compile error), or I might easily get confused between which constant to use in another part of the code. The private embedded namespace allows the programmer to hide constants and other data structures that other parts of the code don't need to see, and also has no naming restrictions.

  • IV.A - Indentation with Tabs, not Spaces

Yes, the age-old debate. The reason we use tabs is because we didn't want to have to force anyone on the programming team to adapt to an indentation width that they are uncomfortable with (ie, mandating 2 space or 4 space indentations). By using tabs, the programmer can set their IDE settings to have tabs represent the amount of spacing that they are most comfortable with. It's also easier to press tab once than press space multiple times.

  • IV.D - 'f' Suffix on Floating Point Numbers

Having an 'f' on the end of floating numbers is part of the ANSI standard. It also helps the compiler to distinguish the value between a float and a double.

  • V.A - Required Integer Types

As technology makes its transition to the 64-bit world, we can no longer rely on an int being exactly 4 bytes anymore. True, in most cases this would not matter, but in some places of the code we use bit-masked values and therefore knowing the correct size of your integer types is critical. The types were made by simplying creating typedefs for the types defined in SDL_types.h (we didn't like their convention which had the first letter of the type capitalized and used an 'S' prefix for signed integers).