skyline/CONTRIBUTING.md
◱ PixelyIon 55a9f8e937 Refactor all Game-related Kotlin Classes/Objects
This refactors most game-related classes and objects mainly adding spacing, adding comments and making improvements if possible.
2020-04-23 22:26:27 +05:30

6.0 KiB

Contributing Guidelines

C++

Include Order

  • STD includes
  • External library includes
  • Includes in a parent directory with <> (where otherwise you would need ../)
  • Local includes with ""

Naming rules

  • Macro: SCREAMING_SNAKE_CASE
  • Namespaces: camelCase
  • Enum: PascalCase
  • Enumerator: PascalCase
  • Union: PascalCase
  • Classes: PascalCase except for abbreviations such as OS and NCE
  • Local Variable: camelCase
  • Member Variables: camelCase
  • Global Variables: PascalCase
  • Functions: PascalCase
  • Parameter: camelCase
  • Files and Directories: snake_case except for when they correspond to a HOS structure (EG: Services, Kernel Objects)

Comments:

Use doxygen style comments for:

  • Classes/Structs - Use /** block comments with a brief
  • Class/Struct Variables - Use //!< single-line comments on their utility
  • Class/Struct Functions - Use /** block comments on their function with a brief, all arguments and the return value (The brief can be skipped if the function's arguments and return value alone explain what the function does)
  • Enumerations - Use a /** block comment with a brief for the enum itself and a //!< single-line comment for all the individual items

Notes:

  • The DeviceState object can be skipped from function argument documentation as well as class members in the constructor
  • Any class members don't need to be redundantly documented in the constructor

Control flow statements (if, for and while):

If a child control-flow statement has brackets, the parent statement must as well

  • Correct
    if (a) {
      while (b) {
        printf("A");
        printf("B");
        b = false;
      }
    }
    
  • Incorrect
    if (a)
      while (b) {
        printf("A");
        printf("B");
        b = false;
      }
    

If any cases of an if statement have curly brackets all must have curly brackets:

  • Correct
    if (a) {
      printf("a");
      a = false;
    } else {
      printf("none");
    }
    
  • Incorrect
    if (a) {
      printf("a");
      a = false;
    } else
      printf("none");
    

Spacing

We generally follow the rule of "Functional Spacing", that being spacing between chunks of code that do something functionally different while functionally similar blocks of code can be closer together.

Here are a few examples of this to help with intution:

  • If variable declarations are small, they can have no spacing
    auto a = 1;
    auto b = GetSomething();
    DoSomething(a, b);
    
    return;
    
  • If variable declarations are large, spacing should follow them. This applies even more if error checking code needs to follow it.
    auto a = GetSomethingA();
    auto b = GetSomethingB();
    auto c = GetSomethingC();
    
    auto result = DoSomething(a, b, c);
    if (!result)
      throw exception("DoSomething has failed: {}", result);
    
  • If a function doesn't require multiple variable declarations, the function call should be right after the variable declaration
    auto a = GetClassA();
    a.DoSomething();
    
    auto b = GetClassB();
    b.DoSomething();
    
  • If a single variable is used by a control flow statement, there can be no spaces after it's declaration
    auto a = GetClass();
    if (a.something) {
      for(const auto& item : a.items)
        DoSomething(item);
    }
    
  • Be consistent (The above examples assume that a/b/c are correlated)
    • Inconsistent Disaster
      auto a = GetClassA();
      a.DoSomething();
      
      auto b = GetClassB();
      auto c = GetClassC();
      
      b.DoSomething();
      c.DoSomething();
      
    • The Right Way™️
      auto a = GetClassA();
      auto b = GetClassB();
      auto c = GetClassC();
      
      a.DoSomething();
      b.DoSomething();
      c.DoSomething();
      

Readability is key at the end, skirt these rules if you think it increases readability

Lambda Usage

We generally support the usage of functional programming and lambda, usage of it for assigning conditional variables is recommended especially if it would otherwise be a nested ternary statement:

  • With Lambda (Inlined function)
    auto a = random();
    auto b = [a] {
      if (a > 1000)
        return 0;
      else if (a > 500)
        return 1;
      else if (a > 250)
        return 2;
      else
        return 3;
    }();
    
  • With Ternary Operator
    auto a = random();
    auto b = (a > 1000) ? 0 : ((a > 500) ? 1 : (a > 250 ? 2 : 3)); 
    

References

For passing any parameter which isn't a primitive prefer to use references/const references to pass them into functions or other places as a copy can be avoided that way.
In addition, always use a const reference rather than a normal reference unless the argument needs to be modified in-place as the compiler knows the intent far better in that case.

void DoSomething(const Class& class, u32 primitive);
void ClassConstructor(const Class& class) : class(class) {} // Make a copy directly from a `const reference` for class member initialization

Range-based Iterators

Use C++ range-based iterators for any C++ container iteration unless it can be performed better with functional programming (After C++20 when they are merged with the container). In addition, stick to using references/const references using them

Usage of auto

Use auto unless a variable needs to be a specific type that isn't automatically deducible.

u16 a = 20; // Only if `a` is required to specifically be 16-bit, otherwise integers should be auto
Handle b = 0x10; // Handle is a specific type that won't be automatically assigned

Constants

If a variable is constant at compile time use constexpr, if it's only used in a local function then place it in the function but if it's used throughout a class then in the corresponding header add the variable to the skyline::constant namespace. If a constant is used throughout the codebase, add it to common.h.

In addition, try to constexpr as much as possible including constructors and functions so that they may be initialized at compile-time and have lesser runtime overhead during usage and certain values can be pre-calculated in advance.