You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: recommend a single project Io.h/Io.c pair for user IOFMT
Adds a "Recommended Layout" section that names the one-pair pattern as
the default and explains the maintenance pitfalls of splitting (per-TU
macro drift, forward-decl sprawl across TUs, include-order auditing).
Splitting is still supported; the guide now spells out the trade-off so
users don't accidentally adopt the harder layout.
Pattern A is the safe default once you have more than one user type.
154
154
155
+
## Recommended Layout: One `Io.h` / `Io.c` Pair per Project
156
+
157
+
The mechanism above is per-TU, but the natural unit of organisation is the
158
+
project: every TU that formats your types needs the same `IOFMT_USER_CASES_`
159
+
list visible, every `_write_T` / `_read_T` symbol needs to be linkable from
160
+
those TUs, and every user type in the list needs its struct declaration in
161
+
scope so the `_Generic` arm can mention it.
162
+
163
+
The cleanest way to satisfy all three constraints is to keep your IO
164
+
extension in a single project-internal pair:
165
+
166
+
```
167
+
MyApp/Io.h -- forward-declares every user type's _write_T / _read_T,
168
+
defines IOFMT_USER_CASES_, then #include <Misra/Std/Io.h>
169
+
MyApp/Io.c -- defines the bodies for every _write_T / _read_T
170
+
```
171
+
172
+
Every TU in your project then has exactly one extra include:
173
+
174
+
```c
175
+
#include"MyApp/Io.h"// pulls in everything: types, hook, Misra/Std/Io.h
176
+
```
177
+
178
+
You can absolutely split the writers/readers across multiple `.c` files, or
179
+
keep them next to each type's own module, and it will compile and link.
180
+
But maintenance gets harder fast:
181
+
182
+
-**Macro drift.** Each TU that calls `WriteFmt(..., my_t)` needs
183
+
`IOFMT_USER_CASES_` defined with `my_t`'s arm. If one TU's hook lags
184
+
behind, that TU silently fails to match (compile error if you're lucky,
185
+
wrong-arm dispatch if a built-in coincidentally matches the type).
186
+
-**Forward-declaration sprawl.** Pattern A (forward-declare every
187
+
user-type writer/reader before any body) becomes "every TU has to
188
+
forward-declare every user type's writer/reader" once nested writers
189
+
cross TU boundaries. Centralising the forward decls in one header
190
+
removes the bookkeeping.
191
+
-**Include-order auditing.** The "define hook before `<Misra/Std/Io.h>`"
192
+
rule is easy to violate when the IO header is pulled transitively. A
193
+
single project IO header that wraps the rule once eliminates the audit
194
+
surface entirely.
195
+
196
+
If you do split, treat `MyApp/Io.h` as authoritative for the hook + forward
197
+
decls and treat the per-module `.c` files purely as homes for the bodies.
198
+
That keeps the macro discipline in one place and lets the bodies live next
199
+
to their data.
200
+
155
201
## Composing Across Libraries
156
202
157
203
`IOFMT_USER_CASES_` is a single preprocessor symbol. If two libraries both want to publish IO-able types, the consumer's chain has to thread them together. The usual idiom is *rename-then-extend*:
0 commit comments