Skip to content

Commit

Permalink
Convert Constant to other libraries' types (#207)
Browse files Browse the repository at this point in the history
If users have set up an exact equivalence with another library's types,
we'll now be able to convert any appropriate `Constant` directly to
those types.  We even take advantage of our exact conversion policy.
  • Loading branch information
chiphogg authored Dec 11, 2023
1 parent 59ad95f commit c3061f0
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 0 deletions.
1 change: 1 addition & 0 deletions au/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ cc_test(
size = "small",
srcs = ["constant_test.cc"],
deps = [
":chrono_interop",
":constant",
":testing",
":units",
Expand Down
10 changes: 10 additions & 0 deletions au/constant.hh
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ struct Constant : detail::MakesQuantityFromNumber<Constant, Unit>,
static constexpr bool can_store_value_in(OtherUnit other) {
return representable_in<T>(unit_ratio(Unit{}, other));
}

// Implicitly convert to type with an exactly corresponding quantity that passes safety checks.
template <
typename T,
typename = std::enable_if_t<can_store_value_in<typename CorrespondingQuantity<T>::Rep>(
typename CorrespondingQuantity<T>::Unit{})>>
constexpr operator T() const {
return as<typename CorrespondingQuantity<T>::Rep>(
typename CorrespondingQuantity<T>::Unit{});
};
};

// Make a constant from the given unit.
Expand Down
19 changes: 19 additions & 0 deletions au/constant_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <sstream>

#include "au/chrono_interop.hh"
#include "au/testing.hh"
#include "au/units/joules.hh"
#include "au/units/meters.hh"
Expand Down Expand Up @@ -253,6 +254,24 @@ TEST(Constant, ImplicitlyConvertsToAppropriateQuantityTypes) {
}
}

TEST(Constant, ImplicitlyConvertsToNonAuTypesWithAppropriateCorrespondingQuantity) {
constexpr auto dt_as_constant = make_constant(seconds * mag<1'000'000'000>());
constexpr auto dt_as_quantity_u32 = dt_as_constant.as<uint32_t>();

// If we had represented this constant as a quantity, it wouldn't be able to convert to a
// quantity of seconds backed by `uint32_t`, because of the overflow safety surface. However,
// the constant value itself can be safely converted, because we know its exact value.
ASSERT_FALSE((
std::is_convertible<decltype(dt_as_quantity_u32), std::chrono::duration<uint32_t>>::value));
EXPECT_TRUE(
(std::is_convertible<decltype(dt_as_constant), std::chrono::duration<uint32_t>>::value));

// Here, the constant value itself can't be safely converted, because it's too big for a
// `uint16_t`.
EXPECT_FALSE(
(std::is_convertible<decltype(dt_as_constant), std::chrono::duration<uint16_t>>::value));
}

TEST(Constant, SupportsUnitSlotAPIs) {
constexpr auto three_c_mps = (3.f * c).as(meters / second);
EXPECT_THAT(three_c_mps.in(c), SameTypeAndValue(3.f));
Expand Down

0 comments on commit c3061f0

Please sign in to comment.