Skip to content

Commit e2e8d7e

Browse files
committed
refactor: simplify unit type detection using C++20 concepts
1 parent c9eb743 commit e2e8d7e

2 files changed

Lines changed: 27 additions & 29 deletions

File tree

docs/core/units.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,25 @@ TelemetryKit works with any WPILib unit type. See the [WPILib Units Documentatio
148148
149149
## How It Works
150150
151-
TelemetryKit uses C++ type traits to detect WPILib unit types at compile time via SFINAE. When a unit type is detected:
151+
TelemetryKit uses a C++20 concept to detect WPILib unit types at compile time:
152+
153+
```cpp
154+
template<typename T>
155+
concept UnitType = requires(T t) {
156+
{ t.value() } -> std::convertible_to<double>;
157+
typename T::unit_type;
158+
};
159+
```
160+
161+
When a unit type is detected:
152162

153163
1. `value.value()` extracts the numeric value
154-
2. `units::abbreviation(UnitType{})` gets the unit string
164+
2. `units::abbreviation(T{})` gets the unit string
155165
3. Both are passed to the standard logging functions
156166

157167
```cpp
158-
template<typename T>
159-
inline auto RecordOutput(std::string_view key, const T& value)
160-
-> std::enable_if_t<detail::is_unit_type_v<T>, void>
161-
{
168+
template<UnitType T>
169+
inline void RecordOutput(std::string_view key, const T& value) {
162170
std::string unit = detail::GetUnitAbbreviation<T>();
163171
Logger::GetInstance().RecordOutput(key, value.value(), unit);
164172
}

src/main/native/include/telemetrykit/core/Units.h

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#pragma once
22

3+
#include <concepts>
34
#include <string>
45
#include <string_view>
5-
#include <type_traits>
66

77
#include <units/base.h>
88

@@ -22,25 +22,19 @@ namespace tkit {
2222
* tkit::RecordOutput("/Speed", units::meters_per_second_t{2.5});
2323
*/
2424

25-
namespace detail {
26-
27-
// Detects whether a type is a WPILib units::unit_t
28-
template<typename T, typename = void>
29-
struct is_unit_type : std::false_type {};
30-
25+
// Concept for WPILib unit types
3126
template<typename T>
32-
struct is_unit_type<T, std::void_t<
33-
decltype(std::declval<T>().value()),
34-
typename T::unit_type
35-
>> : std::true_type {};
27+
concept UnitType = requires(T t) {
28+
{ t.value() } -> std::convertible_to<double>;
29+
typename T::unit_type;
30+
};
3631

37-
template<typename T>
38-
inline constexpr bool is_unit_type_v = is_unit_type<T>::value;
32+
namespace detail {
3933

4034
// Returns the abbreviation string for a unit type
41-
template<typename UnitType>
35+
template<UnitType T>
4236
std::string GetUnitAbbreviation() {
43-
return std::string(units::abbreviation(UnitType{}));
37+
return std::string(units::abbreviation(T{}));
4438
}
4539

4640
} // namespace detail
@@ -54,21 +48,17 @@ std::string GetUnitAbbreviation() {
5448
* tkit::RecordOutput("/Distance", units::meter_t{3.5}); // 3.5, "m"
5549
* tkit::RecordOutput("/Angle", units::degree_t{90.0}); // 90.0, "deg"
5650
*/
57-
template<typename T>
58-
inline auto RecordOutput(std::string_view key, const T& value)
59-
-> std::enable_if_t<detail::is_unit_type_v<T>, void>
60-
{
51+
template<UnitType T>
52+
inline void RecordOutput(std::string_view key, const T& value) {
6153
std::string unit = detail::GetUnitAbbreviation<T>();
6254
Logger::GetInstance().RecordOutput(key, value.value(), unit);
6355
}
6456

6557
/**
6658
* LogTable::Put overload for WPILib unit types.
6759
*/
68-
template<typename T>
69-
inline auto Put(LogTable& table, std::string_view key, const T& value)
70-
-> std::enable_if_t<detail::is_unit_type_v<T>, void>
71-
{
60+
template<UnitType T>
61+
inline void Put(LogTable& table, std::string_view key, const T& value) {
7262
std::string unit = detail::GetUnitAbbreviation<T>();
7363
table.Put(key, value.value(), unit);
7464
}

0 commit comments

Comments
 (0)