Coding guidelines

Naming convention

  • Variables and functions are named using the snake_case naming convention.

  • Namespaces and classes are named using the PascalCase naming convention.

  • Macros and enum values are named using the SCREAMING_SNAKE_CASE naming convention.

Macros

When avoidable, try not to use #defines to define constants as they are not namespaced. Instead, prefer using enums.

Do this:

namespace SomeNamespace {
    enum MyEnum {
        SOME_VALUE = 1,
    };
}

Not that:

namespace SomeNamespace {
    #define SOME_VALUE 1
}

Enums

Prefer using enum class rather than a standard enum if you’re not going to do arithmetic on it. This allows for better type safety and forces the EnumName::VALUE syntax which is more explicit than the VALUE syntax.

Do this:

namespace SomeNamespace {
    enum class MyEnum {
        SOME_VALUE = 1,
    };
}

Not that:

namespace SomeNamespace {
    enum MyEnum {
        MY_ENUM_SOME_VALUE = 1,
    };
}

Comments

Documentation using Doxygen comments is encouraged, paraphrasing is discouraged: comments shall describe the why and the code the how.

However, if a piece of code is unclear, comments describing the operation are encouraged.

Formatting the code

The source code comes with a clang-format file can be used along with the tool of the same name to format code properly. It is suggested to make your text editor format the files you modify on save.

Error handling in functions

Error handling in functions is achieved by using the Gaia::Result class. This class is very similar to Rust’s Result<T, E> type.

The Gaia::Result class is a templated class taking two arguments: T and E, with the former representing the type of the value that will be returned on success and the latter representing the type of the value that will be returned on failure. One common option for the E parameter is the Gaia::Error enum. The Gaia::Ok class and the Gaia::Err class are used to return T and E respectively.

Returning void on success

Since passing void as the T parameter would produce invalid code, the Gaia::Null class is available for use; it is simply an empty struct made for that purpose.

The TRY() macro

The TRY() macro can be used in a function that returns a result to call another function returning a result. On failure, the error of the other function will be returned in the caller (the macro inserts a return statement), this allows for a pretty powerful error passing hierarchy. In fact, this is exactly how Gaia::main() works; the functions it calls that return a result are wrapped in a TRY() statement. If the call does succeed, the value is returned by the macro and execution continues.

Example usage

#include <lib/result.hpp>
#include <lib/error.hpp>

using namespace Gaia;

Result<int, Error> function(int param) {
    if(param == 1) {
        return Err(Error::INVALID_PARAMETERS);
    }

    return Ok(2);
}

Result<int, Error> other_function(int param) {
   // On failure, the error will be returned by the function.
   // On success, the value is returned by the macro and execution is continued
   int ret = TRY(function(param));
   return Ok(ret);
}