diff --git a/wpilibc/src/main/native/cpp/util/Color.cpp b/wpilibc/src/main/native/cpp/util/Color.cpp deleted file mode 100644 index 6923c9cbec..0000000000 --- a/wpilibc/src/main/native/cpp/util/Color.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/util/Color.h" - -using namespace frc; - -std::string Color::HexString() const { - return fmt::format("#{:02X}{:02X}{:02X}", static_cast(255.0 * red), - static_cast(255.0 * green), - static_cast(255.0 * blue)); -} diff --git a/wpilibc/src/main/native/cpp/util/Color8Bit.cpp b/wpilibc/src/main/native/cpp/util/Color8Bit.cpp deleted file mode 100644 index e8e46567d7..0000000000 --- a/wpilibc/src/main/native/cpp/util/Color8Bit.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "frc/util/Color8Bit.h" - -using namespace frc; - -std::string Color8Bit::HexString() const { - return fmt::format("#{:02X}{:02X}{:02X}", red, green, blue); -} diff --git a/wpilibc/src/main/native/include/frc/util/Color.h b/wpilibc/src/main/native/include/frc/util/Color.h index b5855e23ce..868e577e3c 100644 --- a/wpilibc/src/main/native/include/frc/util/Color.h +++ b/wpilibc/src/main/native/include/frc/util/Color.h @@ -11,6 +11,7 @@ #include #include +#include namespace frc { @@ -851,7 +852,16 @@ class Color { * * @return a string of the format \#RRGGBB */ - std::string HexString() const; + constexpr auto HexString() const { + const int r = 255.0 * red; + const int g = 255.0 * green; + const int b = 255.0 * blue; + + return wpi::ct_string, 7>{ + {'#', wpi::hexdigit(r / 16), wpi::hexdigit(r % 16), + wpi::hexdigit(g / 16), wpi::hexdigit(g % 16), wpi::hexdigit(b / 16), + wpi::hexdigit(b % 16)}}; + } double red = 0.0; double green = 0.0; diff --git a/wpilibc/src/main/native/include/frc/util/Color8Bit.h b/wpilibc/src/main/native/include/frc/util/Color8Bit.h index 77bdc9f0ee..ce7915a5fd 100644 --- a/wpilibc/src/main/native/include/frc/util/Color8Bit.h +++ b/wpilibc/src/main/native/include/frc/util/Color8Bit.h @@ -11,6 +11,7 @@ #include #include +#include #include "Color.h" @@ -107,7 +108,12 @@ class Color8Bit { * * @return a string of the format \#RRGGBB */ - std::string HexString() const; + constexpr auto HexString() const { + return wpi::ct_string, 7>{ + {'#', wpi::hexdigit(red / 16), wpi::hexdigit(red % 16), + wpi::hexdigit(green / 16), wpi::hexdigit(green % 16), + wpi::hexdigit(blue / 16), wpi::hexdigit(blue % 16)}}; + } int red = 0; int green = 0; diff --git a/wpilibc/src/test/native/cpp/util/Color8BitTest.cpp b/wpilibc/src/test/native/cpp/util/Color8BitTest.cpp index e0b26dd579..d9027be244 100644 --- a/wpilibc/src/test/native/cpp/util/Color8BitTest.cpp +++ b/wpilibc/src/test/native/cpp/util/Color8BitTest.cpp @@ -2,6 +2,8 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. +#include + #include #include "frc/util/Color8Bit.h" @@ -56,7 +58,12 @@ TEST(Color8BitTest, ImplicitConversionToColor) { } TEST(Color8BitTest, ToHexString) { - constexpr frc::Color8Bit color{255, 128, 64}; + constexpr frc::Color8Bit color1{255, 128, 64}; + EXPECT_EQ("#FF8040", color1.HexString()); - EXPECT_EQ("#FF8040", color.HexString()); + // Ensure conversion to std::string works + [[maybe_unused]] std::string str = color1.HexString(); + + frc::Color8Bit color2{255, 128, 64}; + EXPECT_EQ("#FF8040", color2.HexString()); } diff --git a/wpilibc/src/test/native/cpp/util/ColorTest.cpp b/wpilibc/src/test/native/cpp/util/ColorTest.cpp index c76c7f1da2..8b71d4634c 100644 --- a/wpilibc/src/test/native/cpp/util/ColorTest.cpp +++ b/wpilibc/src/test/native/cpp/util/ColorTest.cpp @@ -2,6 +2,8 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. +#include + #include #include "frc/util/Color.h" @@ -56,7 +58,12 @@ TEST(ColorTest, FromHSV) { } TEST(ColorTest, ToHexString) { - constexpr frc::Color color{255, 128, 64}; + constexpr frc::Color color1{255, 128, 64}; + EXPECT_EQ("#FF8040", color1.HexString()); - EXPECT_EQ("#FF8040", color.HexString()); + // Ensure conversion to std::string works + [[maybe_unused]] std::string str = color1.HexString(); + + frc::Color color2{255, 128, 64}; + EXPECT_EQ("#FF8040", color2.HexString()); } diff --git a/wpiutil/src/main/native/include/wpi/ct_string.h b/wpiutil/src/main/native/include/wpi/ct_string.h index 9f0ef90750..2c7796b4f6 100644 --- a/wpiutil/src/main/native/include/wpi/ct_string.h +++ b/wpiutil/src/main/native/include/wpi/ct_string.h @@ -31,7 +31,7 @@ struct ct_string { template requires(M <= (N + 1)) - consteval ct_string(Char const (&s)[M]) { // NOLINT + constexpr ct_string(Char const (&s)[M]) { // NOLINT if constexpr (M == (N + 1)) { if (s[N] != Char{}) { throw std::logic_error{"char array not null terminated"}; @@ -50,7 +50,7 @@ struct ct_string { } } - explicit consteval ct_string(std::basic_string_view s) { + explicit constexpr ct_string(std::basic_string_view s) { // avoid dependency on // auto p = std::ranges::copy(s, chars.begin()).out; auto p = chars.begin(); @@ -63,6 +63,64 @@ struct ct_string { } } + constexpr bool operator==(const ct_string&) const = default; + + constexpr bool operator==(const std::basic_string& rhs) const { + if (size() != rhs.size()) { + return false; + } + + for (size_t i = 0; i < size(); ++i) { + if (chars[i] != rhs[i]) { + return false; + } + } + + return true; + } + + constexpr bool operator==(std::basic_string_view rhs) const { + if (size() != rhs.size()) { + return false; + } + + for (size_t i = 0; i < size(); ++i) { + if (chars[i] != rhs[i]) { + return false; + } + } + + return true; + } + + template + requires(N + 1 == M) + constexpr bool operator==(Char const (&rhs)[M]) const { + for (size_t i = 0; i < M; ++i) { + if (chars[i] != rhs[i]) { + return false; + } + } + + return true; + } + + constexpr bool operator==(const Char* rhs) const { + for (size_t i = 0; i < N + 1; ++i) { + if (chars[i] != rhs[i]) { + return false; + } + + // If index of rhs's null terminator is less than lhs's size - 1, rhs is + // shorter than lhs + if (rhs[i] == '\0' && i < N) { + return false; + } + } + + return true; + } + constexpr auto size() const noexcept { return N; } constexpr auto begin() const noexcept { return chars.begin(); } @@ -71,6 +129,11 @@ struct ct_string { constexpr auto data() const noexcept { return chars.data(); } constexpr auto c_str() const noexcept { return chars.data(); } + constexpr operator std::basic_string() // NOLINT + const noexcept { + return std::basic_string{chars.data(), N}; + } + constexpr operator std::basic_string_view() // NOLINT const noexcept { return std::basic_string_view{chars.data(), N}; @@ -82,13 +145,13 @@ ct_string(Char const (&s)[M]) -> ct_string, M - 1>; inline namespace literals { template -consteval auto operator""_ct_string() { +constexpr auto operator""_ct_string() { return S; } } // namespace literals template -consteval auto operator+(ct_string const& s1, +constexpr auto operator+(ct_string const& s1, ct_string const& s2) noexcept { return Concat(s1, s2); } @@ -102,7 +165,7 @@ consteval auto operator+(ct_string const& s1, * @return concatenated string */ template -consteval auto Concat(ct_string const& s1, +constexpr auto Concat(ct_string const& s1, ct_string const&... s) { // Need a dummy array to instantiate a ct_string. constexpr Char dummy[1] = {}; @@ -136,7 +199,7 @@ consteval auto Concat(ct_string const& s1, template > requires(Base >= 2 && Base <= 36) -consteval auto NumToCtString() { +constexpr auto NumToCtString() { constexpr char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; auto buflen = [] {