Merge branch 'master' of github.com:mpusz/units

This commit is contained in:
Mateusz Pusz
2020-09-06 10:30:05 +02:00
9 changed files with 194 additions and 128 deletions

View File

@@ -0,0 +1,61 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <cstdint>
#include <cassert>
namespace units::detail {
constexpr std::intmax_t ipow10(std::intmax_t exp)
{
assert(exp >= 0);
if (exp == 0) return 1;
std::intmax_t result = 1;
while (exp > 0) {
result *= 10;
--exp;
}
return result;
}
template<typename Rep>
constexpr Rep fpow10(std::intmax_t exp)
{
if (exp == 0) return Rep(1.0);
Rep result = Rep(1.0);
if (exp < 0) {
while (exp < 0) {
result = result / Rep(10.0);
++exp;
}
} else {
while (exp > 0) {
result = result * Rep(10.0);
--exp;
}
}
return result;
}
} // namespace units::detail

View File

@@ -23,6 +23,7 @@
#pragma once
#include <units/bits/external/downcasting.h>
#include <units/concepts.h>
#include <units/ratio.h>
#include <units/symbol_text.h>

View File

@@ -25,6 +25,7 @@
#include <units/bits/common_quantity.h>
#include <units/bits/dimension_op.h>
#include <units/bits/pow.h>
#include <units/bits/to_string.h>
#include <units/quantity_cast.h>
@@ -342,9 +343,9 @@ template<typename D1, typename U1, typename Rep1, typename D2, typename U2, type
using common_rep = decltype(lhs.count() * rhs.count());
const ratio r = U1::ratio * U2::ratio;
if constexpr (treat_as_floating_point<common_rep>) {
return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * fpow10<common_rep>(r.exp)) / static_cast<common_rep>(r.den);
return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * detail::fpow10<common_rep>(r.exp)) / static_cast<common_rep>(r.den);
} else {
return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * ipow10(r.exp)) / static_cast<common_rep>(r.den);
return lhs.count() * rhs.count() * static_cast<common_rep>(r.num * detail::ipow10(r.exp)) / static_cast<common_rep>(r.den);
}
}

View File

