-
Notifications
You must be signed in to change notification settings - Fork 54
Cpp Style Guidelines
All C++ code that is added to QLever should obey to the following guidelines. The main goal of these guidelines are:
- Reducing noise in code reviews
- Making the QLever codebase more consistent
- Incorporating generally approved best practices for C++ programming by default.
Note: We are aware that a lot of old code in QLever currently does not (yet) follow these guidelines. You are highly encouraged to refactor this code such that it follows the guidelines.
The following guidelines are automatically checked by the CI via Github actions. Please make sure that your pull requests pass all the actions before marking it ready for review.
- QLever is implemented in ISO C++20
- QLever compiles without warnings with the
-Wall -Wextra
flags on the following compilers: G++11-13, clang++16-18, with no failures from the address, undefined behavior, and thread sanitizer. QLever also has to compile when using libc++ instead of libstdc++ for the versions of clang mentioned above. All of those constrains are checked by GitHub actions. - All language features that are supported by all of these compilers can be used in principle, as long as no other guidelines are violated.
- All C++ sources of QLever (in the directories
src
andtest
) must be formatted usingclang-format-16
. To automatically format your commits, consider installing the pre-commit hook provided with qlever (see the.pre-commit-config.yaml
in the main directory).
The compliance with the following guidelines has to be checked during code review. Please make sure that your code is compliant with the guidelines before having it reviewed. Clearly mark all deliberate violations of the guidelines with an explanation.
NOTE: Some of these guidelines are enforced the the static analysis tool Sonarcloud
which is automatically run on all pull requests.
- Local variables and all functions are
camelCase
, e.g.currentQuery
,parseQuery()
, classes and types arePascalCase
, e.g.SparqlParser
, non-static member variables have a trailing underscore, e.g.someMember_
.- Allowed exception: Using
snake_case
if we want a function or type to have the same name as its counterpart in the STL to allow overloading/polymorphism with the STL. Example:std::iterator_traits
among expects a member typevalue_type
(should beValueType
according to our rules) for containers. -
NOTE: Most member variables currently have a leading
_underscore
, please help us transition to trailingunderscore_
s. - TODO: What is the correct format for static member variables and (bad) global non-macro constants?
- Allowed exception: Using
- Names should consist of full words, e.g.
requestedVariable
instead ofreqVar
.- Exception 1: Counter variables for loops can be named
i
,j
, etc. - Exception 2: The following words abbreviations can and should be used in variable names:
ptr
(instead ofpointer
, e.g.requestPtr
),num
(instead ofnumberOf
, e.g.numRequests
), TODO isopt
foroptional
ok, we use them a lot? - Variables that count something should start with
num
, e.g.numCompletedRequests
instead ofcompletedRequests
orcompletedRequestCounter
- Boolean are prefixed with
is
, e.g.isDescending
.
- Exception 1: Counter variables for loops can be named
- All of the above should consist of complete English sentences.
- Comments, Docstrings and Exception messages end with a full stop
.
(seldom: a question mark?
). - Log messages do not end with a full stop
- Exception messages that might leak to the end user should include information about the concrete failure. Example:
// Compute the square root of `x`. Throws `std::out_of_range` on negative `x`.
double sqrt(double x) {
if (x < 0) {
// The messages includes the faulty value of `x`.
// No full stop after log.
LOG(WARN) << "Trying to take the square root of negative number " << x << std::endl;
throw std::out_of_range(absl::StrCat("Cannot compute the square root of negative value ", x, ".));
}
return std::sqrt(x);
}
- Use
class
for classes with private data members where member functions are used to control data access and to enforce invariants. - Use
struct
for simple data containers with public data members - TODO Make this more explicit, and add examples. What about "hybrids", should they be disencouraged in general?
Members in classes should be defined in the following order:
- Type aliases, enums, inner classes.
- Static constant members.
- (Static mutable members should be avoided in general).
- Non-static data members (should be private by default).
- Public constructors and destructor.
- Other public member functions.
- Private constructors and other private member functions.
// Not `<class A, class B>`
template <typename A, typename B>
void f(A a, B b);
- Constraints that only depend on the templated types are positioned right after their definition:
template<typename A, typename B> requires std::is_convertible_v<A, B>
void f(A a, B b) // The requires clause could technically also stand here, but this is discouraged by this guideline.
{ /* ... */ }
- Header guard (QLEVER_SOMETHING_FILENAME_H vs. #pragma once)