feat: added an option to disable space before unit symbol in the text output

Resolves #387
This commit is contained in:
Mateusz Pusz
2023-09-04 11:09:40 +02:00
parent 75482733d1
commit f46b3dad74
6 changed files with 87 additions and 3 deletions

View File

@ -13,7 +13,36 @@ any quantity in the most user-friendly way.
a much better solution, but the library does not have enough information to print it that way by itself.
## Output Streams
## Customization point
The [SI Brochure](../appendix/references.md#SIBrochure) says:
!!! quote "SI Brochure"
The numerical value always precedes the unit and a space is always used to separate the unit from
the number. ... The only exceptions to this rule are for the unit symbols for degree, minute and
second for plane angle, `°`, `` and `″`, respectively, for which no space is left between the
numerical value and the unit symbol.
To support the above, the library exposes `space_before_unit_symbol` customization point. By default,
its value is `true` for all the units, so the space between a number and a unit will be present in the
output text. To change this behavior, we have to provide a partial specialization for a specific unit:
```cpp
template<>
inline constexpr bool space_before_unit_symbol<non_si::degree> = false;
```
!!! note
The above works only for [the default formatting](#default-formatting). In case we provide our own
format specification (i.e. `std::format("{:%Q %q}", q)`), the library will always obey this
specification for all the units (no matter of what is the actual value of the
`space_before_unit_symbol` customization point) and the separating space will always be present
in this case.
## Output streams
!!! tip

View File

@ -398,7 +398,7 @@ private:
// default format should print value followed by the unit separated with 1 space
out = mp_units::detail::format_units_quantity_value<CharT>(out, q.numerical_value(), specs.rep, ctx.locale());
if constexpr (mp_units::detail::has_unit_symbol(get_unit(Reference))) {
*out++ = CharT(' ');
if constexpr (mp_units::space_before_unit_symbol<get_unit(Reference)>) *out++ = CharT(' ');
out = unit_symbol_to<CharT>(out, get_unit(Reference));
}
} else {

View File

@ -40,7 +40,7 @@ void to_stream(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep>& q)
else
os << q.numerical_value();
if constexpr (has_unit_symbol(get_unit(R))) {
os << " ";
if constexpr (space_before_unit_symbol<get_unit(R)>) os << " ";
unit_symbol_to<CharT>(std::ostream_iterator<CharT>(os), get_unit(R));
}
}

View File

@ -807,6 +807,15 @@ constexpr Out unit_symbol_impl(Out out, const derived_unit<Expr...>&, unit_symbo
} // namespace detail
/**
* @brief Puts a space ' ' sign before a unit symbol
*
* Quantities of some units (e.g. degree, arcminute, arcsecond) should not be printed with the
* space between a number and a unit. For those a partial specialization with the value `false` should
* be provided.
*/
template<Unit auto U>
inline constexpr bool space_before_unit_symbol = true;
template<typename CharT = char, std::output_iterator<CharT> Out, Unit U>
constexpr Out unit_symbol_to(Out out, U u, unit_symbol_formatting fmt = unit_symbol_formatting{})

View File

@ -123,4 +123,11 @@ inline constexpr bool unit_can_be_prefixed<non_si::hour> = false;
template<>
inline constexpr bool unit_can_be_prefixed<non_si::day> = false;
template<>
inline constexpr bool space_before_unit_symbol<non_si::degree> = false;
template<>
inline constexpr bool space_before_unit_symbol<non_si::arcminute> = false;
template<>
inline constexpr bool space_before_unit_symbol<non_si::arcsecond> = false;
} // namespace mp_units

View File

@ -236,6 +236,45 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
}
}
SECTION("no space before unit symbol")
{
SECTION("degree")
{
const auto q = 42 * deg;
os << q;
SECTION("iostream") { CHECK(os.str() == "42°"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 °"); }
}
SECTION("arcminute")
{
const auto q = 42 * arcmin;
os << q;
SECTION("iostream") { CHECK(os.str() == "42"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 "); }
}
SECTION("arcsecond")
{
const auto q = 42 * arcsec;
os << q;
SECTION("iostream") { CHECK(os.str() == "42″"); }
SECTION("fmt with default format {} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{}", q) == os.str()); }
SECTION("fmt with format {:%Q %q} on a quantity") { CHECK(MP_UNITS_STD_FMT::format("{:%Q %q}", q) == "42 ″"); }
}
}
SECTION("8-bit integers")
{
SECTION("signed positive")