@@ -26,6 +26,9 @@
#include <units/customization_points.h>
#include <units/bits/dimension_op.h>
#include <units/bits/external/type_traits.h>
#include <units/bits/pow.h>
#include <units/quantity.h>
#include <units/quantity_point.h>
#include <cassert>
#ifdef _MSC_VER
@@ -35,38 +38,6 @@
namespace units {
constexpr std::intmax_t ipow10(std::intmax_t exp)
{
assert(exp >= 0);
if (exp == 0) return 1;
std::intmax_t result = 1;
while (exp > 0) {
result *= 10;
--exp;
}
return result;
}
template<typename Rep>
constexpr Rep fpow10(std::intmax_t exp)
{
if (exp == 0) return Rep(1.0);
Rep result = Rep(1.0);
if (exp < 0) {
while (exp < 0) {
result = result / Rep(10.0);
++exp;
}
} else {
while (exp > 0) {
result = result * Rep(10.0);
--exp;
}
}
return result;
}
// QuantityOf
template<typename T, typename Dim>
concept QuantityOf = Quantity<T> && Dimension<Dim> && equivalent_dim<typename T::dimension, Dim>;
@@ -92,13 +63,13 @@ struct quantity_cast_impl<To, CRatio, CRep, true, true, false> {
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(fpow10<CRep>(CRatio.exp))));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(ipow10(CRatio.exp))));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(detail::ipow10(CRatio.exp))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(ipow10(-CRatio.exp))));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / static_cast<CRep>(detail::ipow10(-CRatio.exp))));
}
}
}
@@ -122,21 +93,21 @@ struct quantity_cast_impl<To, CRatio, CRep, false, false, false> {
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
static_cast<CRep>(fpow10<CRep>(CRatio.exp)) *
static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp)) *
(static_cast<CRep>(CRatio.num) /
static_cast<CRep>(CRatio.den))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
static_cast<CRep>(CRatio.num) *
static_cast<CRep>(ipow10(CRatio.exp)) /
static_cast<CRep>(detail::ipow10(CRatio.exp)) /
static_cast<CRep>(CRatio.den)));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) *
static_cast<CRep>(CRatio.num) /
(static_cast<CRep>(CRatio.den) *
static_cast<CRep>(ipow10(-CRatio.exp)))));
static_cast<CRep>(detail::ipow10(-CRatio.exp)))));
}
}
}
@@ -157,13 +128,13 @@ struct quantity_cast_impl<To, CRatio, CRep, true, false, false> {
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(fpow10<CRep>(CRatio.exp)) * (CRep{1} / static_cast<CRep>(CRatio.den))));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp)) * (CRep{1} / static_cast<CRep>(CRatio.den))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(ipow10(CRatio.exp)) / static_cast<CRep>(CRatio.den)));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(detail::ipow10(CRatio.exp)) / static_cast<CRep>(CRatio.den)));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / (static_cast<CRep>(ipow10(-CRatio.exp)) * static_cast<CRep>(CRatio.den))));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) / (static_cast<CRep>(detail::ipow10(-CRatio.exp)) * static_cast<CRep>(CRatio.den))));
}
}
}
@@ -184,13 +155,13 @@ struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) * static_cast<CRep>(fpow10<CRep>(CRatio.exp))));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) * static_cast<CRep>(detail::fpow10<CRep>(CRatio.exp))));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) * static_cast<CRep>(ipow10(CRatio.exp))));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) * static_cast<CRep>(detail::ipow10(CRatio.exp))));
}
else {
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) / static_cast<CRep>(ipow10(-CRatio.exp))));
return To(static_cast<TYPENAME To::rep>(static_cast<CRep>(q.count()) * static_cast<CRep>(CRatio.num) / static_cast<CRep>(detail::ipow10(-CRatio.exp))));
}
}
}
@@ -202,13 +173,13 @@ struct quantity_cast_impl<To, CRatio, CRep, true, true, false> {
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * fpow10<CRep>(CRatio.exp)));
return To(static_cast<TYPENAME To::rep>(q.count() * detail::fpow10<CRep>(CRatio.exp)));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * ipow10(CRatio.exp)));
return To(static_cast<TYPENAME To::rep>(q.count() * detail::ipow10(CRatio.exp)));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() / ipow10(-CRatio.exp)));
return To(static_cast<TYPENAME To::rep>(q.count() / detail::ipow10(-CRatio.exp)));
}
}
}
@@ -229,13 +200,13 @@ struct quantity_cast_impl<To, CRatio, CRep, false, false, false> {
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * fpow10<CRep>(CRatio.exp) * (CRatio.num / CRatio.den)));
return To(static_cast<TYPENAME To::rep>(q.count() * detail::fpow10<CRep>(CRatio.exp) * (CRatio.num / CRatio.den)));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num * ipow10(CRatio.exp) / CRatio.den));
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num * detail::ipow10(CRatio.exp) / CRatio.den));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count()) * CRatio.num / (CRatio.den * ipow10(-CRatio.exp)));
return To(static_cast<TYPENAME To::rep>(q.count()) * CRatio.num / (CRatio.den * detail::ipow10(-CRatio.exp)));
}
}
}
@@ -256,13 +227,13 @@ struct quantity_cast_impl<To, CRatio, CRep, true, false, false> {
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * fpow10<CRep>(CRatio.exp) / CRatio.den));
return To(static_cast<TYPENAME To::rep>(q.count() * detail::fpow10<CRep>(CRatio.exp) / CRatio.den));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * ipow10(CRatio.exp) / CRatio.den));
return To(static_cast<TYPENAME To::rep>(q.count() * detail::ipow10(CRatio.exp) / CRatio.den));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() / (ipow10(-CRatio.exp) * CRatio.den)));
return To(static_cast<TYPENAME To::rep>(q.count() / (detail::ipow10(-CRatio.exp) * CRatio.den)));
}
}
}
@@ -283,13 +254,13 @@ struct quantity_cast_impl<To, CRatio, CRep, false, true, false> {
static constexpr To cast(const Q& q)
{
if constexpr (treat_as_floating_point<CRep>) {
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num * fpow10<CRep>(CRatio.exp)));
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num * detail::fpow10<CRep>(CRatio.exp)));
} else {
if constexpr (CRatio.exp > 0) {
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num * ipow10(CRatio.exp)));
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num * detail::ipow10(CRatio.exp)));
}
else {
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num / ipow10(-CRatio.exp)));
return To(static_cast<TYPENAME To::rep>(q.count() * CRatio.num / detail::ipow10(-CRatio.exp)));
}
}
}

