skyline/CONTRIBUTING.md

179 lines
5.9 KiB
Markdown
Raw Normal View History

# 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
Note: The DeviceState object can be skipped from function argument documentation as well as class members 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
```cpp
if (a) {
while (b) {
printf("A");
printf("B");
b = false;
}
}
```
* Incorrect
```cpp
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
```cpp
if (a) {
printf("a");
a = false;
} else {
printf("none");
}
```
* Incorrect
```cpp
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
```cpp
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.
```cpp
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
```cpp
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
```cpp
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
```cpp
auto a = GetClassA();
a.DoSomething();
auto b = GetClassB();
auto c = GetClassC();
b.DoSomething();
c.DoSomething();
```
* The Right Way:tm:
```cpp
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)
```cpp
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
```cpp
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.
```cpp
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.
```cpp
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.