Building C++ modules

Walter Bright newshound2 at digitalmars.com
Mon Aug 12 19:33:07 UTC 2019


On 8/8/2019 3:10 AM, Russel Winder wrote:
> So if C++ has this dependency problem why doesn't D, Go, or Rust?

Great question! The answer is it does, it's just that you don't notice it.

The trick, if you can call it that, is the same as how D can compile:

   int x = y;
   enum y = 3;

In other words, D can handle forward references, and even many cases of circular 
references. (C++, weirdly, can handle forward references in struct definitions, 
but nowhere else.)

D accomplishes this with 3 techniques:

1. Parsing is completely separate from semantic analysis. I.e. all code can be 
lexed/parsed in parallel, or in any order, without concern for dependencies.

2. Semantic analysis is lazy, i.e. it is done symbol-by-symbol on demand. In the 
above example, when y is encountered, the compiler goes "y is an enum, I'd 
better suspend the semantic analysis of x and go do the semantic analysis for y 
now".

3. D is able to partially semantically analyze things. This comes into play when 
two structs mutually refer to each other. It does this well enough that only 
rarely do "circular reference" errors come up that possibly could be resolved.

D processes imports by reading the file and doing a parse on them. Only "on 
demand" does it do semantic analysis on them. My original plan was to parse them 
and write a binary file, and then the import would simply and fastly load the 
binary file. But it turned out the code to load the binary file and turn it into 
an AST wasn't much better than simply parsing the source code, so I abandoned 
the binary file approach.

Another way D deals with the issue is you can manually prepare a "header" file 
for a module, a .di file. This makes a lot of sense for modules full of code 
that's irrelevant to the user, like the gc implementation.

-------------------

Some languages deal with this issue by disallowing circular imports entirely. 
Then the dependency graph is a simple acyclic graph, i.e. a tree. This method 
does have its attractions, as it forces the programmer to carefully decompose 
the project into properly encapsulated units. On the other hand, it makes it 
very difficult to interface smoothly with C and C++ files, which typically each 
just #include all the headers in the project.



More information about the Digitalmars-d mailing list