View File

@@ -25,6 +25,7 @@
#include "units/physical/si/frequency.h"
#include "units/physical/si/speed.h"
#include <chrono>
#include <type_traits>
#include <utility>
using namespace units;
@@ -65,7 +66,7 @@ template<typename T>
struct expl_constructible : scalar_ops<expl_constructible<T>> {
T value_{};
expl_constructible() = default;
constexpr expl_constructible(T v) : value_(std::move(v)) {}
constexpr explicit expl_constructible(T v) : value_(std::move(v)) {}
// no conversion to fundamental arithmetic types
};
@@ -172,60 +173,60 @@ using namespace units::physical::si;
// Quantity from Scalar
// int <- int
static_assert(length<metre, int>(expl_impl<int>(1)).count() == 1);
// static_assert(length<metre, int>(impl_expl<int>(1)).count() == 1); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, int>, impl_expl<int>>);
static_assert(length<metre, int>(int(impl_expl<int>(1))).count() == 1);
// static_assert(length<metre, expl_impl<int>>(1).count() == expl_impl<int>{1}); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, expl_impl<int>>, int>);
static_assert(length<metre, expl_impl<int>>(expl_impl<int>(1)).count() == expl_impl<int>{1});
static_assert(length<metre, impl_expl<int>>(1).count() == impl_expl<int>{1});
// double <- double
static_assert(length<metre, double>(expl_impl<double>(1.0)).count() == 1.0);
// static_assert(length<metre, double>(impl_expl<double>(1.0)).count() == 1.0); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, double>, impl_expl<double>>);
static_assert(length<metre, double>(double(impl_expl<double>(1.0))).count() == 1.0);
// static_assert(length<metre, expl_impl<double>>(1.0).count() == expl_impl<double>{1.0}); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, double>);
static_assert(length<metre, expl_impl<double>>(expl_impl<double>(1.0)).count() == expl_impl<double>{1.0});
static_assert(length<metre, impl_expl<double>>(1.0).count() == impl_expl<double>{1.0});
// double <- int
static_assert(length<metre, double>(expl_impl<int>(1)).count() == 1.0);
// static_assert(length<metre, double>(impl_expl<int>(1)).count() == 1.0); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, double>, impl_expl<int>>);
static_assert(length<metre, double>(int(impl_expl<int>(1))).count() == 1.0);
// static_assert(length<metre, expl_impl<double>>(1).count() == expl_impl<double>{1}); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, int>);
static_assert(length<metre, expl_impl<double>>(expl_impl<double>(1)).count() == expl_impl<double>{1});
static_assert(length<metre, impl_expl<double>>(1).count() == impl_expl<double>{1.0});
// int <- double
// static_assert(length<metre, int>(expl_impl<double>(1.0)).count() == 1); // should not compile (truncating conversion)
// static_assert(length<metre, impl_expl<int>>(1.0).count() == impl_expl<int>{1}); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<metre, int>, expl_impl<double>>);
static_assert(!std::is_constructible_v<length<metre, impl_expl<int>>, double>);
// Quantity from other Quantity with different Rep
// int <- int
static_assert(length<metre, int>(length<metre, expl_impl<int>>(expl_impl<int>(1))).count() == 1);
// static_assert(length<metre, int>(length<metre, impl_expl<int>>(1)).count() == 1); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, int>, length<metre, impl_expl<int>>>);
static_assert(length<metre, int>(quantity_cast<int>(length<metre, impl_expl<int>>(1))).count() == 1);
// static_assert(length<metre, expl_impl<int>>(length<metre, int>(1)).count() == expl_impl<int>{1}); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, expl_impl<int>>, length<metre, int>>);
static_assert(length<metre, expl_impl<int>>(quantity_cast<expl_impl<int>>(length<metre, int>(1))).count() == expl_impl<int>{1});
static_assert(length<metre, impl_expl<int>>(length<metre, int>(1)).count() == impl_expl<int>{1});
// double <- double
static_assert(length<metre, double>(length<metre, expl_impl<double>>(expl_impl<double>(1.0))).count() == 1.0);
// static_assert(length<metre, double>(length<metre, impl_expl<double>>(1.0)).count() == 1.0); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, double>, length<metre, impl_expl<double>>>);
static_assert(length<metre, double>(quantity_cast<double>(length<metre, impl_expl<double>>(1.0))).count() == 1.0);
// static_assert(length<metre, expl_impl<double>>(length<metre>(1.0).count() == expl_impl<double>{1.0}); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, length<metre, double>>);
static_assert(length<metre, expl_impl<double>>(quantity_cast<expl_impl<double>>(length<metre>(1.0))).count() == expl_impl<double>{1.0});
static_assert(length<metre, impl_expl<double>>(length<metre>(1.0)).count() == impl_expl<double>{1.0});
// double <- int
static_assert(length<metre, double>(length<metre, expl_impl<int>>(expl_impl<int>(1))).count() == 1.0);
// static_assert(length<metre, double>(length<metre, impl_expl<int>>(1)).count() == 1.0); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, double>, length<metre, impl_expl<int>>>);
static_assert(length<metre, double>(quantity_cast<int>(length<metre, impl_expl<int>>(1))).count() == 1.0);
// static_assert(length<metre, expl_impl<double>>(length<metre, int>(1)).count() == expl_impl<double>{1}); // should not compile (not convertible)
static_assert(!std::is_constructible_v<length<metre, expl_impl<double>>, length<metre, int>>);
static_assert(length<metre, expl_impl<double>>(quantity_cast<expl_impl<double>>(length<metre, int>(1))).count() == expl_impl<double>{1});
static_assert(length<metre, impl_expl<double>>(length<metre, int>(1)).count() == impl_expl<double>{1.0});
// int <- double
// static_assert(length<metre, int>(length<metre, expl_impl<double>>(1.0)).count() == 1); // should not compile (truncating conversion)
// static_assert(length<metre, impl_expl<int>>(length<metre, double>(1.0)).count() == impl_expl<int>{1}); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<metre, int>, length<metre, expl_impl<double>>>);
static_assert(!std::is_constructible_v<length<metre, impl_expl<int>>, length<metre, double>>);
// unit conversions
@@ -236,43 +237,43 @@ static_assert(length<metre, impl_expl<int>>(length<kilometre, impl_expl<int>>(1)
static_assert(length<metre, expl_impl<int>>(length<kilometre, expl_impl<int>>(expl_impl<int>(1))).count() == expl_impl<int>(1000));
static_assert(length<metre, expl_expl<int>>(length<kilometre, expl_expl<int>>(expl_expl<int>(1))).count() == expl_expl<int>(1000));
// static_assert(length<kilometre, impl<int>>(length<metre, impl<int>>(2000)).count() == impl<int>(2)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<kilometre, impl<int>>, length<metre, impl<int>>>);
static_assert(length<kilometre, impl<int>>(quantity_cast<kilometre>(length<metre, impl<int>>(2000))).count() == impl<int>(2));
// static_assert(length<kilometre, expl<int>>(length<metre, expl<int>>(expl<int>(2000))).count() == expl<int>(2)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<kilometre, expl<int>>, length<metre, expl<int>>>);
static_assert(length<kilometre, expl<int>>(quantity_cast<kilometre>(length<metre, expl<int>>(expl<int>(2000)))).count() == expl<int>(2));
// static_assert(length<kilometre, impl_impl<int>>(length<metre, impl_impl<int>>(2000)).count() == impl_impl<int>(2)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<kilometre, impl_impl<int>>, length<metre, impl_impl<int>>>);
static_assert(length<kilometre, impl_impl<int>>(quantity_cast<kilometre>(length<metre, impl_impl<int>>(2000))).count() == impl_impl<int>(2));
// static_assert(length<kilometre, impl_expl<int>>(length<metre, impl_expl<int>>(2000)).count() == impl_expl<int>(2)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<kilometre, impl_expl<int>>, length<metre, impl_expl<int>>>);
static_assert(length<kilometre, impl_expl<int>>(quantity_cast<kilometre>(length<metre, impl_expl<int>>(2000))).count() == impl_expl<int>(2));
// static_assert(length<kilometre, expl_impl<int>>(length<metre, expl_impl<int>>(expl_impl<int>(2000))).count() == expl_impl<int>(2)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<kilometre, expl_impl<int>>, length<metre, expl_impl<int>>>);
static_assert(length<kilometre, expl_impl<int>>(quantity_cast<kilometre>(length<metre, expl_impl<int>>(expl_impl<int>(2000)))).count() == expl_impl<int>(2));
// static_assert(length<kilometre, expl_expl<int>>(length<metre, expl_expl<int>>(expl_expl<int>(2000))).count() == expl_expl<int>(2)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<kilometre, expl_expl<int>>, length<metre, expl_expl<int>>>);
static_assert(length<kilometre, expl_expl<int>>(quantity_cast<kilometre>(length<metre, expl_expl<int>>(expl_expl<int>(2000)))).count() == expl_expl<int>(2));
// static_assert(speed<metre_per_second, impl<int>>(speed<kilometre_per_hour, impl<int>>(72)).count() == impl<int>(20)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<metre_per_second, impl<int>>, speed<kilometre_per_hour, impl<int>>>);
static_assert(speed<metre_per_second, impl<int>>(quantity_cast<metre_per_second>(speed<kilometre_per_hour, impl<int>>(72))).count() == impl<int>(20));
// static_assert(speed<metre_per_second, expl<int>>(speed<kilometre_per_hour, expl<int>>(expl(72))).count() == expl<int>(20)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<metre_per_second, expl<int>>, speed<kilometre_per_hour, expl<int>>>);
static_assert(speed<metre_per_second, expl<int>>(quantity_cast<metre_per_second>(speed<kilometre_per_hour, expl<int>>(expl<int>(72)))).count() == expl<int>(20));
// static_assert(speed<metre_per_second, impl_impl<int>>(speed<kilometre_per_hour, impl_impl<int>>(72)).count() == impl_impl<int>(20)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<metre_per_second, impl_impl<int>>, speed<kilometre_per_hour, impl_impl<int>>>);
static_assert(speed<metre_per_second, impl_impl<int>>(quantity_cast<metre_per_second>(speed<kilometre_per_hour, impl_impl<int>>(72))).count() == impl_impl<int>(20));
// static_assert(speed<metre_per_second, impl_expl<int>>(speed<kilometre_per_hour, impl_expl<int>>(72)).count() == impl_expl<int>(20)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<metre_per_second, impl_expl<int>>, speed<kilometre_per_hour, impl_expl<int>>>);
static_assert(speed<metre_per_second, impl_expl<int>>(quantity_cast<metre_per_second>(speed<kilometre_per_hour, impl_expl<int>>(72))).count() == impl_expl<int>(20));
// static_assert(speed<metre_per_second, expl_impl<int>>(speed<kilometre_per_hour, expl_impl<int>>(expl_impl(72))).count() == expl_impl<int>(20)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<metre_per_second, expl_impl<int>>, speed<kilometre_per_hour, expl_impl<int>>>);
static_assert(speed<metre_per_second, expl_impl<int>>(quantity_cast<metre_per_second>(speed<kilometre_per_hour, expl_impl<int>>(expl_impl<int>(72)))).count() == expl_impl<int>(20));
// static_assert(speed<metre_per_second, expl_expl<int>>(speed<kilometre_per_hour, expl_expl<int>>(expl_expl(72))).count() == expl_expl<int>(20)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<metre_per_second, expl_expl<int>>, speed<kilometre_per_hour, expl_expl<int>>>);
static_assert(speed<metre_per_second, expl_expl<int>>(quantity_cast<metre_per_second>(speed<kilometre_per_hour, expl_expl<int>>(expl_expl<int>(72)))).count() == expl_expl<int>(20));
// static_assert(speed<kilometre_per_hour, impl<int>>(speed<metre_per_second, impl<int>>(20)).count() == impl<int>(72)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<kilometre_per_hour, impl<int>>, speed<metre_per_second, impl<int>>>);
static_assert(speed<kilometre_per_hour, impl<int>>(quantity_cast<kilometre_per_hour>(speed<metre_per_second, impl<int>>(20))).count() == impl<int>(72));
// static_assert(speed<kilometre_per_hour, expl<int>>(speed<metre_per_second, expl<int>>(expl<int>(20))).count() == expl<int>(72)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<kilometre_per_hour, expl<int>>, speed<metre_per_second, expl<int>>>);
static_assert(speed<kilometre_per_hour, expl<int>>(quantity_cast<kilometre_per_hour>(speed<metre_per_second, expl<int>>(expl<int>(20)))).count() == expl<int>(72));
// static_assert(speed<kilometre_per_hour, impl_impl<int>>(speed<metre_per_second, impl_impl<int>>(20)).count() == impl_impl<int>(72)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<kilometre_per_hour, impl_impl<int>>, speed<metre_per_second, impl_impl<int>>>);
static_assert(speed<kilometre_per_hour, impl_impl<int>>(quantity_cast<kilometre_per_hour>(speed<metre_per_second, impl_impl<int>>(20))).count() == impl_impl<int>(72));
// static_assert(speed<kilometre_per_hour, impl_expl<int>>(speed<metre_per_second, impl_expl<int>>(20)).count() == impl_expl<int>(72)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<kilometre_per_hour, impl_expl<int>>, speed<metre_per_second, impl_expl<int>>>);
static_assert(speed<kilometre_per_hour, impl_expl<int>>(quantity_cast<kilometre_per_hour>(speed<metre_per_second, impl_expl<int>>(20))).count() == impl_expl<int>(72));
// static_assert(speed<kilometre_per_hour, expl_impl<int>>(speed<metre_per_second, expl_impl<int>>(expl_impl<int>(20))).count() == expl_impl<int>(72)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<kilometre_per_hour, expl_impl<int>>, speed<metre_per_second, expl_impl<int>>>);
static_assert(speed<kilometre_per_hour, expl_impl<int>>(quantity_cast<kilometre_per_hour>(speed<metre_per_second, expl_impl<int>>(expl_impl<int>(20)))).count() == expl_impl<int>(72));
// static_assert(speed<kilometre_per_hour, expl_expl<int>>(speed<metre_per_second, expl_expl<int>>(expl_expl<int>(20))).count() == expl_expl<int>(72)); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<speed<kilometre_per_hour, expl_expl<int>>, speed<metre_per_second, expl_expl<int>>>);
static_assert(speed<kilometre_per_hour, expl_expl<int>>(quantity_cast<kilometre_per_hour>(speed<metre_per_second, expl_expl<int>>(expl_expl<int>(20)))).count() == expl_expl<int>(72));
} // namespace

View File

@@ -47,7 +47,7 @@ concept invalid_types = requires
!requires { typename quantity<metre, DimLength, double>; }; // reordered arguments
};
static_assert(invalid_types<physical::si::dim_length>);
static_assert(invalid_types<dim_length>);
// member types
@@ -226,12 +226,16 @@ static_assert(quantity_point_cast<int>(quantity_point(1.23q_m)).relative().count
// time
#if COMP_MSVC || COMP_GCC >= 10
static_assert(!std::equality_comparable_with<quantity_point<dim_time, second, int>,
quantity_point<dim_length, metre, int>>); // different dimensions
#endif
static_assert(quantity_point{1q_h} == quantity_point{3600q_s});
template<typename Metre>
constexpr bool no_crossdimensional_equality = !requires
{
quantity_point(1q_s) == quantity_point(length<Metre, int>(1));
};
static_assert(no_crossdimensional_equality<metre>);
// length
static_assert(quantity_point(1q_km) == quantity_point(1000q_m));

View File

@@ -36,10 +36,15 @@ using namespace units::physical::si;
// class invariants
// constexpr quantity<si::dim_length, second, int> error(0); // should not compile (unit of a different dimension)
// constexpr quantity<si::dim_length, metre, quantity<si::dim_length, metre, int>> error(0); // should not compile (quantity used as Rep)
// constexpr quantity<metre, si::dim_length, double> error(0); // should not compile (reordered arguments)
// constexpr quantity<si::dim_length, scaled_unit<ratio(-1, 1), metre>, int> error(0); // should not compile (negative unit ratio)
template<typename DimLength>
concept invalid_types = requires
{
!requires { typename quantity<DimLength, second, int>; }; // unit of a different dimension
!requires { typename quantity<DimLength, metre, quantity<DimLength, metre, int>>; }; // quantity used as Rep
!requires { typename quantity<metre, DimLength, double>; }; // reordered arguments
};
static_assert(invalid_types<dim_length>);
// member types
@@ -56,18 +61,21 @@ static_assert(km.count() == 1000);
static_assert(length<metre, int>(km).count() == km.count());
static_assert(length<metre, int>(1).count() == 1);
// static_assert(length<metre, int>(1.0).count() == 1); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<metre, int>, double>); // truncating conversion
static_assert(length<metre, double>(1.0).count() == 1.0);
static_assert(length<metre, double>(1).count() == 1.0);
static_assert(length<metre, double>(3.14).count() == 3.14);
static_assert(length<metre, int>(km).count() == 1000);
// static_assert(length<metre, int>(length<metre, double>(3.14)).count() == 3); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<metre, int>,
length<metre, double>>); // truncating conversion
static_assert(length<metre, double>(1000.0q_m).count() == 1000.0);
static_assert(length<metre, double>(km).count() == 1000.0);
static_assert(length<metre, int>(1q_km).count() == 1000);
// static_assert(length<metre, int>(1q_s).count() == 1); // should not compile (different dimensions)
//static_assert(length<kilometre, int>(1010q_m).count() == 1); // should not compile (truncating conversion)
static_assert(!std::is_constructible_v<length<metre, int>,
physical::si::time<second, int>>); // different dimensions
static_assert(!std::is_constructible_v<length<kilometre, int>,
length<metre, int>>); // truncating conversion
// assignment operator
@@ -89,24 +97,22 @@ static_assert((-km).count() == -1000);
static_assert((+(-km)).count() == -1000);
static_assert((-(-km)).count() == 1000);
// binary member operators
static_assert([](auto v) {
auto vv = v++;
return std::make_pair(v, vv);
}(km) == std::make_pair(length<metre, int>(1001), length<metre, int>(1000)));
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(1001), length<metre, int>(1000)));
static_assert([](auto v) {
auto vv = ++v;
return std::make_pair(v, vv);
}(km) == std::make_pair(length<metre, int>(1001), length<metre, int>(1001)));
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(1001), length<metre, int>(1001)));
static_assert([](auto v) {
auto vv = v--;
return std::make_pair(v, vv);
}(km) == std::make_pair(length<metre, int>(999), length<metre, int>(1000)));
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(999), length<metre, int>(1000)));
static_assert([](auto v) {
auto vv = --v;
return std::make_pair(v, vv);
}(km) == std::make_pair(length<metre, int>(999), length<metre, int>(999)));
return std::pair(v, vv);
}(km) == std::pair(length<metre, int>(999), length<metre, int>(999)));
// compound assignment
@@ -116,22 +122,29 @@ static_assert((1q_m *= 2).count() == 2);
static_assert((2q_m /= 2).count() == 1);
static_assert((7q_m %= 2).count() == 1);
static_assert((7q_m %= 2q_m).count() == 1);
// static_assert((7.m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types)
// static_assert((7.m %= 2).count() == 1); // should not compile (operation not allowed for floating-point types)
// static_assert((7q_m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types)
static_assert((7q_m %= 2q_m).count() == 1);
// static_assert((7.m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types)
// static_assert((7.m %= 2q_m).count() == 1); // should not compile (operation not allowed for floating-point types)
// static_assert((7q_m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types)
// static_assert(2q_m += 3.5q_m); // should not compile
static_assert((2.5q_m += 3q_m).count() == 5.5);
static_assert((2.5q_m += 3.5q_m).count() == 6);
// static_assert(2q_m *= 3.5); // should not compile
static_assert((2.5q_m *= 3).count() == 7.5);
static_assert((2.5q_m *= 3.5).count() == 8.75);
// operations not allowed for the respective quantities
template<typename Metre>
concept invalid_compound_assignments = requires()
{
!requires(length<Metre, double> l) { l %= 2.; };
!requires(length<Metre, double> l) { l %= 2; };
!requires(length<Metre, int> l) { l %= 2.; };
!requires(length<Metre, double> l) { l %= 2.q_m; };
!requires(length<Metre, double> l) { l %= 2q_m; };
!requires(length<Metre, int> l) { l %= 2.q_m; };
!requires(length<Metre, int> l) { l += 3.5q_m; };
!requires(length<Metre, int> l) { l *= 3.5q_m; };
};
static_assert(invalid_compound_assignments<metre>);
// non-member arithmetic operators
static_assert(is_same_v<decltype(length<metre, int>() + length<metre, double>()), length<metre, double>>);
@@ -253,9 +266,13 @@ static_assert(quantity_cast<int>(1.23q_m).count() == 1);
// time
// static_assert(1q_s == 1q_m); // should not compile (different dimensions)
static_assert(1q_h == 3600q_s);
template<typename Metre>
constexpr bool no_crossdimensional_equality = !requires { 1q_s == length<Metre, int>(1); };
static_assert(no_crossdimensional_equality<metre>);
// length
static_assert(1q_km == 1000q_m);

