diff --git a/au/BUILD.bazel b/au/BUILD.bazel index c45e4021..a4d18db0 100644 --- a/au/BUILD.bazel +++ b/au/BUILD.bazel @@ -159,9 +159,9 @@ cc_library( hdrs = ["code/au/chrono_interop.hh"], includes = ["code"], deps = [ - ":units", ":prefix", ":quantity", + ":units", ], ) @@ -417,6 +417,7 @@ cc_library( ":operators", ":rep", ":unit_of_measure", + ":utility", ":zero", ], ) diff --git a/au/code/au/CMakeLists.txt b/au/code/au/CMakeLists.txt index 807fda6f..3973077a 100644 --- a/au/code/au/CMakeLists.txt +++ b/au/code/au/CMakeLists.txt @@ -117,6 +117,7 @@ header_only_library( operators rep unit_of_measure + utility zero GTEST_SRCS quantity_chrono_policy_correspondence_test.cc diff --git a/au/code/au/quantity.hh b/au/code/au/quantity.hh index a5723550..ace81ae3 100644 --- a/au/code/au/quantity.hh +++ b/au/code/au/quantity.hh @@ -22,6 +22,7 @@ #include "au/rep.hh" #include "au/stdx/functional.hh" #include "au/unit_of_measure.hh" +#include "au/utility/type_traits.hh" #include "au/zero.hh" namespace au { @@ -115,6 +116,8 @@ class Quantity { using Unit = UnitT; static constexpr auto unit = Unit{}; + static_assert(IsValidRep::value, "Rep must meet our requirements for a rep"); + // IMPLICIT constructor for another Quantity of the same Dimension. template ; template using QuantityU64 = Quantity; +// Forward declare `QuantityPoint` here, so that we can give better error messages when users try to +// make it into a quantity. +template +class QuantityPoint; + template struct QuantityMaker { using Unit = UnitT; @@ -491,6 +499,18 @@ struct QuantityMaker { return {value}; } + template + constexpr void operator()(Quantity) const { + constexpr bool is_not_already_a_quantity = detail::AlwaysFalse::value; + static_assert(is_not_already_a_quantity, "Input to QuantityMaker is already a Quantity"); + } + + template + constexpr void operator()(QuantityPoint) const { + constexpr bool is_not_a_quantity_point = detail::AlwaysFalse::value; + static_assert(is_not_a_quantity_point, "Input to QuantityMaker is a QuantityPoint"); + } + template constexpr auto operator*(Magnitude m) const { return QuantityMaker{}; diff --git a/au/code/au/quantity_point.hh b/au/code/au/quantity_point.hh index a8de99fa..41a07106 100644 --- a/au/code/au/quantity_point.hh +++ b/au/code/au/quantity_point.hh @@ -287,6 +287,19 @@ struct QuantityPointMaker { return QuantityPoint{make_quantity(value)}; } + template + constexpr void operator()(Quantity) const { + constexpr bool is_not_a_quantity = detail::AlwaysFalse::value; + static_assert(is_not_a_quantity, "Input to QuantityPointMaker is a Quantity"); + } + + template + constexpr void operator()(QuantityPoint) const { + constexpr bool is_not_already_a_quantity_point = detail::AlwaysFalse::value; + static_assert(is_not_already_a_quantity_point, + "Input to QuantityPointMaker is already a QuantityPoint"); + } + template constexpr auto operator*(Magnitude m) const { return QuantityPointMaker{}; diff --git a/au/code/au/utility/test/type_traits_test.cc b/au/code/au/utility/test/type_traits_test.cc index c74daff7..821bca40 100644 --- a/au/code/au/utility/test/type_traits_test.cc +++ b/au/code/au/utility/test/type_traits_test.cc @@ -44,5 +44,12 @@ TEST(SameTypeIgnoringCvref, CanTakeInstances) { EXPECT_FALSE(same_type_ignoring_cvref(1.0, 2.0f)); } +TEST(AlwaysFalse, IsAlwaysFalse) { + EXPECT_FALSE(AlwaysFalse::value); + EXPECT_FALSE(AlwaysFalse::value); + EXPECT_FALSE(AlwaysFalse<>::value); + EXPECT_FALSE((AlwaysFalse::value)); +} + } // namespace detail } // namespace au diff --git a/au/code/au/utility/type_traits.hh b/au/code/au/utility/type_traits.hh index 674bec0d..ce276788 100644 --- a/au/code/au/utility/type_traits.hh +++ b/au/code/au/utility/type_traits.hh @@ -34,6 +34,9 @@ constexpr bool same_type_ignoring_cvref(T, U) { return SameTypeIgnoringCvref::value; } +template +struct AlwaysFalse : std::false_type {}; + //////////////////////////////////////////////////////////////////////////////////////////////////// // Implementation details below.