![]() |
hivemind 1.0.0
|
This document serves as both a guide to the developers and maintaners of Hivemind, and an explanation of code design and architecture choices for other actors who needs to or wants to look at the source code of Hivemind.
Compiler warnings are useful hints to improve code. Generally, if the compiler issues a warning, adjust the code to suppress this.
Generally, developers of Hivemind should adhere to an object oriented programming (OOP) style in Hivemind's codebase, especially when implementing top-level interfaces of major components. It is important to note, however, that it is encouraged avoid OOP when moving into deeper implementation details. Being too strict on an object oriented approach often leads to unnecessary complications.
The use of assertions in code is encouraged. Assertions are not only very useful for verifying states and data, they also document expected behaviour for other developers looking at the codebase.
The root of the project contains a .clang-format
configuration file. This should be used to format all source files to maintain consistent formatting throughout the codebase, and to prevent unnecessary changes to untouched code cluttering the version control history.
The control comments clang-format off
and clang-format on
can be used for specific code blocks where retaining a specific format is preferable, but this should be used sparingly.
Comments are useful for documenting source code and provides improved readability and maintainability. Comments should provide an explanation of the code's purpose rather than an explanation of how it is done; the code documents the process itself.
Proper documentation of class definitions are expected. The purpose of a class and how it works should be explained with Doxygen comments to keep the docs as up to date as possible. These comments should be located in the header file of the class.
Generally, prefer C++-style comments rather than C-style. For normal comments, this means using //
, and using ///
for doxygen comments.
Prefer using triple-slash (///
) comments for doxygen documentation. Prefer using backslash (\
) over at (@
) for doxygen tags such as param
, file
and returns
.
Prefer:
Avoid:
Prefer spaces over tabs. There are valid arguments for both the use of spaces and tabs, but a mixture of both of them should not be used. Therefore, a standard of using spaces is preferred in this project. Clang Format should ensure that tabs are converted to spaces.
If a maximum column width is going to be defined, using a standard width makes sense. Therefore, a maximum column width of 80 has been set. This should be enforced by Clang Format. There are exceptions to this rule, and these are generally related to comments or strings that have a specific format that makes more sense than the one enforced by Clang Format. In these cases, the use of clang-format off
and clang-format on
are allowed.
For now, all the source code of Hivemind is written in C++. When we start to use ROS as part of the system, there are plans to experiment with the feasibility of implementing modules in Python.
Hivemind uses C++17.
Although C++20 is both feature-complete and mostly supported by the major compilers, our preferred build tool-chain, CMake, only supports some features through the use of experimental flags. As such, we currently view C++20 as not fully supported and not a viable option. This may change in the future.
Generally prefer to use the data structures, algorithms and functions available in the C++ standard library rather than implementing custom solutions. The standard library is mature, robust, extensively tested and highly optimized.
Maintaining a uniform naming convention throughout the codebase helps increase readability. As such, we use a well-defined naming convention that must be adhered to when writing C++ code.
PascalCase
.SCREAMING_SNAKE_CASE
.camelCase
.PascalCase
, but private attributes should be pre-fixed with m_
.In C++, classes and structs are essentially the same thing and they can generally be used interchangeably, given that you take access specifiers into account.
We define a semantic difference in our codebase: Classes are to be used for more complex data objects with attributes and members of both private and public access. Structs are to be used for more simple data objects where all attributes are public.
When defining classes, the attributes and members of different access specifiers should be defined in the following order:
The rationale for this is that if someone looks at the header file of a class to see what attributes and members they can access, they will not care about private members and implementation details. They want to know which members and attributes they can actually use.
At the top of the file, below the header guard in the case of header files, should the includes required by the file be listed. They should be ordered as follows:
The main module header only applies to .cpp files with a header files whose classes and functions it implements. The project headers refer to other header files part of the Hivemind project that the file depends on. The library headers refer to dependant header files from external libraries such as QT headers. Finally, system headers generally refer to headers that are part of the C standard library and C++ standard library.
The main module header and project files should be include with the double-quote style, and library files and system files should be included with the angled brackets style.
Header files should only include other header files that it strictly needs. If the include can be moved to the corresponding .cpp file instead, it should.
Example:
Header files should be protected using #pragma once
rather than traditional header guards. #pragma once
is technically not standard but it is widely supported and provides several advantages including less code, less risk of name clashing and potentially improved compilation speed.
Prefer:
Avoid:
The use of the auto
keyword should be reserved for cases where the type can be deduced from the context. An example of this is when casting a variable to another type. The cast operation will specify the type, so it is easily deduces.
Example:
The auto
keyword can sometimes also be used to increase readability of the codebase. Examples of this is when using the chrono library in the std namespace. It is extremely verbose, and using auto can help with readability.
Example:
RAII, or Resource Acquisition Is Initialization, is a C++ programming technique which ensures that the life-cycle of a limited resource, such as heap memory or a locked mutex, is bound to the life-cycle of an object, meaning that the resource is accessible and usable as long as the object lives, and that it is automatically freed when the object is destroyed.
RAII is generally implemented by acquiring the needed resource in the constructor of a class, and freed in the destructor.
Prefer to use RAII where applicable.