Skip to content

Commit

Permalink
Improve dimensionless nholthaus unit support (#161)
Browse files Browse the repository at this point in the history
Previously, we had a special implementation for `percent` only.  This PR
switches it to a more general solution that will handle every
dimensionless value.  (Note that the existing `percent` tests still
pass, unmodified.)

This change uncovered the fact that some nholthaus units (including
`ppb_t`) are defined in terms of other `unit<...>` specializations,
_not_ directly in terms of a `base_unit<...>`.  I added a new generic
pattern to deal with this case recursively.
  • Loading branch information
chiphogg authored Jul 31, 2023
1 parent 3fa22a2 commit 01e60f7
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 8 deletions.
38 changes: 30 additions & 8 deletions compatibility/nholthaus_units.hh
Original file line number Diff line number Diff line change
Expand Up @@ -234,19 +234,41 @@ struct CorrespondingQuantity<

// nholthaus handles dimensionless values inconsistently, so we must work around it. See:
// https://github.com/nholthaus/units/issues/276
template <typename R>
struct CorrespondingQuantity<units::unit_t<units::concentration::percent, R, units::linear_scale>> {
using Unit = Percent;
using Rep = R;

template <typename R, typename RationalScale>
struct CorrespondingQuantity<
units::unit_t<units::unit<RationalScale, units::base_unit<>, std::ratio<0>, std::ratio<0>>,
R,
units::linear_scale>> {
using NholthausType = typename detail::SoleTemplateParameter<CorrespondingQuantity>::type;
using NholthausUnit = typename detail::NholthausUnitType<NholthausType>::type;
using Mag = detail::NholthausUnitMagT<NholthausUnit>;

using Unit = UnitImpl<Dimension<>, Mag>;
using Rep = R;

// This is the workaround: we must manually multiply the value by 100, because the nholthaus
// library divides it by 100. Note the glaring asymmetry between `extract_value()` and
// `construct_from_value()`.
static constexpr Rep extract_value(NholthausType d) { return 100 * d.template to<Rep>(); }
// library divides it by 100. Note the asymmetry between `extract_value()` and
// `construct_from_value()`: we must multiply by `inverse_mag` only in the former.
static constexpr Rep extract_value(NholthausType d) {
return get_value<Rep>(mag<1>() / Mag{}) * d.template to<Rep>();
}

static constexpr NholthausType construct_from_value(Rep x) { return NholthausType{x}; }
};

// If nholthaus, for whatever reason, defined a unit in terms of a non-`base_unit` specialization,
// unpack it one more level. Eventually, we should recursively reach a `base_unit` specialization,
// and match one of the above `CorrespondingQuantity` specializations.
template <typename OuterRatio, typename InnerRatio, typename BaseUnit, typename R>
struct CorrespondingQuantity<units::unit_t<
units::unit<OuterRatio, units::unit<InnerRatio, BaseUnit, std::ratio<0>, std::ratio<0>>>,
R,
units::linear_scale>>
: CorrespondingQuantity<units::unit_t<units::unit<std::ratio_multiply<OuterRatio, InnerRatio>,
BaseUnit,
std::ratio<0>,
std::ratio<0>>,
R,
units::linear_scale>> {};

} // namespace au
3 changes: 3 additions & 0 deletions compatibility/nholthaus_units_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ TEST(NholthausTypes, MapsDerivedUnitsFoundInCodebaseCorrectly) {
expect_equivalent<::units::area::square_meter_t>(squared(meters));

expect_equivalent<::units::concentration::percent_t>(percent);
expect_equivalent<::units::concentration::ppm_t>(unos / mag<1'000'000>());
expect_equivalent<::units::concentration::ppb_t>(unos / mag<1'000'000'000>());
expect_equivalent<::units::concentration::ppt_t>(unos / mag<1'000'000'000'000>());

expect_equivalent<::units::data_transfer_rate::bytes_per_second_t>(bytes / second);
expect_equivalent<::units::data_transfer_rate::megabytes_per_second_t>(mega(bytes) / second);
Expand Down

0 comments on commit 01e60f7

Please sign in to comment.