Skip to content

Commit da78a0e

Browse files
committed
improve Number-double implicit conversion
1 parent 27b738c commit da78a0e

File tree

2 files changed

+105
-57
lines changed

2 files changed

+105
-57
lines changed

include/units/units.hpp

Lines changed: 104 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -548,96 +548,143 @@ NEW_UNIT(Luminosity, candela, 0, 0, 0, 0, 0, 0, 1, 0);
548548
NEW_UNIT(Moles, mol, 0, 0, 0, 0, 0, 0, 0, 1);
549549

550550
namespace units {
551-
template <isQuantity Q> constexpr Q abs(const Q& lhs) { return Q(std::abs(lhs.internal())); }
552-
553-
template <isQuantity Q, isQuantity R> constexpr Q max(const Q& lhs, const R& rhs)
554-
requires Isomorphic<Q, R>
555-
{
556-
return (lhs > rhs ? lhs : rhs);
551+
// Helper: if T is arithmetic, convert it to Number; otherwise leave it unchanged.
552+
template <typename T> constexpr auto to_quantity(T value) -> std::conditional_t<std::is_arithmetic_v<T>, Number, T> {
553+
if constexpr (std::is_arithmetic_v<T>) return Number(value);
554+
else return value;
557555
}
558556

559-
template <isQuantity Q, isQuantity R> constexpr Q min(const Q& lhs, const R& rhs)
560-
requires Isomorphic<Q, R>
561-
{
562-
return (lhs < rhs ? lhs : rhs);
557+
// abs: works with either a quantity or an arithmetic value.
558+
template <typename T> constexpr auto abs(const T& lhs) {
559+
auto q = to_quantity(lhs);
560+
using Q = decltype(q);
561+
return Q(std::abs(q.internal()));
563562
}
564563

565-
template <isQuantity Q> constexpr Number sgn(const Q& lhs) {
566-
if (lhs.internal() > 0) return 1;
567-
if (lhs.internal() < 0) return -1;
568-
return 0;
564+
// max: converts both arguments; a static_assert ensures the resulting quantities are isomorphic.
565+
template <typename T, typename U> constexpr auto max(const T& lhs, const U& rhs) {
566+
auto qlhs = to_quantity(lhs);
567+
auto qrhs = to_quantity(rhs);
568+
static_assert(Isomorphic<decltype(qlhs), decltype(qrhs)>, "max: Quantities must be isomorphic");
569+
return (qlhs > qrhs ? qlhs : qrhs);
569570
}
570571

571-
template <int R, isQuantity Q, isQuantity S = Exponentiated<Q, std::ratio<R>>> constexpr S pow(const Q& lhs) {
572-
return S(std::pow(lhs.internal(), R));
572+
// min: similar to max.
573+
template <typename T, typename U> constexpr auto min(const T& lhs, const U& rhs) {
574+
auto qlhs = to_quantity(lhs);
575+
auto qrhs = to_quantity(rhs);
576+
static_assert(Isomorphic<decltype(qlhs), decltype(qrhs)>, "min: Quantities must be isomorphic");
577+
return (qlhs < qrhs ? qlhs : qrhs);
573578
}
574579

575-
template <isQuantity Q, isQuantity S = Exponentiated<Q, std::ratio<2>>> constexpr S square(const Q& lhs) {
576-
return pow<2>(lhs);
580+
// sgn: always returns a Number.
581+
template <typename T> constexpr auto sgn(const T& lhs) {
582+
auto q = to_quantity(lhs);
583+
if (q.internal() > 0) return Number(1);
584+
if (q.internal() < 0) return Number(-1);
585+
return Number(0);
577586
}
578587

579-
template <isQuantity Q, isQuantity S = Exponentiated<Q, std::ratio<3>>> constexpr S cube(const Q& lhs) {
580-
return pow<3>(lhs);
588+
// pow: now accepts an arithmetic or quantity value.
589+
template <int R, typename T> constexpr auto pow(const T& lhs) {
590+
auto q = to_quantity(lhs);
591+
using Q = decltype(q);
592+
using S = Exponentiated<Q, std::ratio<R>>;
593+
return S(std::pow(q.internal(), R));
581594
}
582595

583-
template <int R, isQuantity Q, isQuantity S = Rooted<Q, std::ratio<R>>> constexpr S root(const Q& lhs) {
584-
return S(std::pow(lhs.internal(), 1.0 / R));
596+
// square and cube call pow with 2 or 3.
597+
template <typename T> constexpr auto square(const T& lhs) { return pow<2>(lhs); }
598+
599+
template <typename T> constexpr auto cube(const T& lhs) { return pow<3>(lhs); }
600+
601+
// root: similarly for roots.
602+
template <int R, typename T> constexpr auto root(const T& lhs) {
603+
auto q = to_quantity(lhs);
604+
using Q = decltype(q);
605+
using S = Rooted<Q, std::ratio<R>>;
606+
return S(std::pow(q.internal(), 1.0 / R));
585607
}
586608

587-
template <isQuantity Q, isQuantity S = Rooted<Q, std::ratio<2>>> constexpr S sqrt(const Q& lhs) { return root<2>(lhs); }
609+
template <typename T> constexpr auto sqrt(const T& lhs) { return root<2>(lhs); }
588610

589-
template <isQuantity Q, isQuantity S = Rooted<Q, std::ratio<3>>> constexpr S cbrt(const Q& lhs) { return root<3>(lhs); }
611+
template <typename T> constexpr auto cbrt(const T& lhs) { return root<3>(lhs); }
590612

591-
template <isQuantity Q, isQuantity R> constexpr Q hypot(const Q& lhs, const R& rhs)
592-
requires Isomorphic<Q, R>
593-
{
594-
return Q(std::hypot(lhs.internal(), rhs.internal()));
613+
// hypot: requires the two quantities be isomorphic.
614+
template <typename T, typename U> constexpr auto hypot(const T& lhs, const U& rhs) {
615+
auto qlhs = to_quantity(lhs);
616+
auto qrhs = to_quantity(rhs);
617+
static_assert(Isomorphic<decltype(qlhs), decltype(qrhs)>, "hypot: Quantities must be isomorphic");
618+
return decltype(qlhs)(std::hypot(qlhs.internal(), qrhs.internal()));
595619
}
596620

597-
template <isQuantity Q, isQuantity R> constexpr Q mod(const Q& lhs, const R& rhs)
598-
requires Isomorphic<Q, R>
599-
{
600-
return Q(std::fmod(lhs.internal(), rhs.internal()));
621+
// mod: using std::fmod.
622+
template <typename T, typename U> constexpr auto mod(const T& lhs, const U& rhs) {
623+
auto qlhs = to_quantity(lhs);
624+
auto qrhs = to_quantity(rhs);
625+
static_assert(Isomorphic<decltype(qlhs), decltype(qrhs)>, "mod: Quantities must be isomorphic");
626+
return decltype(qlhs)(std::fmod(qlhs.internal(), qrhs.internal()));
601627
}
602628

603-
template <isQuantity Q, isQuantity R> constexpr Q remainder(const Q& lhs, const R& rhs) {
604-
return Q(std::remainder(lhs.internal(), rhs.internal()));
629+
// remainder: using std::remainder.
630+
template <typename T, typename U> constexpr auto remainder(const T& lhs, const U& rhs) {
631+
auto qlhs = to_quantity(lhs);
632+
auto qrhs = to_quantity(rhs);
633+
return decltype(qlhs)(std::remainder(qlhs.internal(), qrhs.internal()));
605634
}
606635

607-
template <isQuantity Q1, isQuantity Q2> constexpr Q1 copysign(const Q1& lhs, const Q2& rhs) {
608-
return Q1(std::copysign(lhs.internal(), rhs.internal()));
636+
// copysign: applies std::copysign.
637+
template <typename T, typename U> constexpr auto copysign(const T& lhs, const U& rhs) {
638+
auto qlhs = to_quantity(lhs);
639+
auto qrhs = to_quantity(rhs);
640+
return decltype(qlhs)(std::copysign(qlhs.internal(), qrhs.internal()));
609641
}
610642

611-
template <isQuantity Q> constexpr bool signbit(const Q& lhs) { return std::signbit(lhs.internal()); }
643+
// signbit: returns a bool.
644+
template <typename T> constexpr auto signbit(const T& lhs) {
645+
auto q = to_quantity(lhs);
646+
return std::signbit(q.internal());
647+
}
612648

613-
template <isQuantity Q, isQuantity R, isQuantity S> constexpr Q clamp(const Q& lhs, const R& lo, const S& hi)
614-
requires Isomorphic<Q, R, S>
615-
{
616-
return Q(std::clamp(lhs.internal(), lo.internal(), hi.internal()));
649+
// clamp: requires three isomorphic quantities.
650+
template <typename T, typename U, typename V> constexpr auto clamp(const T& lhs, const U& lo, const V& hi) {
651+
auto qlhs = to_quantity(lhs);
652+
auto qlo = to_quantity(lo);
653+
auto qhi = to_quantity(hi);
654+
static_assert(Isomorphic<decltype(qlhs), decltype(qlo), decltype(qhi)>, "clamp: Quantities must be isomorphic");
655+
return decltype(qlhs)(std::clamp(qlhs.internal(), qlo.internal(), qhi.internal()));
617656
}
618657

619-
template <isQuantity Q, isQuantity R> constexpr Q ceil(const Q& lhs, const R& rhs)
620-
requires Isomorphic<Q, R>
621-
{
622-
return Q(std::ceil(lhs.internal() / rhs.internal()) * rhs.internal());
658+
// ceil: rounds up to a multiple of rhs.
659+
template <typename T, typename U> constexpr auto ceil(const T& lhs, const U& rhs) {
660+
auto qlhs = to_quantity(lhs);
661+
auto qrhs = to_quantity(rhs);
662+
static_assert(Isomorphic<decltype(qlhs), decltype(qrhs)>, "ceil: Quantities must be isomorphic");
663+
return decltype(qlhs)(std::ceil(qlhs.internal() / qrhs.internal()) * qrhs.internal());
623664
}
624665

625-
template <isQuantity Q, isQuantity R> constexpr Q floor(const Q& lhs, const R& rhs)
626-
requires Isomorphic<Q, R>
627-
{
628-
return Q(std::floor(lhs.internal() / rhs.internal()) * rhs.internal());
666+
// floor: rounds down.
667+
template <typename T, typename U> constexpr auto floor(const T& lhs, const U& rhs) {
668+
auto qlhs = to_quantity(lhs);
669+
auto qrhs = to_quantity(rhs);
670+
static_assert(Isomorphic<decltype(qlhs), decltype(qrhs)>, "floor: Quantities must be isomorphic");
671+
return decltype(qlhs)(std::floor(qlhs.internal() / qrhs.internal()) * qrhs.internal());
629672
}
630673

631-
template <isQuantity Q, isQuantity R> constexpr Q trunc(const Q& lhs, const R& rhs)
632-
requires Isomorphic<Q, R>
633-
{
634-
return Q(std::trunc(lhs.internal() / rhs.internal()) * rhs.internal());
674+
// trunc: rounds toward zero.
675+
template <typename T, typename U> constexpr auto trunc(const T& lhs, const U& rhs) {
676+
auto qlhs = to_quantity(lhs);
677+
auto qrhs = to_quantity(rhs);
678+
static_assert(Isomorphic<decltype(qlhs), decltype(qrhs)>, "trunc: Quantities must be isomorphic");
679+
return decltype(qlhs)(std::trunc(qlhs.internal() / qrhs.internal()) * qrhs.internal());
635680
}
636681

637-
template <isQuantity Q, isQuantity R> constexpr Q round(const Q& lhs, const R& rhs)
638-
requires Isomorphic<Q, R>
639-
{
640-
return Q(std::round(lhs.internal() / rhs.internal()) * rhs.internal());
682+
// round: rounds to the nearest.
683+
template <typename T, typename U> constexpr auto round(const T& lhs, const U& rhs) {
684+
auto qlhs = to_quantity(lhs);
685+
auto qrhs = to_quantity(rhs);
686+
static_assert(Isomorphic<decltype(qlhs), decltype(qrhs)>, "round: Quantities must be isomorphic");
687+
return decltype(qlhs)(std::round(qlhs.internal() / qrhs.internal()) * qrhs.internal());
641688
}
642689
} // namespace units
643690

src/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ void initialize() {
1717
std::cout << std::format("{:.2f}", a) << std::endl;
1818
units::Vector2D<Length> v2a = units::V2Position(2_in, 2_in) / 2;
1919
std::cout << std::format("{}", v2a) << std::endl;
20+
units::max(2, 2_num);
2021
}
2122

2223
constexpr void miscTests() {

0 commit comments

Comments
 (0)