This is the style guide for the PlayRho library. This guide is meant for contributors who want to contribute pull requests for code changes.
For general information on contributing, see the Contributing document.
For anything not described herein, please follow the C++ Core Guidelines.
Write code to the C++17 standard. Try to document any changes recommended for C++20.
- Just because a guideline is listed in this document, does not mean that it's a good guideline (or a bad one for that matter).
- Some of the guidelines described herein are established primarily by precedence from the history of this code.
Prefer to use auto
unless it makes it harder for you to do what you're trying to do. Don't remove the use of auto
from code unless it's code you've contributed.
Generally speaking, don't change the formatting of pre-existing code.
Indent code using 4-spaces for every level of code indentation desired. Tabs may not be used for this.
Don't add any levels of indentation within namespaces. For example, use the following style for namespaces and any declarations within them:
namespace playrho {
struct MyData;
namespace d2 {
int MyNewFunctionForD2(const MyData& arg);
} // namespace d2
} // namespace playrho
Don't indent class access specifiers (public:
, protected:
, and private:
).
I.e., use the following formatting style:
class MyNewClass {
public:
MyNewClass() = default;
// ... whatever
private:
int m_data;
};
This speaks to when to wrap source code to the next line. Use a column width of 100.
For namespaces, if-statements, switch-statements, case-labels, and class definitions,
wrap to the next line after the {
character.
For function definitions, prefer to wrap to the next line before the {
character.
Here's what this looks like:
namespace Test { // ClangFormat: "AfterNamespace: false"
class MyClass { // ClangFormat: "AfterClass: true"
public:
MyClass(int foo): m_data{foo}
{
// Nothing done here in function block.
}
private:
int m_data;
};
struct MyStruct { // ClangFormat: "AfterStruct: false"
int wow;
};
void DoSomething(bool someBool, int val) // ClangFormat: "AfterFunction: true"
{
if (someBool) {
myFunction();
} // ClangFormat: "BeforeElse: true"
else {
someOtherFunction();
}
switch (val) {
case 0: { // ClangFormat: "AfterCaseLabel: false"
int foo = 2;
doIt(foo);
break;
}
case 1:
break;
default:
break;
}
}
} // namespace Test
Make operator overload definitions look like function definitions. Don't put any spaces between the specification of the operator and its parameters:
constexpr Acceleration operator-(const Acceleration& lhs, const Acceleration& rhs)
{
return Acceleration{lhs.linear - rhs.linear, lhs.angular - rhs.angular};
}
constexpr Acceleration Subtract(const Acceleration& lhs, const Acceleration& rhs)
{
return Acceleration{lhs.linear - rhs.linear, lhs.angular - rhs.angular};
}
Here are some links to available tools to help with formatting. Unfortunately they don't seem perfectly up to the job by themselves so it's still necessary to be aware of these guidelines:
.editorconfig
: Configuration file for EditorConfig..clang-format
: Configuration file for ClangFormat. From a POSIX shell, ClangFormat can be run as:clang-format -i Library/source/PlayRho/**/*.cpp Library/include/PlayRho/**/*.hpp
.
Compose globally accessible definitions with full words that explain them. Definitions used in smaller contexts can be increasingly tersely named and abbreviated.
Both class
and struct
class definition names are to begin with a capitalized letter.
Unless the definition is meant to be replaceable with a Standard Library definition, names are to be camel-case separated. Definitions meant to be replaceable with Standard Library definitions meanwhile are to mimic the naming style of the definitions they're meant to be interchangeable with.
This — in a sense — keeps the include guard macros in their own namespace.
So for example a macro called "one" would be spelled like ONE
.
Thus a macro called "this is a macro" would appear as: THIS_IS_A_MACRO
. Or an include guard might be written as: PLAYRHO_BODY_TYPE_HPP
.
Capitalize the first letter of every function unless the function is intended to be a candidate for replacement by a Standard Library function.
Start the names of all functions — whether non-static member functions, static member functions, non-member functions, etc. — with the action that they perform. For example: Get*
, Set*
, Find*
, Transform*
, Create*
, Destroy*
, Insert*
, Erase*
For example: IsSleepingAllowed
, NeedsFiltering
, or HasValidToi
.
Code missing Doxygen documentation won't pass all of the Pull Request checks and is unlikely to be accepted.
This is especially the case for new classes. Lots of existing class
-class definitions don't document any invariants but I'd like them to. Make it a habit to document invariants in a class definition or use the struct
-class if the class is not supposed to have any invariants.
Declare classes that have no invariants using the struct
class-key.
Classes that have one or more invariants are to be declared using the class
class-key.
PlayRho library classes that are declared using the class
class-key should have one or more @invariant
Doxygen commands in their class comments.