@@ -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
183197The component's own impl headers are always included (via ` write_depends() ` ,
184198unguarded) since they contain the component-specific type definitions.
@@ -241,14 +255,15 @@ import winrt;
241255import std;
242256#endif
243257import 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
405426Component .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
414436module.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