Skip to content

Commit e25fb9a

Browse files
committed
WINRT_IMPL_SKIP_INCLUDES is finally gone
1 parent 4ff8c7b commit e25fb9a

File tree

6 files changed

+115
-89
lines changed

6 files changed

+115
-89
lines changed

.github/instructions/modules.instructions.md

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ targets (or manually for non-NuGet projects).
3131

3232
### .g.h (component base template)
3333
- `#ifdef WINRT_MODULE`: emits `#include "winrt/base_macros.h"` for macros,
34-
conditional `import std;`, `import winrt;`, locally sets `WINRT_IMPL_SKIP_INCLUDES`
35-
- Always includes component's own headers (platform SDK deps skipped by the
36-
local `WINRT_IMPL_SKIP_INCLUDES`, but cross-namespace component deps are NOT)
34+
conditional `import std;`, `import winrt;`
35+
- Always includes component's own headers. Platform deps are skipped by
36+
per-namespace `WINRT_MODULE_NS_*` guards; component cross-namespace deps
37+
are included normally.
3738

3839
### .g.cpp (factory + optimized constructors)
3940
- Always emits the `winrt_make_*` factory function
@@ -42,42 +43,47 @@ targets (or manually for non-NuGet projects).
4243

4344
## Macro Scoping
4445

45-
Three macros with distinct scopes control module behavior:
46+
Two project-level macros and one set of per-namespace macros control behavior:
4647

4748
- `WINRT_BUILD_MODULE` — Defined by cppwinrt inside winrt.ixx's global module
48-
fragment. Controls base.h skip in version assert. Also `#undef`s
49-
`WINRT_IMPL_SKIP_INCLUDES` so cross-namespace deps work inside the ixx.
49+
fragment. Controls base.h skip in version assert. Does NOT include
50+
`winrt_module_namespaces.h` (inside the ixx, all deps must resolve).
5051
Never set by users or NuGet targets.
5152
- `WINRT_MODULE` — Defined project-wide by NuGet targets. Controls .g.h/.g.cpp
52-
behavior AND version assert base.h skip. Does NOT suppress cross-namespace
53-
deps between component namespaces.
54-
- `WINRT_IMPL_SKIP_INCLUDES` — Set locally inside .g.h files only (under
55-
`#ifdef WINRT_MODULE`). Suppresses cross-namespace platform SDK deps that
56-
are already in the module. NOT defined project-wide.
53+
behavior, version assert base.h skip, AND triggers inclusion of
54+
`winrt_module_namespaces.h` for per-namespace guards.
55+
- `WINRT_MODULE_NS_*` — Per-namespace macros (e.g. `WINRT_MODULE_NS_Windows_Foundation`)
56+
defined in the generated `winrt_module_namespaces.h`. Each cross-namespace
57+
`#include` dep is guarded by `#ifndef WINRT_MODULE_NS_<namespace>`. Only
58+
namespaces in the module are skipped; component and other deps always resolve.
5759

58-
## WINRT_IMPL_SKIP_INCLUDES
60+
## Per-Namespace Include Guards
5961

60-
Generated namespace headers guard cross-namespace `#include` dependencies with:
62+
Generated namespace headers use per-namespace guards for cross-namespace deps:
6163
```cpp
62-
#ifndef WINRT_IMPL_SKIP_INCLUDES
63-
#include "winrt/impl/OtherNamespace.0.h"
64+
#ifndef WINRT_MODULE_NS_Windows_Foundation
65+
#include "winrt/impl/Windows.Foundation.0.h"
6466
#endif
6567
```
6668

67-
The version assert at the top of each namespace header checks all three macros:
69+
The version assert at the top of each namespace header:
6870
```cpp
69-
#if defined(WINRT_BUILD_MODULE) || defined(WINRT_MODULE) || defined(WINRT_IMPL_SKIP_INCLUDES)
71+
#if defined(WINRT_BUILD_MODULE) || defined(WINRT_MODULE)
7072
#include "winrt/base_macros.h"
71-
#else
73+
#endif
74+
#if defined(WINRT_MODULE) && !defined(WINRT_BUILD_MODULE)
75+
#include "winrt/winrt_module_namespaces.h"
76+
#endif
77+
#if !defined(WINRT_BUILD_MODULE) && !defined(WINRT_MODULE)
7278
#include "winrt/base.h"
7379
#endif
7480
```
7581

