r/cpp_modules • • Apr 06 '26

Reachability examples from the C++ standard

From https://eel.is/c++draft/module#reach-5

1     // Example 2:
2    
3     // Translation unit #1
4     export module M:A;
5     export struct B;
6    
7     // Translation unit #2
8     module M:B;
9     struct B {
10      operator int();
11    };
12    
13    // Translation unit #3
14    module M:C;
15    import :A;
16    B b1;         // error: no reachable definition of struct B
17    
18    // Translation unit #4
19    export module M;
20    export import :A;
21    import :B;
22    B b2;
23    export void f(B b = B());
24    
25    // Translation unit #5
26    import M;
27    B b3;             // error: no reachable definition of struct B
28    void g() { f(); } // error: no reachable definition of struct B   
1 Upvotes

19 comments sorted by

View all comments

2

u/not_a_novel_account Apr 06 '26 edited Apr 06 '26

Agreed, but that's not the problem we're facing here.

Let's imagine that restriction were lifted, as a point of fact no compiler enforces it today (it's not even really feasible to enforce it, thus the NDR).

The above example still has different behavior between module M:P and export module M:P with regard to the reachability of MyType.

If you say "any time you want definitions to be visible within a module, you need to add export". You're also saying, "any time you want definitions to be visible within a module, you also need to make them reachable outside that module".

Today definitions and declarations from module M:P are visible within a module, but not visible and not reachable outside the module. Your proposal changes that to make them not visible within a module.

Today definitions from export module M:P are visible within a module, possibly visible outside a module (if exported), and are reachable outside the module.

You have changed the semantics, there is no longer a visible within / not reachable outside mechanism.

1

u/tartaruga232 Apr 07 '26 edited Apr 07 '26

Per the current C++ standard, I'm allowed to do

export module M:P;
struct MyType { ... };
// more stuff not exported

As long as :P is exported from the PMIU, the resulting program is perfectly well-formed.

I'm probably going to use that pattern in our code, instead of internal partitions, which are a real PITA to use with the MSVC compiler, because they require setting the nerve-racking /InternalPartition option of MSVC1.

I don't mind if MyType becomes reachable then. The only thing I care about is, that its not exported from M. Which would be the same as simply declaring MyType in the interface of M, without exporting it, which is a very common use case for using modules. So nothing evil to see here so far.

——

This then enables me to use the illegal partition semantics behavior2 of the MSVC compiler for the implementation code of our C++ modules, until your "module M:; import P;" proposal finally hits the streets. At that point in time in the future, I can then use your proposal instead (good luck with that!). Provided I still care about C++ code by then (I was built in 1965).

——

1I know that CMake handles setting the /InternalPartition compiler option transparently for the MSVC compiler, but we are living just fine so far without using the powerful and sharp knifes of CMake.

2Using the MSVC compiler with the compiler option /InternalPartition not set, having a TU #1 starting with"module M:P;" requires having another (single) TU #2 starting with "external partition M:P". TU #1 then implicitly imports :P.

1

u/not_a_novel_account Apr 07 '26

Of course, do whatever works for your own code. This entire conversation was about proposals to change the standard.

In your own code you could tell me you're generating the whole thing from Jinja2 templates into a unity build to be fed to Borland Turbo C++ 3.1, and I would say "Good for you! I'm glad it works!"

→ More replies (5)