Skip to content

Generate the reference from the header, and check that it is one - #10

Merged
tamnd merged 1 commit into
mainfrom
docs-reference
Aug 23, 2026
Merged

tamnd merged 1 commit into
mainfrom
docs-reference

Conversation

@tamnd

@tamnd tamnd commented Aug 23, 2026

Copy link
Copy Markdown
Owner

The reference item on zu-c's scorecard: a generated API reference, published with the release rather than written by hand beside it.

Most of the work was in the header. Doxygen only reads /** and /*!, and every comment in zu.hpp was written as /*, so a generator run over it produced a reference with 355 undocumented members in it. The comments that document a declaration are now /** and the rest are left alone: a section divider is not a doc comment and would arrive as the description of whatever came after it, and a remark inside a function body has nothing to attach to. Two comments had been written inside a body, where nothing reads them, and are now above the function they belong to.

Where the header documents an overload set once, it still does. DISTRIBUTE_GROUP_DOC hands the first comment in a ///@{ block to every member of the block, which is what fits this header's authoring style: the try_ half of each class is the same call as the one above it answering a failure rather than throwing, and it carries no prose of its own on purpose, so that it cannot drift from the throwing half. Writing sixty comments that restate their signatures would have made the reference worse rather than better.

The check

This is the part worth reviewing.

Doxygen exits 0, with no warnings and no pages, on a header it extracted nothing from. WARN_IF_UNDOCUMENTED cannot fire when there is nothing there to be undocumented, so a gate that is only "Doxygen was quiet" passes over an empty reference.

That is not hypothetical. A ZU_HPP left in PREDEFINED while this was being built defined the header's own include guard, so #ifndef ZU_HPP was false, the preprocessor dropped the whole file, and the run was green with nothing in it. The Doxyfile now carries a comment saying never to put it back.

So docs/reference.py reads the header a second time and by different means. It greps the types zu.hpp declares at namespace scope, parses the XML Doxygen wrote, and fails when the first list is not covered by the second. One grep and one parser disagreeing is what an empty reference looks like from the outside.

Both failures were exercised rather than reasoned about. With the include guard back in PREDEFINED:

27 of the 27 types zu.hpp declares have no page: Position, Node, Rel, Temporal, Error, ...
all of them, which is what an empty reference looks like. Check PREDEFINED in the Doxyfile for the include guard.
exit 1

And with one field's comment turned back into a plain /*:

/root/zu-c/include/zu.hpp:192: warning: Member line (variable) of struct zu::Position is not documented.
doxygen had that much to say about the header
exit 1

Building it

Off by default, unlike tests, examples and benchmarks. Doxygen is not needed to build this header or to use it, and a contributor with a compiler and nothing else should be able to configure the repository.

cmake -B build -DZU_CPP_DOCS=ON
cmake --build build --target docs
ctest --test-dir build -R reference

No engine is needed for any of it, which is why the CI job is thirty seconds rather than ten minutes. The reference is about the header, and the header is what this repository owns. zu.h is generated in the engine's tree and is not here, so a reference for the C API belongs where the C API is written rather than as a second copy that drifts the day the generator runs again.

release.yml builds the reference from the tag, runs the same check over it, and attaches it to the release, so there is no directory of generated HTML in the tree and no copy anybody has to remember to regenerate.

Checked

On a Linux box with the engine at HEAD:

  • 41 of 41 tests green, which includes the readme/ suite over the two blocks this PR adds to the page.
  • The reference builds with no warnings of any kind, over 61 pages and 27 types, from 355 undocumented members before.

The scorecard item asks for a generated API reference published with the
release rather than written by hand beside it. This is that, and most of
the work was in the header: Doxygen only reads /** and /*!, and every
comment in zu.hpp was written as /*, so a generator run over it produced
a reference with 355 undocumented members in it.

The comments that document a declaration are now /**, and the rest are
left alone. Section dividers are not doc comments and would arrive as
the description of whatever came next, and a remark inside a function
body has nothing to attach to.

Where the header documents an overload set once, the group stays one
comment. DISTRIBUTE_GROUP_DOC hands the first comment in a ///@{ block
to every member of it, which is how the try_ half of each class is
documented: it is the same call as the one above it answering a failure
rather than throwing, and saying so sixty times is how prose comes to
disagree with itself. Two comments had been written inside a function
body, where nothing reads them, and are now above the function.

The check is the part worth reading. Doxygen exits 0, with no warnings
and no pages, on a header it extracted nothing from, and
WARN_IF_UNDOCUMENTED cannot fire when there is nothing there to be
undocumented. That is not hypothetical: a ZU_HPP left in PREDEFINED
defined this header's own include guard, the preprocessor dropped the
whole file, and the run was green and empty. So docs/reference.py reads
the header a second time and by different means, greps the types it
declares at namespace scope, and fails when the reference does not have
them. Both failures are exercised: with the guard back in PREDEFINED it
reports all 27 types missing and names the cause, and with one field's
comment turned back into a plain /* it reports that field.

ZU_CPP_DOCS is off even at top level, unlike tests, examples and
benchmarks. Doxygen is not needed to build this header or to use it, and
a contributor with a compiler and nothing else should be able to
configure the repository. CI turns it on, and so does the release, which
builds the reference from the tag and attaches it rather than keeping a
directory of generated HTML in the tree.

No engine is needed for any of it. The reference is about the header,
and the header is what this repository owns; zu.h is generated in the
engine's tree and a copy of its reference here would be wrong the day
the generator runs again.

41 tests green, and the reference builds with no warnings of any kind
over 61 pages.
@tamnd
tamnd merged commit 21a190e into main Aug 23, 2026
3 of 8 checks passed
@tamnd
tamnd deleted the docs-reference branch August 23, 2026 04:00
tamnd added a commit to tamnd/zu that referenced this pull request Aug 23, 2026
tamnd/zu-c#10 generates the reference from include/zu.hpp and publishes
it with the release rather than keeping a copy in the tree.

The item is held on the check rather than on the output. Doxygen exits
0, with no warnings and no pages, on a header it extracted nothing from,
so a gate that is only "the generator was quiet" passes over an empty
reference. It did, for a day: an include guard left in PREDEFINED made
the preprocessor drop the whole header. The client's check reads the
header a second time and by different means and fails when the types it
declares are not in what came out.

Practice goes 67 to 78 over a denominator of 90, which is the tier's 100
less the api-map and perf weights zu-c does not owe. install and
stability are what is left.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant