Project

General

Profile

Actions

Best Practises

This page is a list of practises usually followed in the project's code base.

Character encoding (charset) of strings

C++ does not have a defined encoding (charset, character set) for its string types, most notably std::string. It is thus required to always be aware of what encoding a string is in. This project uses std::string as its main string type (as opposed to std::wstring) and ensures that the character encoding used in this strings is UTF-8. When interfacing with other software, conversion is applied as required. For example, if a programming library returns strings that are not in UTF-8, they are immediately converted to UTF-8 before storing them for later use. The Win32 API as the most prominent example uses UTF-16LE for example, thus interacting with it requires conversion from and to the UTF-8-encoded std::string instances used in this project.

Encoding conversion functions are provided in os/encoding.hpp.

Likewise, all files written by the programme are written in UTF-8, regardless of the platform. BOMs (byte order marks) are not to be used.

Type for filesystem pathes

Use C++17's std::filesystem::path for dealing with pathes on the filesystem. Use std::filesystem::u8path() to create an instance of std::filesystem::path from an std::string encoded in UTF-8.

Note that it is not required to use encoding conversion functions for creating a std::filesystem::path object from a string in filesystem native encoding, because std::filesystem::path's constructor is smart enough to realise this. As per its documentation, it:

  • Assumes its argument to be in native narrow encoding (typically UTF-8 on Linux) if it is of type char or std::string,
  • Assumes its argument to be in native wide encoding (typically UTF-16LE on Windows) if its is of type wchar_t or std::wstring.

Consequently, as long as you only pass char-based types to the std::filesystem::path constructor on Linux and wchar_t-based types to it on Windows, things should “just work”. Just do not pass wchar_t-based strings on Linux or char-based strings on Windows.

Inclusion of STL and other namespaces

In header files, do not include the std namespace or any other namespace, but write it out in full. This is to prevent unexpected namespace changes on #include.

In implementation files, do include the std namespace. Include other namespaces if it is useful and adds to the readability. Inclusion of namespaces should normally be done towards the beginning of a .cpp file, though it might be useful to only include a namespace in a single function. Use readability as the goal for decision.

The std::filesystem namespace is annoying to type even with std included. In .cpp files, abbreviate it as fs like this:

namespace fs = std::filesystem;
// Now you can access std::filesystem::path more
// easily as fs::path.

System-specific conditional compilation

When implementing system-specific code, use an #if/#elif/#else preprocessor block. The #else block should always give the compilation error message "unsupported system" (by using the #error preprocessor directive). This eases porting of the software to new platforms, because every system-specific code section will cause a compilation error on new platforms. The porting developer can then look at that and adapt the statements as necessary one by one.

Example:

#if defined(_WIN32)
// Windows-specific code
#elif defined(__unix__)
// Unix-specific code
#else
#error Unsupported system
#endif

There is a list of system-specific compiler macros available.

Reminding people to change something

GCC has a nice #pragma directive to print warnings. This can be used if something is only implemented prelimaryly to get on with other tasks. Each time the file is compiled, it will cause a warning. Since unknown #pragma directives are typically ignored by other compilers with a warning, this even works on non-gcc compilers.

Example:

#pragma GCC warning "Do not store data in /tmp, this is only for debugging"

That being said, use this trick sparingly.

Updated by quintus about 3 years ago · 3 revisions