Header-Only Library - pragma(root)

Petar Petar
Tue Jun 16 07:06:16 UTC 2020


On Sunday, 14 June 2020 at 16:05:39 UTC, Andrei Alexandrescu 
wrote:
> On 6/14/20 11:36 AM, Adam D. Ruppe wrote:
>> On Sunday, 14 June 2020 at 10:24:33 UTC, Walter Bright wrote:
>>> It would probably be better as the implementer, as a 
>>> header-only library has to be designed for it.
>> 
>> That's not true. -i just works with almost any D code. It is 
>> enormously useful exactly the way it is.
>
> What code wouldn't it work with? Would be good to document 
> those. "How to write a header-only D library". Blog post!

The '-i' flag is the symmetric opposite of '-I'. '-I' expects 
either header-only libraries, or separate compilation, while '-i' 
expects neither. What Walter is suggesting (marking modules with 
'pragma (root);' to make them header-only mainly serves the '-I' 
flag. The only advantage 'pragma (root);' may have to '-i' is a 
compile-time guarantee that modules intended to be header-only 
actually don't impose any link-time dependencies.

The main use case of the `-i` flag is to compile (not just 
import) modules. However the killer feature of '-i' is that while 
'-I' a is blunt tool that just adds all modules under a dir to 
the import path, '-i' leverages D's import system and lazily 
compiles only the modules that are imported starting from the 
root set of modules.

For example, giving the following project structure:

     project/
     ├── lib1
     │   ├── mod1.d
     │   ├── ...
     │   └── modn.d
     ├── lib2
     │   ├── mod1.d
     │   ├── ...
     │   └── modn.d
     └── main.d


Previously, in order to compile it, you could do the following:

     find lib1/ lib2/ -type f -name '*.d' | xargs dmd -betterC 
main.d

However, this has the disadvantage of compiling stuff that main.d 
doesn't need. Say main.d was this:

     extern (C) int main(int argc, const char** argv)
     {
         import lib1.mod1 : fun1a;
         return fun1a(argc, argc + 1);
     }

The only function that ever needs to be compiled is fun1a. 
However the command above will end up compiling the whole world 
(every module under lib1/).

With the '-i' flag we can compile main.d like so:

     dmd -betterC -i=lib1 -i=lib2 main.d

Now, set of modules that would be compiled equals precisely the 
set of modules imported from main.d - in this case only 
lib1/mod1.d.

The only deficiency currently is that it doesn't take advantage 
of selective imports - each imported module is compiled as a 
whole, even if you only need to compile the imported symbols. 
This problem is not just an implementation deficiency though, as 
whether something is compiled and whether it exists is somewhat 
conflated and has impact on D's meta programming (DbI and all 
that). I think the best way to address this issue is at the root 
- we need to define precise semantics over what lazy compilation 
means. I have been meaning to write a DIP that addresses this 
issue for a while, based on the idea of an @export attribute 
(orthogonal to public/protected/package/private access modifiers) 
and @export inference. You can see a sketch of what I have in 
mind in these posts:

https://forum.dlang.org/post/xbllqrpvflazfpowizwj@forum.dlang.org
https://forum.dlang.org/post/nvvgrdlucajshjngdjlj@forum.dlang.org



More information about the Digitalmars-d mailing list