Some general rules to write code: Try to follow the same style/format of the file that you are editing (naming, indentation, etc.) or the style of the module (some submodules, created by us, or by third-parties, have their own style).
There is a .clang-format file available but we are not using it at the moment, probably we should start using some clang-format-diff.py for patches, but this wasn't yet adopted in the development process.
There is a .clang-tidy file used in the GitHub actions executed on each PR. These rules are adopted progressively on patches because are only executed in the diff, and if some rule is violated a comment by aseprite-bot is made.
Basic statements:
void global_function(int arg1,
const int arg2, // You can use "const" preferably
const int arg3, ...)
{
int value;
const int constValue = 0;
// We prefer to use "var = (condition ? ...: ...)" instead of
// "var = condition ? ...: ...;" to make clear about the
// ternary operator limits.
int conditionalValue1 = (condition ? 1: 2);
int conditionalValue2 = (condition ? longVarName:
otherLongVarName);
// If a condition will return, we prefer the "return"
// statement in its own line to avoid missing the "return"
// keyword when we read code.
if (condition)
return;
// You can use braces {} if the condition has multiple lines
// or the if-body has multiple lines.
if (condition1 ||
condition2) {
return;
}
if (condition) {
...
...
...
}
// We prefer to avoid whitespaces between "var=initial_value"
// or "var<limit" to see better the "; " separation. Anyway it
// can depend on the specific condition/case, etc.
for (int i=0; i<10; ++i) {
...
// Same case as in if-return.
if (condition)
break;
...
}
while (condition) {
...
}
do {
...
} while (condition);
switch (condition) {
case 1:
...
break;
case 2: {
int varInsideCase;
...
break;
}
default:
break;
}
}
Define namespaces with lower case:
namespace app {
...
} // namespace app
Define classes with CapitalCase
and member functions with camelCase
:
class ClassName {
public:
ClassName()
: m_memberVarA(1),
m_memberVarB(2),
m_memberVarC(3) {
...
}
virtual ~ClassName();
// We can return in the same line for getter-like functions
int memberVar() const { return m_memberVar; }
void setMemberVar();
protected:
virtual void onEvent1() { } // Do nothing functions can be defined as "{ }"
virtual void onEvent2() = 0;
private:
int m_memberVarA;
int m_memberVarB;
int m_memberVarC;
int m_memberVarD = 4; // We can initialize variables here too
};
class Special : public ClassName {
public:
Special();
protected:
void onEvent2() override { // No need to repeat virtual in overridden methods
...
}
};
We use the const-west notation:
There is a problem with clang-tidy
that will make comments using
East const notation: #4361
We are using C++17 standard. Some things cannot be used because we're targetting macOS 10.9, some notes are added about this:
- Use
nullptr
instead ofNULL
macro - Use
auto
/auto*
for complex types/pointers, iterators, or when the variable type is obvious (e.g.auto* s = new Sprite;
) - Use range-based for loops (
for (const auto& item : values) { ... }
) - Use template alias (
template<typename T> alias = orig<T>;
) - Use generic lambda functions
- Use
std::shared_ptr
,std::unique_ptr
, orbase::Ref
, but generally we'd prefer value semantics instead of smart pointers - Use
std::min
/std::max
/std::clamp
- Use
std::optional
but taking care of some limitations from macOS 10.9: - Use
std::variant
but taking care of some limitations from macOS 10.9: - Use
std::any
but taking care of some limitations from macOS 10.9:- Use
T* p = std::any_cast<T>(&value)
instead ofT v = std::any_cast<T>(value)
(example)
- Use
- Use
static constexpr T v = ...;
- You can use
<atomic>
,<thread>
,<mutex>
, and<condition_variable>
- Prefer
using T = ...;
instead oftypedef ... T
- Use
[[fallthrough]]
if needed - Use
= {}
only to specify a default argument value of an user-defined type in a function declaration, e.g.void func(const std::string& s = {}) { ... }
. In other cases (e.g. a member variable of an user-defined type) it's not required or we prefer to use the explicit value for built-in types (int m_var = 0;
). - We use gcc 9.2 or clang 9.0 on Linux, so check the features available in https://en.cppreference.com/w/cpp/compiler_support