From 6c188be218540c3f022f1de90b56cd4b85b884ac Mon Sep 17 00:00:00 2001
From: Davide Basso <davide.basso@skywarder.eu>
Date: Thu, 19 Dec 2024 22:01:33 +0000
Subject: [PATCH] [Units] Add more operators

Operators added: unary + - and ! >> <<
---
 src/shared/units/Units.h       | 57 ++++++++++++++++++++++++++++++++--
 src/tests/catch/test-units.cpp | 20 ++++++++++++
 2 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/src/shared/units/Units.h b/src/shared/units/Units.h
index f4f382139..35d6dd67d 100644
--- a/src/shared/units/Units.h
+++ b/src/shared/units/Units.h
@@ -24,6 +24,7 @@
 
 #include <utils/Debug.h>
 
+#include <ostream>
 #include <ratio>
 #include <typeinfo>
 
@@ -32,6 +33,9 @@ namespace Boardcore
 namespace Units
 {
 
+/**
+ * @brief Enumeration of the different kinds of units.
+ */
 enum class UnitKind
 {
     Angle,
@@ -43,7 +47,16 @@ enum class UnitKind
     Frequency,
 };
 
-// Base class to implement custom measurement units logic.
+/**
+ * Base class to implement custom measurement units logic.
+ * @tparam Kind The kind of unit.
+ * @tparam Ratio The ratio of the unit.
+ *
+ * The Ratio template parameter is used to convert between different units of
+ * the same kind. For example, to convert from meters to kilometers, the ratio
+ * is 1/1000.
+ *
+ */
 template <UnitKind Kind, class Ratio = std::ratio<1>>
 class Unit
 {
@@ -56,7 +69,9 @@ public:
     {
     }
 
-    // Get the value of the unit in the specified ratio.
+    /**
+     * @brief Return the value of the unit in the target ratio.
+     */
     template <class TargetRatio = Ratio>
     constexpr float value() const
     {
@@ -74,6 +89,10 @@ public:
         return Unit<TargetKind, TargetRatio>(value<TargetRatio>());
     }
 
+    template <UnitKind PKind, class PRatio>
+    friend std::istream& operator>>(std::istream& is,
+                                    Unit<PKind, PRatio>& unit);
+
 private:
     float _value;
 };
@@ -191,5 +210,39 @@ constexpr Unit<Kind, Ratio>& operator/=(Unit<Kind, Ratio>& lhs, float rhs)
     return lhs;
 }
 
+// Unary operators
+template <UnitKind Kind, class Ratio>
+constexpr Unit<Kind, Ratio> operator+(const Unit<Kind, Ratio>& unit)
+{
+    return Unit<Kind, Ratio>(unit.template value());
+}
+
+template <UnitKind Kind, class Ratio>
+constexpr Unit<Kind, Ratio> operator-(const Unit<Kind, Ratio>& unit)
+{
+    return Unit<Kind, Ratio>(-unit.template value());
+}
+
+template <UnitKind Kind, class Ratio>
+constexpr bool operator!(const Unit<Kind, Ratio>& unit)
+{
+    return !unit.template value();
+}
+
+// Stream operators
+template <UnitKind Kind, class Ratio>
+std::ostream& operator<<(std::ostream& os, const Unit<Kind, Ratio>& unit)
+{
+    os << unit.template value();
+    return os;
+}
+
+template <UnitKind Kind, class Ratio>
+inline std::istream& operator>>(std::istream& is, Unit<Kind, Ratio>& unit)
+{
+    is >> unit._value;
+    return is;
+}
+
 }  // namespace Units
 }  // namespace Boardcore
diff --git a/src/tests/catch/test-units.cpp b/src/tests/catch/test-units.cpp
index b461d9fed..456aacb4a 100644
--- a/src/tests/catch/test-units.cpp
+++ b/src/tests/catch/test-units.cpp
@@ -30,6 +30,7 @@
 
 #include <catch2/catch.hpp>
 #include <cmath>
+#include <sstream>
 
 using namespace Boardcore;
 
@@ -103,4 +104,23 @@ TEST_CASE("Units Test")
     REQUIRE(a == Radian(2 * PI));
     a /= 2;
     REQUIRE(a == Radian(PI));
+
+    // Test unary operators
+    REQUIRE(+Radian(PI) == Radian(PI));
+    REQUIRE(-Radian(PI) == Radian(-PI));
+    REQUIRE(!Radian(PI) == false);
+    REQUIRE(!Radian(0) == true);
+
+    // Test stream operators
+    std::ostringstream ss;
+    ss << 1_deg;
+    REQUIRE(ss.str() == "1");
+    ss << 2_rad;
+    REQUIRE(ss.str() == "12");
+    ss << 3_m;
+    REQUIRE(ss.str() == "123");
+
+    std::istringstream is("4");
+    is >> a;
+    REQUIRE(a == 4_rad);
 }
-- 
GitLab