This page documents the details about the coding environment, including how to build the project, the recommended IDE (i.e. CLion), and code style. For more information on how to setup the environment based on this information, see the setup documentation.
Requirements:
- CMake 3.14+
- A generator (GNU make, ninja, ...) (usually included with CMake installation)
- A C++ compiler (GCC, Clang, MSVC, MinGW, ...)
Building is simple: invoke CMake and let it take care of the rest.
mkdir build && cd build
cmake ..
makeCMake will be used as the build system.
It has a bit of a learning curve, but once it's set up,
there will be little need for modification outside of copying other build
configurations to make new ones.
Cayden will be in charge of maintaining the CMakeLists.txt file,
which defines the build configurations.
One nice thing about CMake is that it works for virtually any operating system and compiler. One person can build with gcc and GNU Make on Linux, and another can compile with MSVC and ninja on Windows, without changing the build file at all.
Clang-Tidy will be used for code linting (i.e., diagnosing and fixing typical programming errors).
All checks are on by default. Some unimportant or irrelevant checks are turned off. This means that your code needs to be very clean. Most warnings will give you instructions on what needs to change; often, it will have automatic fixes that you can apply. If a warning is confusing, search it on Google; you'll be able to find a lot of good advice on Stack Overflow and elsewhere.
All code must pass Clang-Tidy's checks before committing. This will make a huge difference when it comes to code correctness and robustness. It won't save you from every segmentation fault, but it will save you from most.
It doesn't take long to get the hang of it and naturally start writing code that passes the checks.
Notice that Clang-Tidy has a hyphen and ClangFormat does not. This is not a typo.
ClangFormat is a handy tool that will help us to keep our code clean, readable, and consistent. It works in-place. It's incredibly easy to use.
The target style is described in detail later in this document.
All code must be formatted with ClangFormat before committing.
CLion is an IDE from JetBrains
that has a rich feature set and powerful integration with clangd.
When you open the Commander project in CLion for the first time,
it will automatically load the build configurations from the CMakeLists.txt
file.
When you go to run the project, you can select any of the build configurations
from a drop-down menu.
You can set breakpoints and run a debugger.
It has a built-in compiler, which is especially handy on Windows,
and it can integrate with WSL to build Linux binaries as well.
This includes integration with Clang-Tidy. Problematic code will be highlighted, and hovering over that code will show a warning with a detailed description of what needs to change. Often, you can automatically apply the needed changes, with no intervention outside of clicking on the button on the drop-down menu.
Furthermore, it integrates nicely with ClangFormat. A single keyboard shortcut will run ClangFormat and apply the needed style changes.
All files and folders should be lower-case. They should contain underscores instead of spaces.
All source files should be split into two parts:
the header file (with the suffix .hpp)
and the implementation file (with the suffix .cpp).
Exception: Templated functions require
that the implementation be directly in the header file,
or the compiler won't be able to compile an object file
because it won't know what the template parameters
are going to be.
All non-templated functions should go in the .cpp file.
The project repository will be organized according to the following structure:
/documentation: All documentation, except for the Doxygen documentation inside code files./source: All source files go inside here. They are organized by namespaces./source/lexer: Contains all lexer-related files. All classes and functions go in the namespaceLexer::./source/parser: Contains all parser-related files. All classes and functions go in the namespaceParser::./source/type_checker: Contains all type-checker-related files. All classes and functions go in the namespaceTypeChecker::.- (Etc.)
/tests: All unit tests go inside here. These don't need to be organized into directories; all tests for a particular module of the code should be in one file.
All files, classes, functions, and member variables (i.e., fields of a class) need to be documented. The documentation style that we will use is Doxygen, specifically the Javadoc style.
Documentation comments begin with /** and end with */.
Each line of a documentation comment must begin with *.
All files need to have a header comment with the following information:
@file: The filename.@brief: A brief description of what the file contains. Should be one line long. Every file must have this, even if the file has a@detailsfield.@details: (as necessary) A more in-depth description of the purpose of the file. Use this when@briefdoesn't describe as much as you'd like.
Example:
/**
* @file main.cpp
* @brief Contains the main method of the program.
*
*/
/**
* @brief Main program entry point.
*
* @return 0 on success.
*/
int main() {
return 0;
}All classes must be documented with the following information:
@brief: A brief description of what the class is. Should be one line long. Every class must have this, even if the class has a@detailsfield.@details: (as necessary) A more in-depth description of the purpose of the class. Use this when@briefdoesn't describe as much as you'd like.@tparam: (as necessary) One of these for each template argument. Made of the template argument's name, and then what the argument is.
All class members must be documented with the following information:
@brief: A brief description of what the member is. Should be one line long. Every member must have this, even if the member has a@detailsfield.@details: (as necessary) A more in-depth description of the purpose of the member, or what it represents. Use this when@briefdoesn't describe as much as you'd like.
Example:
/**
* @brief Represents a matrix of data.
*
* @tparam DataType The type of data stored in the matrix.
*/
template<typename DataType>
struct Matrix {
/**
* @brief The matrix's data.
*
*/
std::vector<std::vector<DataType>> values;
};All functions must be documented with the following information:
@brief: A brief description of what the function does. Should be one line long. Every function must have this, even if the function has a@detailsfield.@details: (as necessary) A more in-depth description of the purpose of the function. Use this when@briefdoesn't describe as much as you'd like.@param: (as necessary) One of these for each function argument. Made of the function argument's name, and then what the argument is.@tparam: (as necessary) One of these for each template argument. Made of the template argument's name, and then what the argument is.@return: (as necessary) The returned value of the function.
Example:
/**
* @brief Triples the given number.
*
* @param number The number to triple.
* @return Three times the given number.
*/
int triple(int number);The following case definitions come from Clang-Tidy documentation:
lower_caseUPPER_CASEcamelBackCamelCasecamel_Snake_BackCamel_Snake_CaseaNy_CasELeading_upper_snake_case
Here are the naming conventions for each symbol type:
-
Functions:
camelBack- At least three letters.
- Should be named after what the function does.
- Up to three words are allowed.
-
classes andstructs:-
CamelCase -
At least three letters.
-
Should be named after what the class is.
-
Up to three words are allowed, not including its super class if it's a derived type.
For example,
WordWordWordExprwould be allowed if it's a derived type of a class namedExpr.
-
-
Members of
classes andstructs:-
camelBack -
At least three letters.
-
Includes both member functions and member variables.
-
Also includes static functions of a
classorstruct. -
Should be named after what the member is.
-
Up to three words are allowed.
-
Should be
privateunless one of the following is true:- It's constant.
- It makes sense for outsiders to directly modify it.
- It needs to be used by a derived class
(in which case it should be
protected).
-
If the member is private or protected, it must have the prefix
_. -
Example:
class Vehicle { private: // Private: nothing external to the class needs access. Vin _vin; // Private: even though it needs to be read outside // of the class, it doesn't make sense for // external environments to modify it. // Therefore, a getter is provided instead. unsigned int odometerMileage; protected: // Protected: subclasses `Car` and `Plane` // need to know the fuel type. FuelType _fuelType; public: // Public: anyone can construct the vehicle. Vehicle(Vin vin); // Public: anyone can get the mileage. // It doesn't make sense for anyone to modify it, // so it has a getter method instead of being // a public variable. unsigned int getMileage(); // Public: anyone can rename their vehicle // whenever they want to. std::string name; };
-
-
unions:- Don't use
unions.
- Don't use
-
Variables and parameters:
camelBack(just likeclass/structmembers)- At least three letters.
- At most three words.
-
enums:-
CamelCase(just likeclasses) -
At least three letters.
-
At most three words.
-
Items in an
enumare inUPPER_CASE. -
Example:
enum FuelType { GASOLINE, DIESEL, JET_FUEL, ELECTRIC };
-
-
namespaces:-
CamelCase(just likeclasses) -
At least three letters.
-
At most three words.
-
Example:
namespace MiscFunctions { void doFoo() {} void doBar() {} }
-
-
Macros:
- Don't use macros.
-
Template parameters:
-
CamelCase -
At least three letters.
-
At most three words.
-
Types must have the suffix
Type. -
Values must have the suffix
Val. -
Example:
template <typename DataType, int SizeVal> struct ArrayWrapper { std::array<DataType, SizeVal> _backingArray; };
-
-
Type Aliases:
-
CamelCase -
At least three letters.
-
At most three words.
-
A type alias is when you use the
usingkeyword to define a new alias to a type. -
Never use type aliases outside of a class or function.
-
Never use the
typedefkeyword. -
Example:
using TokenVec = std::vector<Token>;
-
It's far too lengthy to write out in Markdown.
The highlights are below.
For details, see the .clang-format file.
- Indentation is 4 spaces.
public/private/protectedkeywords are lined up evenly with theclass/structthat contains them.- Short
if/loop statements are allowed on a single line. - The maximum length of a line is 120 characters.
- Pointer asterisks are aligned to the left, next to the data type
(that is,
int* variableinstead ofint *variable).