Cpp Style Guidelines - ad-freiburg/qlever GitHub Wiki

C++ Style Guidelines for QLever

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.

Programmatically enforced 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.

C++ Standard

  • QLever is implemented in ISO C++20
  • QLever compiles without warnings with the -Wall -Wextra flags on the following compilers: G++11, G++12, clang++13
  • All language features that are supported by all of these compilers can be used in principle, as long as no other guidelines are violated.
  • TODO Include sanitizers into the CI pipeline.
  • TODO Make a CI using libstdc++ and talk about which standard libraries should be supported.

Code Formatting

  • All C++ sources of QLever (in the directories src and test) must be formatted using clang-format-11
  • TODO Update clang-format, version 11 does not really understand C++20 concepts.

Manually enforced guidelines

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.

Naming of variables and types

  • Local variables and all functions are camelCase, e.g. currentQuery, parseQuery(), classes and types are PascalCase, 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 type value_type (should be ValueType according to our rules) for containers.
    • NOTE: Most member variables currently have a leading _underscore, please help us transition to trailing underscore_s.
    • TODO: What is the correct format for static member variables and (bad) global non-macro constants?
  • Names should consist of full words, e.g. requestedVariable instead of reqVar.
    • 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 of pointer, e.g. requestPtr), num (instead of numberOf, e.g. numRequests), TODO is opt for optional ok, we use them a lot?
    • Variables that count something should start with num, e.g. numCompletedRequests instead of completedRequests or completedRequestCounter
    • Boolean are prefixed with is, e.g. isDescending.

Style of Comments/Docstring/Exception messages/Log messages.

  • 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);
}

Classes and Structs

class vs `struct

  • 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?

Order of elements in classes

Members in classes should be defined in the following order:

  1. Type aliases, enums, inner classes.
  2. Static constant members.
  3. (Static mutable members should be avoided in general).
  4. Non-static data members (should be private by default).
  5. Public constructors and destructor.
  6. Other public member functions.
  7. Private constructors and other private member functions.

Templates and Generic Programming

Use typename instead of class when declaring templates

// Not `<class A, class B>`
template <typename A, typename B>
void f(A a, B b);

Positioning of requires- constraints.

  • 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.   
{ /* ... */ }

TODOS

  • Header guard (QLEVER_SOMETHING_FILENAME_H vs. #pragma once)
⚠️ **GitHub.com Fallback** ⚠️