refactor(xodr,osc): one enum table and one XML helper set instead of two (p-ponytail-a) - #564
Merged
Merged
Conversation
…two (#563) Six OpenDRIVE enums each had a read table (an if-chain in xodr/reader.cpp) and a write table (a switch in xodr/writer.cpp), in different files, with nothing tying the two directions together. That is the shape #476 came in: the writer re-spelled parsed lane and mark enums into different semantics on save. They now share one constexpr table each in core/src/xodr/enum_names.hpp. Three things stay out of the tables, each preserving today's behaviour: *::Other never gets a row (it is the writer's lossy fallback argument — as a row, "solid" would parse back as RoadMarkType::Other); the empty string never gets a row (absent-attribute policy is per-attribute and stays at the call site with the paragraph that mandates it); and value_of returns nullopt rather than guessing. Five pugixml scalar helpers the two formats each kept a private copy of — to_double, num, set_num, set_optional_num, node_to_string — collapse into core/src/xml/xml_common.hpp. append_fragment does NOT: OpenSCENARIO passes pugi::parse_fragment and OpenDRIVE takes the defaults, so that pair is a real divergence rather than a copy defended against a hypothetical one. Also: linked_end was byte-identical in edit/operations.cpp and mesh/junction_maneuvers.cpp and moves to road/junction_adjacency.hpp; the @orientation if-chain was written out twice in the reader; is_one_of hand-rolled std::ranges::find; and Environment::procedural_sky was set, asserted by two tests, and never read by GLRenderer. Re-emitting all 143 tracked .xodr/.xosc fixtures is byte-identical, diagnostics included. That probe cannot see these tables, though — a round trip is invariant under a consistent permutation, and the preserved tier (*::type_str) means a parsed file never consults them — so core/tests/test_enum_names.cpp pins the spellings against literals transcribed from the ASAM tables. Four sabotages the byte probe called clean all fail it. -99 lines of executable code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 1 of 2 for #563 (ponytail audit remediation). Kernel-side duplication only;
the docs-site half follows in its own PR.
No behaviour change — and the evidence for that is byte-level, not "the
tests pass". See Acceptance below, including the part where my first gate
turned out to be blind.
What collapsed
if-chain inxodr/reader.cppand aswitchinxodr/writer.cpp, in different files, per enumconstexprtable each in the newcore/src/xodr/enum_names.hpp, serving both directionsto_double,num,set_num,set_optional_num,node_to_string)core/src/xml/xml_common.hpplinked_endedit/operations.cppandmesh/junction_maneuvers.cppcore/src/road/junction_adjacency.hppis_one_ofinitializer_liststd::ranges::find@orientationif-chain written out twice in the readervalue_ofcall eachEnvironment::procedural_skyGLRendererThe enums are the point. Two tables maintained separately, in opposite
directions, is the exact shape #476 came in — the writer re-spelled parsed lane
and mark enums into different semantics on save. One table cannot disagree with
itself.
Three invariants the table deliberately does not absorb, each documented in
the header:
*::Othernever gets a row (it is the writer's lossyfallbackargument — as a row,
"solid"would parse back asRoadMarkType::Other); theempty string never gets a row (absent-attribute policy is per-attribute and
stays at the call site with the paragraph that mandates it); and
value_ofreturns
nulloptrather than guessing.Not shared, deliberately
append_fragmentlooks like a sixth duplicated helper and is not one: theOpenSCENARIO writer passes
pugi::parse_fragmentand the OpenDRIVE writer takespugixml's defaults. That pair is a real divergence, so it stays per-format —
unlike the five helpers above, whose copies carried comments defending them
against a divergence that never happened.
Also left alone, and why:
point_segment_distance(identical in twosubsystems that share no internal header — a new one to save 13 lines is
break-even), and the OpenSCENARIO
PhaseSemanticstwin (one enum, six values,not worth a file).
Acceptance
1. Byte-identical output. A throwaway probe reads and re-emits all 143
tracked
.xodr/.xoscfixtures — 128 produce documents, 63 produce diagnostics— and diffs bytes and diagnostic text against the same probe built on
main.Zero differences.
2. That probe is structurally blind to these tables, which I found by
sabotaging it. With
{Solid,"broken"}, {Broken,"solid"}compiled in it stillreported zero differences, for two compounding reasons:
"+"asMinus, writeMinusas"+", identical bytes, every object facingbackwards;
Lane/RoadMark/Object::type_strhold the verbatim spelling for every parsed element (the xodr: unknown enum spellings are rewritten on save — lane type → "none", road-mark type → "solid", colour → "standard" #476 fix), so
re-emitting a parsed file never consults these tables at all.
The tables are reached by networks the editor, the bindings and the importers
build, which no fixture round trip exercises. So the probe proves the
preserved tier, the diagnostics and everything non-enum are untouched — and
nothing about the tables.
3.
core/tests/test_enum_names.cppis the gate that does cover them, withliterals transcribed from the ASAM tables rather than derived from the code.
The obvious test —
value_of(name_of(v)) == v— is a tautology for exactlyreason (1) above, and is not what this file does. Four sabotages, all caught,
none of which the byte probe noticed:
test_enum_namessolid/broken@orientation+/-walkingalias to canonicalcurbas a realRoadMarkTyperow4.
ci-macos(-Werror): build clean, 3329/3329 ctest green.git ls-files '*.cpp' '*.hpp' | xargs clang-format --dry-run --Werrorclean —run after staging, since CI's command skips untracked files and all three new
files were untracked.
Size
−99 lines of executable code (+214 / −313, comments and blanks excluded);
+26 lines net counting the two new headers' comments, which carry the
"never make this a row" rules and the spec citations. The audit predicted −240
for these items; it had counted the deletions without the shared implementation
that replaces them.
Closes #563 is deliberately not claimed here — the docs-site half is still
outstanding.