View File

@@ -29,7 +29,6 @@ using namespace units;
static_assert(ratio(2, 4) == ratio(1, 2));
// basic exponents tests
// note use of ::type is required because template params are changed while stamping out template
static_assert(ratio(2, 40, 1) == ratio(1, 20, 1));
static_assert(ratio(20, 4, -1) == ratio(10, 2, -1));
static_assert(ratio(200, 5) == ratio(20'000, 50, -1));
@@ -89,4 +88,10 @@ static_assert(common_ratio(ratio(100, 1), ratio(1, 10)) == ratio(1, 10));
static_assert(common_ratio(ratio(1), ratio(1, 1, 3)) == ratio(1));
static_assert(common_ratio(ratio(10, 1, -1), ratio(1, 1, -3)) == ratio(1, 1, -3));
// nonzero denominator
template <auto> struct require_constant; // [range.split.view]
template <auto F> concept constant = requires { typename require_constant<F()>; };
static_assert(!constant<[] { ratio(1, 0); }>);
} // namespace

View File

@@ -40,7 +40,9 @@ struct hour : named_scaled_unit<hour, "h", no_prefix, ratio(36, 1, 2), second> {
struct dim_time : base_dimension<"time", second> {};
struct kelvin : named_unit<kelvin, "K", no_prefix> {};
// struct kilokelvin : prefixed_unit<kilokelvin, si::kilo, kelvin> {}; // should not compile (prefix not allowed for this reference unit)
#if COMP_MSVC || COMP_GCC >= 10
static_assert([]<Prefix P>(P) { return !requires { typename prefixed_unit<struct kilokelvin, P, kelvin>; }; }(si::kilo{})); // negative unit ratio
#endif
struct metre_per_second : unit<metre_per_second> {};
struct dim_speed : derived_dimension<dim_speed, metre_per_second, units::exp<dim_length, 1>, units::exp<dim_time, -1>> {};
@@ -51,6 +53,9 @@ static_assert(is_same_v<downcast<scaled_unit<ratio(1, 1, -2), metre>>, centimetr
static_assert(is_same_v<downcast<scaled_unit<ratio(yard::ratio.num, yard::ratio.den, yard::ratio.exp), metre>>, yard>);
static_assert(is_same_v<downcast<scaled_unit<yard::ratio * ratio(1, 3), metre>>, foot>);
static_assert(is_same_v<downcast<scaled_unit<kilometre::ratio / hour::ratio, metre_per_second>>, kilometre_per_hour>);
#if COMP_MSVC || COMP_GCC >= 10
static_assert([]<ratio R>() { return !requires { typename scaled_unit<R, metre>; }; }.template operator()<ratio(-1, 1)>()); // negative unit ratio
#endif
static_assert(centimetre::symbol == "cm");
static_assert(kilometre::symbol == "km");