Clone wiki

OpenSauce-Release / Code / Doc_Code_CodingStandards

Introduction

Bungie

When it comes to general C/C++ code, Bungie uses a lower-underscore-case. Eg, object_create. All words in it are lowercase, and the underscore is used for separating those words. From Halo2 and onward, they started using a subset of Hungarian notation for types:

  • class: c_this_is_a_class
  • struct: s_this_is_a_struct
  • enum: e_this_is_an_enum_typename
  • constant: k_i_am_a_named_constant

For non-POD types they prefix members with m_ (eg, m_definition_index). I've also seen cases where they use the g_" prefix for global variables.

For enumeration types, the typename will be prefixed with e_. Members of the enum wll be prefixed with the typename, sans the initial e. For members which describe a bit index, they typically postfix the member name with _bit. For members which describe a series of flags, I recall seeing them use _flags before as a postfix.

Like say, these are conventions used with the start of Halo2. With Halo1, they were still only using C in the engine code (albeit, the tools had some C++ code). Many of these conventions weren't followed at the time because they didn't have fancy pants classes and such.

And of course, they also use SCREAMING_CAPS for macros definitions.

Kornner Studios

When it comes to C++ based code, we typically follow the same conventions, with a few differences. Where Bungie doesn't use namespaces (as far as I've seen in their production code), we do. For the most part, we also use pascal case for our functions and namespace names. Usually when they're not directly related to reverse engineered Bungie code anyway. There are some exceptions (see EngineFunctions.hpp).

We also keep enum values tucked away in a nested "Enums" namespace. The same goes for bit/flag enumerations, but in a "Flags" namespace.

For typedef's we postfix the typedef name with _t to keep in line with the standard template library, such as this_is_a_typedef_t.

Class and struct names should use lower-underscore-case with a prefix indicating their type. For instance i_configuration_leaf or c_configuration_container_list

  • c_ for classes
  • i_ for interfaces
  • s_ for structs

When defining classes in headers we use the following format:

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// <summary>   Abstract list object for a list of configuration values. </summary>
    ///
    /// <typeparam name="T">    The configuration value type. </typeparam>
    template<typename T>
    class c_configuration_list
        abstract
        : public i_configuration_value
    {

Since C++11 we have been using the final and override keywords in our classes and member functions, you should too. Also, auto is love, auto is life.

When it comes to const correctness I (TheFieryScythe) tend to const all functions and their parameters wherever I can.

.NET

We tend to follow the conventions used by the .NET framework itself. However, for newer code (anything made after 2009) we've started to use m_ prefixes for non-public member data (some of the .NET framework's internals do this, other parts don't). We also use a k prefixed for literals as well (still pascal case after the k though)

Comments

Our in-code documentation of functions is handled using Atomineer Pro Doc. It is a paid product, but it's such a time saver in making documentation quick to add and consistently formatted we find it's worth the cost.

Our documentation across the board has not been converted to a single style yet, that's work in progress. So to save confusion this is the style we are using:

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// <summary>   Makes a unit exit it's current seat. </summary>
    ///
    /// <param name="unit_index">   Zero-based index of the unit. </param>
    /// <param name="force_state">
    ///     (Optional) Whether to force the idle state so that the unit is guaranteed to exit
    ///     gracefully.
    /// </param>
    /// <param name="force_exit">
    ///     (Optional) Whether to forcibly remove the unit from the seat.
    /// </param>
    void UnitExitFromSeat(const datum_index unit_index
        , const bool force_state = false
        , const bool force_exit = false) const;

Assertions

Various ASSERT macros

  • assert(x) - CRT assertion. Only present in _DEBUG builds
  • YELO_ASSERT(x) - Sort of a misnomer. Maps to the blam's ASSERT constructs. These are technically only available in editor builds. YELO_ASSERT's are compiled no matter _DEBUG's definition
  • ASSERT(x, msg) - Yelo's ASSERT macro that exists in both runtime and editor builds, and includes a custom message parameter (string). Only compiled in API_DEBUG builds

assert(x)

  • Use this to assert logic that is truly debug related, esp. to the development of OS
  • ...and when it can't be triggered from user input

    For example, in CheApe assert() is used to verify group definitions (see Engine/AnimationUpgrade.cpp) as these are set in stone.

  • ...and will always be executed (assuming the macro expands to anything).

    For example, tag_group_memory.cpp builds extra data from group definitions (mostly set in stone, CheApe.map is the exception). There's no code path which skips the initialization of tag groups, so as long as this system is enabled this code, and the assert()'s, will be executed

YELO_ASSERT(x)

  • This is mostly for editor/CheApe-only code (as it expands to calls defined in Engine/EngineFunctions.cpp)

ASSERT(x, msg)

  • This is mostly for runtime code
  • Use for code which is compiled in both runtime/editor builds (will expand to YELO_ASSERT(x) in CheApe)

File Names

blamlib and YeloLib

  • Filenames should be lowercase and use underscores instead of spaces
  • _definitions use this postfix for data (namely tag) struct definitions. Eg, unit_definitions.hpp
  • _structures use this postfix for runtime struct definitions. Eg, unit_structures.hpp
  • _system use this postfix for game systems which have an a lot of system definition code (ie, initialize, dispose, etc) on top of regular system interface code (eg some_data_get_id()). Eg, tag_groups_system.cpp

Implementations (eg, Halo1_CE)

  • Filenames should be Pascal cased, ie each first letter is uppercase, no spaces. This is mostly just as a way for telling open files apart

C++

When writing systems using classes it is preferred for each class to be in it's own file, with the file name being the same as the name of the class.

Updated