Closed Lwmte closed 6 months ago
Some proposed additions/amendments:
1) Only use / / style in case you are about to temporarily comment certain block for testing purposes, or when writing a comment that will serve as the source for generated documentation.
Reason: documentation comments are commonly multiline and often require many changes, which are pretty frustrating to write if you need to keep adding or removing slashes.
2) Make sure that any new code is warning-free before committing.
Exceptions to this guideline include cases where fixing warnings would require you to make many changes outside of the new code, where the warning originates from a 3rd-party library, or where fixing the warning would make the code significantly less readable or performant.
3) Functions designed to take an enum argument should take the enum type itself instead of an int or short.
PassInfo(InfoEnum type)
is more readable and error-proof than
PassInfo(int type)
For private variables inside a class, _camelCase should be used.
That's redundant.
We already signify private variables in a class with private:
and using them outside of said class will throw a compiler error anyway.
I'd also use the following for Pointers and References:
Type* somePointer
Type& someReference
instead of
Type * somePointer
Type & someReference
or
Type *somePointer
Type &someReference
The reason is simple:
Pointers and references are distinct types, hence why the notation for them should have the token on the side of the type.
What do we think about names of constant variables?
Some suggestions:
kConstantVar or constant_var or CONSTANT_VAR
CONSTANT_VAR is already used a fair bit, but could cause confusion because people could see it as "old Core constants", therefore kConstantVar gets my vote.
please find the coding conventions here: https://github.com/MontyTRC89/TombEngine/blob/master/CONTRIBUTING.md
General Rules
TODO
prefix, and date-author signature, like this:Do not modify existing code without a clear understanding of what it does. Even if you think you are fixing bugs or restoring original behavior by reverting the code, consult the TombEngine discord before committing any changes. It is possible you may ruin the work of other people since the original Core Design codebase is very fragile and it is very easy to falsely identify code as bugged.
Do not introduce quick hacks to fix issues. Hacks like
if (room == 314)
orif (direction == WEST)
should be left in the history of bad Core Design coding practices.If code remains unimplemented or you need to visit another part of the code for a considerable amount of time to implement missing functionality for that first part of code, leave a comment prefixed
FIXME
and with a date-author signature, describing missing functionality that you are about to add:Make sure that any new code is warning-free before committing. Exceptions to this guideline include cases where fixing warnings would require you to make many changes outside of the new code, where the warning originates from a 3rd-party library, or where fixing the warning would make the code significantly less readable or performant.
Avoid using magic numbers. Use constants or enums instead.
Use American English.
Parenthesis and new lines
Please do not write like this:
Write like this instead:
However, if you have only one line enclosed (i. e. only
foo
), and there are no followingelse
orelse if
conditions, you may omit brackets and do it like this:For one-line if statements, if the condition itself is multi-line, brackets should be added anyway to maintain readability:
Same rule applies to loops.
Avoid multiple nested conditional statements. Where possible, do an early exit from the code using the opposite statement instead of enclosing the statement body in an extra set of brackets. For example, instead of this:
Write this:
Spacing
Don't condense arguments, loop statements, or any other code which requires splitting with commas/semicolons or operators like this:
Instead, separate everything with spaces on the both sides of operator, and after the comma or semicolon, like this:
The same rule applies to any code body, not only function declarations or loop statements.
Tabulation and indents
When you have numerous assignment operations in a row, and/or their names are somewhat of equal length, and their data types are similar, align "left" and "right" parts of the assignment using tabs most of the way and spaces the rest of the way, like this:
Or:
In case you have pointers defined along with "normal" variables, the asterisk symbol must be placed instead of the last tab's space symbol (this also applies for class declarations and/or implementations), like this:
Of course, if one's left part is way longer than another one's left part, there's no need for such alignment, so you can leave it like this:
In a switch case, each case body must be one tab further case's own label. Like this:
If you need to enclose a particular case into a bracket block, put the body one tab further:
Code splitting
If a code block becomes too large, it is recommended to split it into several "sub-blocks" with empty lines, taking each sub-block's meaning into account, like this:
Conditional statements, if there are many, should be grouped according to their meaning. That is, if you are doing early exit from the function because of different conditions, group them as such:
However, if there are few conditional statements, you can group them together. The rule of thumb is if there are more than three conditional statements and two of them are of a different kind, split them. This variant is allowed:
Sometimes IDA decompiled output generates "pascal-styled" or "basic-styled" variable declarations at the beginning of the function or code block. Like this:
Please, get rid of this style everywhere you see it and declare variables in the place narrowest to its actual usage, like this:
Let's cite Google Style Guide here:
Naming
Use
auto
where possible and if the original type name is longer thanauto
. E.g. there is no point in changingbool enabled = true
toauto enabled = true
. Remember that C++auto
is not similar to C#var
, and for referencing existing value withauto
, you should add&
to it in the end, e.g.auto& item = g_Level.Items[itemIndex];
Also, for underlying pointer types, please writeauto*
instead ofauto
, even if it seems redundant.Avoid using Hungarian notation for function names. Currently, inconsistent notation indicates an unrefactored coding style, because the original code used inconsistent naming, like
SOUND_Stop()
,have_i_got_object()
orgar_SuperpackYXZ()
. These should eventually be eradicated along the course of code restyling. For new function names,PascalCase
should be used.For global struct or class members or methods,
PascalCase
should be used. For local variable names,camelCase
should be used. For global variables, which are temporary until full refactoring occurs, an exclusive case of Hungarian notation with theg_
prefix (e.g.g_Foo
) is permitted.Functions designed to take an enum argument should take the enum type itself instead of an int or short.
PassInfo(InfoEnum type)
is more readable and error-proof thanPassInfo(int type)
.Functions designed to return boolean value (0 or 1) should define return type as bool. Please don't use
int
return type and don't writereturn 0
orreturn 1
in bool return functions, usereturn false
orreturn true
.Use the following convention for Pointers and References:
Type* somePointer
/Type& someReference
. Do not write this:Type * somePointer
/Type & someReference
orType *somePointer
/Type &someReference
. Pointers and references are distinct types, hence why the notation for them should have the token on the side of the type.Avoid unscoped enum types. For scoped
enum class
types, usePascalCase
without including enum prefix:ENUM_ALL_CAPS
primarily indicates old C-styled Core notation. For C-styled (unscoped) enum values themselves,ALL_CAPS
may be used for now, along with enum prefix:Data types
Avoid using
_t
data types, such asuint8_t
,int8_t
,int16_t
etc. Useunsigned char
,signed char
,short
etc. instead. If new variables or fields are introduced, prefer longer and more contemporary data types over deprecated ones. That is, if integer value is used, useint
instead ofchar
orshort
. If potentially fractional value is used (such as coordinates which are eventually multiplied or divided or transformed otherwise), preferfloat
overint
.For legacy functions and code paths, preserving original data types may be necessary. Special case are angle values - original games used weird
signed short
angle convention. So extra caution must be taken when writing code which operates on native TR angles, and it should always be kept in variables ofsigned short
data type.Prefer using references over pointers, both in function body and as arguments. When using references or pointers, prefix with
const
for read-only safety if the variable is not being written to.Casting
Prefer using C-styled casting instead of C++-styled casting where it is safe. While using it, avoid separating casting operator and variable with space:
bar = (int)foo;
For expressions, you can enclose expression into brackets to cast it:
bar = int(foo + blah * 4);
Using C++-styled casting is allowed when C-styled casting provides undefined or unacceptable behaviour.
Includes
For header files from project itself, always use includes with quotes, not with brackets. Also avoid using Windows-specific
\
backslash symbols to specify subpaths, only use normal/
slashes. Also please include full path to a header file and order includes alphabetically:Includes with brackets are only allowed when using external libraries:
Namespaces
Don't shorten
std
namespace types and methods by usingusing
directive. This is bad:auto x = vector<int>();
Leave them as is. This is good:auto x = std::vector<int>();
Comments
Use
//
-styled comments where possible. Only use/* */
style in case you are about to temporarily comment certain block for testing purposes or when writing a comment that will serve as the source for generated documentation.Branches and pull requests
Make sure that epic branches (tens or hundreds of files changed due to renames, namespace wrappings, etc) are focused on a single feature or task. Don't jump in to others epic branches with another round of your epic changes. It masks bugs and makes review process very cumbersome.
Avoid making new branches based on unapproved epic PRs which are in the process of review. It may render your work useless if parent epic branch is unapproved and scrapped.