Skip to content

v3.4.0: readable diagnostics (class-based named units) + #357 fix + natvis + error-msg CI harness - #358

Merged
nholthaus merged 5 commits into
v3.xfrom
proto/strong-adl-357
Aug 14, 2026
Merged

v3.4.0: readable diagnostics (class-based named units) + #357 fix + natvis + error-msg CI harness#358
nholthaus merged 5 commits into
v3.xfrom
proto/strong-adl-357

Conversation

@nholthaus

Copy link
Copy Markdown
Owner

Summary

A readability + robustness release for the v3.x line — focused on what you see in the compiler and the
debugger. No public API changes: meters<> / meters<double> and every unit spell exactly as before.

1. Diagnostics name the unit, not the template soup

Named units are now classes deriving from units::unit<...> (instead of alias templates), so a diagnostic
reads units::length::meters<double>units::frequency::hertz<double> instead of
unit<conversion_factor<std::ratio<1>, dimension_t<...>>, double, linear_scale> — for both operands and
computed results
(length*lengthsquare_meters<double>, etc.). Rendering only; representation/values/
API unchanged.

2. Fix #357 — include order no longer breaks the build

Forming an expression that reduces to a not-yet-included dimension (velocity/length1/time) failed with
"explicit specialization after instantiation." The strong-type lookup is now an ADL customization point, so
a later-included header just adds a better overload — include order can't decide compilability.

3. Visual Studio natvis

natvis/units.natvis shows a unit as 3 m in Watch/Locals/DataTips; attached to the target + installed
(MSVC-only, no-op elsewhere).

4. std:: math extensions (upstream PR #356 by @carlfriess, merged into this branch)

std::isnan/isinf/signbit switched off the removed operator() to raw(); adds std::isfinite; macOS
locale branch for the locale test.

Testing

  • unitLibTest 238/238 green, incl. new namedUnitReportedTypeIsPreserved + unitsAsContainerKeys.
  • New error-message harness (test/errorMessages/, 29 cases) grading readable-named-type presence and
    no-conversion_factor-soup — compiler-portable (g++/clang/MSVC) and wired into all three CI workflows.

Under the hood

Trait/std specializations, arithmetic operators, to_string/operator<<, name()/abbreviation() all
see through / preserve the named class; numeric_limits<Named> returns the named type; common_type <dimensioned-named, scalar> is SFINAE-empty (parity with the plain form); pure dimensionless stays a plain
alias (interchangeable with int/double).

Version bumped to 3.4.0.

Upstream PR #356 (carlfriess:v3.x-fix-std-extension). The std:: math extensions (isnan/isinf/signbit) still
called the removed unit operator() value accessor; switch them to raw(), add the missing std::isfinite
overload, correct the doc comment (m() -> m.raw()), and add a macOS locale branch (BSD libc needs the
canonical de_DE.UTF-8/en_US.UTF-8 spelling) to to_string_locale.
@nholthaus
nholthaus force-pushed the proto/strong-adl-357 branch 5 times, most recently from 8a48f5c to a777ab3 Compare August 14, 2026 07:22
…print meters<double>

Two linked readability wins for the v3.x unit type:

1. #357 (explicit-specialization-after-instantiation): forming an expression that reduces to a not-yet-
   included dimension (velocity/length -> 1/time) no longer breaks the build. traits::strong resolves the
   named strong type via an ADL customization point (detail::strong_name), not an explicit specialization of
   a class template — a later-included dimension header just contributes a better overload.

2. Named units (meters, hertz, square_meters, ...) are now CLASSES deriving from units::unit<...> instead of
   alias templates, so a compiler diagnostic prints the friendly 'meters<double>' instead of the
   'unit<conversion_factor<...>, double, linear_scale>' soup — operands AND computed results. NO public API
   change: meters<>/meters<double> spell identically. The trait layer sees through / preserves the named type
   (detail::unit_base_t / is_named_unit_v / rewrap_to_named_t + the named_class_of ADL reverse map keyed on
   conversion_factor AND scale so linear vs decibel don't collide; replace_underlying, floating_point_promotion,
   std::common_type, std::hash, std::numeric_limits, arithmetic return types, to_string/operator<<,
   name()/abbreviation()). Pure dimensionless stays a plain alias (interchangeable with int/double).
…ression & coverage tests

Harness (test/errorMessages): generate_cases.py emits 25 cases across the unit zoo (cross-dimension bad
conversions, incompatible add/sub, derived-result mis-assignment, #357 include-ordering) + 4 curated = 29;
run.py grades expected pass/fail + readable-named-type present + no conversion_factor soup, COMPILER-PORTABLE
(MSVC cl /Zs + a normalize() so tokens match g++/clang/MSVC). Wired into all three CI workflows as an
'Error-message tests' step (MSVC job sets up cl via msvc-dev-cmd).

Tests: namedUnitReportedTypeIsPreserved (arithmetic results named, math fns preserve name, traits see through
the derived class, numeric_limits<Named> returns the named type, common_type<dimensioned-named,scalar> is
SFINAE-empty, name()/abbreviation() incl. a compound unit, non-registered derived CF stays plain);
unitsAsContainerKeys (units as std::map/set/unordered_map keys). unitLibTest 238/238; harness 29/29 on g++.
…opy)

Show a unit as '3 m' / '5 mps' in Visual Studio Watch/Locals/DataTips instead of an opaque object. Adapted
for this repo: type is units::unit<Cf,T,Scale> (not unit_t); magnitude is _linearized_value (not m_value);
named units are classes deriving from units::unit<...> and natvis <Type> rules are inheritable, so the
unit<*...> rules visualize a named unit through its base; linear_scale/decibel_scale are stateless here so
the old scale-base rules are dropped. Abbreviation via an Optional func-eval intrinsic with a tag-name
fallback. Wired into the INTERFACE target + install (MSVC-guarded, no-op elsewhere).
Sync the CMakeLists project() version (was stale at 3.1.2) to the v3.4.0 release.
@nholthaus
nholthaus force-pushed the proto/strong-adl-357 branch from a777ab3 to 279f12e Compare August 14, 2026 07:32
@nholthaus
nholthaus merged commit e555819 into v3.x Aug 14, 2026
6 checks passed
@nholthaus
nholthaus deleted the proto/strong-adl-357 branch August 14, 2026 07:38
@chiphogg

Copy link
Copy Markdown

Readable compiler errors with concise typenames? That's a huge upgrade! Congrats on this improvement --- it's awesome to see. 🚀

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.

3 participants