Welcome to the rAthena Code Guidelines. This is here to help all developers in creating safe, maintainable, easily readable and understandable code.
These guidelines are inspired by the CppCoreGuidelines. Some of the examples below maybe taken directly from there. If you need more information that may not be listed here, please be sure to check out CppCoreGuidelines. If your questions are still unanswered after consulting this or CppCoreGuidelines, consider consulting the rAthena forum, rAthena's Discord, or add your concerns directly to a pull request.
Commits should be on point, documented and as small as possible.
Check for removed or added blank lines on each commit.
std::vector
requires to be shrinked after using std::remove
. To avoid forgetting about proper shrinking after removing an element the removing should always be bound to a call to earse
.
If using a std::list
one should rely on std::list::remove
instead of std::remove
.
vec.erase(std::remove_if(vec.begin(), vec.end(), [](int a) {return a % 2 == 0;}), vec.end()); // good
Check for std::remove
on datatype std::vector
without being surrounded by an erase
.
Doing so avoids verbosity and eliminates some opportunities for mistakes. Helps make style consistent and conventional.
By definition, a condition in an if
-statement, while
-statement, or a for
-statement selects between true
and false
.
A numeric value is compared to 0
and a pointer value to nullptr
.
// These all mean "if `p` is not `nullptr`"
if (p) { ... } // good
if (p != 0) { ... } // redundant `!=0`; bad: don't use 0 for pointers
if (p != nullptr) { ... } // redundant `!=nullptr`, not recommended
Often, if (p)
is read as "if p
is valid" which is a direct expression of the programmers intent,
whereas if (p != nullptr)
would be a long-winded workaround.
Explicit comparison of an integer to 0
is in general not redundant.
The reason is that (as opposed to pointers and Booleans) an integer often has more than two reasonable values.
Furthermore 0
(zero) is often used to indicate success.
Consequently, it is best to be specific about the comparison.
void f(int i)
{
if (i) // suspect
// ...
if (i == success) // possibly better
// ...
}
Always remember that an integer can have more than two values.
Easy, just check for redundant use of !=
and ==
in conditions.
Unnamed constants embedded in expressions are easily overlooked and often hard to understand:
for (int m = 1; m <= 12; ++m) // don't: magic constant 12
cout << month[m] << '\n';
No, we don't all know that there are 12 months, numbered 1..12, in a year. Better:
// months are indexed 1..12
constexpr int first_month = 1;
constexpr int last_month = 12;
for (int m = first_month; m <= last_month; ++m) // better
cout << month[m] << '\n';
Better still, don't expose constants:
for (auto m : month)
cout << m << '\n';
Flag literals in code. Give a pass to 0
, 1
, nullptr
, \n
, ""
, and others on a positive list.
- Simple repetition is tedious and error-prone.
- When you use
auto
, the name of the declared entity is in a fixed position in the declaration, increasing readability. - In a template function declaration the return type can be a member type.
Consider:
auto p = v.begin(); // vector<int>::iterator
auto h = t.future();
auto q = make_unique<int[]>(s);
auto f = [](int x){ return x + 10; };
In each case, we save writing a longish, hard-to-remember type that the compiler already knows but a programmer could get wrong.
Avoid 'auto' when declaring a variable which is directly iniated with a return value of a function call which does not indicate the type. This is only required when using the fucnction call the first time within a method.
Also avoid auto
for initializer lists and in cases where you know exactly which type you want and where an initializer might require conversion.
auto lst = { 1, 2, 3 }; // lst is an initializer list
auto x{1}; // x is an int (in C++17; initializer_list in C++11)
void some_function()
{
my_type a = my_function();
auto b = my_function(); // here we already know the return type of 'my_function'
...
}
Flag redundant repetition of type names in a declaration.
--style=kr
--indent=tab=4
--indent-classes
--indent-preprocessor --convert-tabs
--indent-col1-comments
--indent-labels
--indent-cases
--indent-switches
When a header is built of multiple lines, the lines will be aligned with the paren on the preceding line.
--min-conditional-indent=0
--unpad-paren
--pad-header
--pad-oper
--keep-one-line-statements
--align-pointer=name
--align-reference=name
--lineend=linux
--suffix=none
--recursive