Skip to content

Latest commit

 

History

History
139 lines (105 loc) · 4.28 KB

guidelines.md

File metadata and controls

139 lines (105 loc) · 4.28 KB

Guidelines for using modules

The benefits of using modules are many, and very well-documented here: https://clang.llvm.org/docs/Modules.html. The only question that remains, besides when we will get reliable compilers, is how to best structure programs with modules.

I must add that there is no correct way of doing this, and as always with C++, we have different options. Modules were only accepted because they are a strictly non-breaking change, and because they can be adopted gradually. If this was not the case, the proposal never would have been accepted in the first place.

General rules and suggestions

With that out of the way, these are some general rules:

  • Each translation unit must export a module. The exception is for the main file. Why? Because without headers how else can we access the definitions in our code without exporting them?
  • Don't use headers together with modules, except for as a transition period away from headers.
  • Don't be afraid of heavily templated code inside modules. A module is compiled into a BMI (binary module interface), which is a binary form of an abstract syntax tree; so the compiler can generate the template instantiations on request by simply reading the binary files. HUUGE bonus! No more header-only libraries.
  • Libraries can no longer depend upon header files containing documentation, and must use alternative means. The best bet is auto-generated documentation through doxygen or similar tools. I would suggest publishing a pdf, but html is also a viable option.
  • Libraries can be built first and then distributed - we do this already - but now we will just no longer distribute the headers. This is likely a lot safer.
  • Include directories will not matter anymore, only module mappers. So creating reliable build tools is going to be a lot easier (let's get rid of CMake in the process, shall we?).
  • While I will not suggest how to structure open-source projects or their distributions, nor the architecture of build tools, I will say this: Module mapping files is going to be the way to go. Whether these should be distributed as part of the source code, or generated by the client's compiler, is unknown.

Code structure

NOTE: This is subject to change!

We have roughly two choices for the structure of a code base, following the rules described above (at least to my knowledge - correct me if I'm wrong!). These two choices are:

  1. Using submodules / module partitions
  2. Using module implementation files

I will briefly showcase code for both of them:

Submodule

// submodule
export module std.math : pwr;
import std.cstdint;

/*
 * Documentation string here.
 */
export constexpr bool is_pwr2(uint32_t val) {
    return uint32_t{0} == ((val - uint32_t{1}) & val);
}
// module interface unit / module
export module std.math;

export import :pwr;
// application
import <iostream>; // header unit, ie. not yet modularized! (example)
import std.math;

int main()
{
    std::cout << std::boolalpha << "is_pwr2(16): " << is_pwr2(16) << '\n';
    return 0;
}

Note how the exported symbol is_pwr2 is directly accessible inside the application, which in this case is because it has not been placed inside a namespace.

Module implementation unit

// module interface unit
export module std.math;
import std.cstdint;

/*
 * Documentation string here.
 */
export constexpr bool is_pwr2(uint32_t val);
// module implementation unit
module std.math;
import std.cstdint; // I am unsire if this will be needed!

export constexpr bool is_pwr2(uint32_t val) {
    return uint32_t{0} == ((val - uint32_t{1}) & val);
}
// application
import std.iostream; // module unit, ie. has been modularized! (example)
import std.math;

int main()
{
    std::cout << std::boolalpha << "is_pwr2(16): " << is_pwr2(16) << '\n';
    return 0;
}

Which is best?

I think this is too early to say. I'm already assuming a lot by writing this code. The 2) approach with module implementation units is the most similar to the current header/source file approach, so that will most likely end of being the most widely adopted. In my own opinion the submodule approach is more clean, but module exports from submodules is a matter of debate. We will see how this develops.