76-
- Cross-namespace dependencies (other namespaces' impl headers): GUARDED by
77-
`WINRT_IMPL_SKIP_INCLUDES` (`write_depends_guarded` / `write_root_include_guarded`)
78-
- Self-namespace dependencies (own impl headers): NOT guarded
82+
- Cross-namespace dependencies: GUARDED by `WINRT_MODULE_NS_*`
83+
(`write_depends_guarded` / `write_root_include_guarded`)
84+
- Self-namespace dependencies: NOT guarded
7985
(`write_depends` / `write_root_include`)
80-
- base.h include in version assert: GUARDED by all three macros
86+
- base.h include: GUARDED by `WINRT_BUILD_MODULE || WINRT_MODULE`
8187

8288
## Test Project Architecture
8389

cppwinrt/file_writers.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ import std;
6060
}
6161

6262
// Lightweight header containing only the preprocessor macros needed by
63-
// generated namespace headers. Used when WINRT_IMPL_SKIP_INCLUDES is
64-
// defined (i.e., consuming component headers after 'import winrt;').
65-
// Macros don't cross module boundaries, so this provides the macros
66-
// that base.h would normally supply.
63+
// generated namespace headers. Used when WINRT_MODULE or WINRT_BUILD_MODULE
64+
// is defined (i.e., consuming or building the winrt module). Macros don't
65+
// cross module boundaries, so this provides the macros that base.h would
66+
// normally supply.
6767
static void write_base_macros_h()
6868
{
6969
writer w;

docs/modules-internals.md

Lines changed: 68 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ them, and the interactions between the various moving pieces.
1010
- [winrt.ixx Generation](#winrtixx-generation)
1111
- [Split Standard Library Includes](#split-standard-library-includes)
1212
- [base_macros.h: Why Macros Need Special Handling](#base_macrosh-why-macros-need-special-handling)
13-
- [WINRT_IMPL_SKIP_INCLUDES: Guarded Cross-Namespace Includes](#winrt_impl_skip_includes-guarded-cross-namespace-includes)
13+
- [Per-Namespace Include Guards (WINRT_MODULE_NS_*)](#per-namespace-include-guards-winrt_module_ns)
1414
- [WINRT_EXPORT on Extern Handlers](#winrt_export-on-extern-handlers)
1515
- [The -module Flag: Component Code Generation](#the--module-flag-component-code-generation)
1616
- [The Combined-ixx Approach](#the-combined-ixx-approach)
@@ -133,52 +133,66 @@ import winrt;
133133
// ... template code using WINRT_IMPL_EMPTY_BASES etc.
134134
```
135135

136-
## WINRT_IMPL_SKIP_INCLUDES: Guarded Cross-Namespace Includes
136+
## Per-Namespace Include Guards (WINRT_MODULE_NS_*)
137137

138-
**Problem**: Inside generated `.g.h` files, after `import winrt;`, the
139-
component's own projection headers are included. Those headers have
140-
cross-namespace `#include` deps for platform SDK types (e.g.,
141-
`Windows.Foundation`) that are already available from the module. Re-including
142-
platform headers works (MSVC handles the re-declarations gracefully), but
143-
skipping them in this narrow scope avoids redundant processing.
138+
**Problem**: After `import winrt;`, consumers textually `#include` component
139+
and reference projection headers. Those headers have cross-namespace `#include`
140+
deps. Platform namespace deps (already in the module) must be skipped to avoid
141+
MSVC redeclaration errors on `inline constexpr` variable template specializations
142+
like `name_v`. But component-to-component cross-namespace deps must NOT be
143+
skipped, or multi-namespace components break.
144144

145-
**Solution**: Generated `.g.h` files set `WINRT_IMPL_SKIP_INCLUDES` locally
146-
(under `#ifdef WINRT_MODULE`) before including the component's own headers.
147-
Generated namespace headers wrap their cross-namespace deps with guards:
145+
**Solution**: cppwinrt generates `winrt/winrt_module_namespaces.h` alongside
146+
`winrt.ixx`. This header defines one macro per namespace in the module:
148147

149148
```cpp
150-
// In generated Windows.Foundation.h:
151-
#ifndef WINRT_IMPL_SKIP_INCLUDES
152-
#include "winrt/base.h"
149+
// Generated winrt/winrt_module_namespaces.h:
150+
#pragma once
151+
#define WINRT_MODULE_NS_Windows_Foundation
152+
#define WINRT_MODULE_NS_Windows_Foundation_Collections
153+
// ... one per namespace in the module
154+
```
155+
156+
Generated namespace headers use per-namespace guards for cross-namespace deps:
157+
158+
```cpp
159+
// In generated TestModuleComponent.h:
160+
#ifndef WINRT_MODULE_NS_Windows_Foundation
161+
#include "winrt/impl/Windows.Foundation.2.h"
153162
#endif
154-
// ... (cross-namespace deps guarded)
155-
#include "winrt/impl/Windows.Foundation.2.h" // self-namespace: NOT guarded
163+
#include "winrt/impl/TestModuleComponent.Widgets.2.h" // NOT guarded (not in module)
164+
#include "winrt/impl/TestModuleComponent.2.h" // self-namespace: never guarded
156165
```
157166

158-
**Critical scoping**: `WINRT_IMPL_SKIP_INCLUDES` is NOT defined project-wide.
159-
It is only defined locally inside `.g.h` files. This ensures that consumer
160-
source files can `#include <winrt/MyComponent.Widgets.h>` after `import winrt;`
161-
and the cross-namespace deps between component namespaces resolve normally.
162-
163-
**WINRT_MODULE vs WINRT_IMPL_SKIP_INCLUDES in the version assert**: The version
164-
assert at the top of every namespace header checks `WINRT_BUILD_MODULE`,
165-
`WINRT_MODULE`, or `WINRT_IMPL_SKIP_INCLUDES` to decide whether to skip
166-
`#include "base.h"` and use `base_macros.h` instead. This three-way check
167-
covers: (a) inside winrt.ixx, (b) consumer source files, (c) inside .g.h files.
168-
169-
**WINRT_BUILD_MODULE in winrt.ixx**: The ixx defines `WINRT_BUILD_MODULE`
170-
and `#undef`s `WINRT_IMPL_SKIP_INCLUDES` in its global module fragment. This
171-
ensures the version assert uses `base_macros.h` (since base.h is already
172-
explicitly included), but cross-namespace deps are included normally so
173-
namespace headers can reference each other's types inside the ixx.
174-
175-
**Implementation**: `write_root_include_guarded()` in `type_writers.h` wraps the
176-
`#include` with `#ifndef WINRT_IMPL_SKIP_INCLUDES`. Used by:
167+
When `WINRT_MODULE` is defined, the version assert at the top of each namespace
168+
header includes `winrt_module_namespaces.h`, making the `WINRT_MODULE_NS_*`
169+
macros available for the guards. When `WINRT_BUILD_MODULE` is defined (inside
170+
winrt.ixx), `winrt_module_namespaces.h` is NOT included, so all cross-namespace
171+
deps resolve normally — the ixx needs them to build the module.
172+
173+
**Implementation**: `write_root_include_guarded()` in `type_writers.h` extracts
174+
the namespace from the include path and emits `#ifndef WINRT_MODULE_NS_<ns>`.
175+
Used by:
177176
- `write_depends_guarded()` for cross-namespace impl includes
178177
- `write_parent_depends()` for parent namespace includes
179178

180-
`write_version_assert()` in `code_writers.h` checks `WINRT_BUILD_MODULE ||
181-
WINRT_MODULE || WINRT_IMPL_SKIP_INCLUDES` for the base.h skip.
179+
`write_version_assert()` in `code_writers.h`:
180+
```cpp
181+
#if defined(WINRT_BUILD_MODULE) || defined(WINRT_MODULE)
182+
#include "winrt/base_macros.h"
183+
#endif
184+
#if defined(WINRT_MODULE) && !defined(WINRT_BUILD_MODULE)
185+
#include "winrt/winrt_module_namespaces.h"
186+
#endif
187+
#if !defined(WINRT_BUILD_MODULE) && !defined(WINRT_MODULE)
188+
#include "winrt/base.h"
189+
#endif
190+
```
191+
192+
**winrt_module_namespaces.h generation**: In `main.cpp`, emitted alongside
193+
`winrt.ixx` when `settings.base` is true and `settings.component` is false.
194+
This prevents component projections (which may also use `-base`) from generating
195+
a stale version that would mask the module builder's file on the include path.
182196

183197
The component's own impl headers are always included (via `write_depends()`,
184198
unguarded) since they contain the component-specific type definitions.
@@ -241,14 +255,15 @@ import winrt;
241255
import std;
242256
#endif
243257
import winrt;
244-
#define WINRT_IMPL_SKIP_INCLUDES
245258
#endif
246259
#include "winrt/test_component.h" // always emitted
247260
```
248261

249-
When `WINRT_MODULE` is defined, `WINRT_IMPL_SKIP_INCLUDES` causes the component
250-
headers to skip cross-namespace platform `#include` deps (already in the module).
251-
When not defined, the headers pull in `base.h` transitively as normal.
262+
When `WINRT_MODULE` is defined, the version assert in each included header
263+
pulls in `winrt_module_namespaces.h`, which provides per-namespace
264+
`WINRT_MODULE_NS_*` macros. Platform namespace deps are skipped by these
265+
guards; component cross-namespace deps are included normally.
266+
When `WINRT_MODULE` is not defined, the headers pull in `base.h` transitively.
252267

253268
### Toaster.g.cpp (factory + optional optimized constructors)
254269

@@ -387,34 +402,41 @@ User sets CppWinRTModuleBuild=true (or CppWinRTModuleConsume=true) + BuildStlMod
387402
├── NuGet targets define WINRT_IMPORT_STD on all ClCompile items
388403
389404
390-
winrt.ixx compilation:
405+
winrt.ixx compilation (WINRT_BUILD_MODULE defined in global module fragment):
391406
module;
407+
#define WINRT_BUILD_MODULE
392408
<intrin.h, version, directxmath.h> ← platform includes (base_includes)
393409
<algorithm, array, string, coroutine, ...> ← std library includes (base_std_includes)
394410
export module winrt;
395411
#define WINRT_EXPORT export
396-
#include "winrt/base.h" ← base.h (pragma once)
412+
#include "winrt/base.h" ← base.h (explicit include)
397413
#include "winrt/Windows.Foundation.h" ← SDK types + specializations
398414
... ← winrt::impl is exported
415+
Also generates: winrt/winrt_module_namespaces.h (per-namespace macros)
399416
400-
Consumer .cpp compilation:
417+
Consumer .cpp compilation (WINRT_MODULE defined by NuGet targets):
401418
#include "pch.h" ← PCH (no winrt/ headers)
402419
import std; ← std module (optional, needs BuildStlModules)
403420
import winrt; ← winrt module (SDK types + exported impl)
421+
#include "winrt/MyComponent.h" ← reference projection header
422+
→ version assert includes winrt_module_namespaces.h (via WINRT_MODULE)
423+
→ platform deps guarded by WINRT_MODULE_NS_* (skipped)
424+
→ component cross-namespace deps NOT guarded (included normally)
404425
405426
Component .g.h (WINRT_MODULE defined by NuGet targets):
406427
#include "winrt/base_macros.h" ← macros only (WINRT_EXPORT etc.)
407428
#ifdef WINRT_IMPORT_STD
408429
import std; ← conditional on WINRT_IMPORT_STD
409430
#endif
410431
import winrt; ← SDK types + exported impl templates
411-
#define WINRT_IMPL_SKIP_INCLUDES ← skip SDK #include deps
412432
#include "winrt/MyComponent.h" ← component projection (specializes impl)
433+
→ platform deps skipped by WINRT_MODULE_NS_* guards
434+
→ cross-namespace component deps included normally
413435
414436
module.g.cpp compilation:
415437
#include "pch.h" ← PCH preserved
416438
#ifdef WINRT_IMPORT_STD / import std; ← conditional
417-
import winrt; ← from -module code gen
439+
import winrt; ← module import
418440
void* winrt_make_MyComponent_Toaster() ← factory function
419441
bool __stdcall ..._can_unload_now() ← DLL entry points
420442
```

0 commit comments

Comments
 (0)