[wpiutil,hal] Move C++ Handle wrapper to wpiutil (#8935)

Also move WPI_Handle typedef to its own header (util/Handle.h).
This commit is contained in:
Peter Johnson
2026-06-01 13:57:25 -07:00
committed by GitHub
parent 3d982f81dd
commit 3f0d7bc2c4
24 changed files with 126 additions and 127 deletions

View File

@@ -7,6 +7,7 @@
#include <stddef.h> // NOLINT
#include <stdint.h>
#include "wpi/util/Handle.h"
#include "wpi/util/PixelFormat.h"
#include "wpi/util/string.h"
@@ -40,7 +41,7 @@ extern "C" {
typedef int CS_Bool;
typedef int CS_Status;
typedef int CS_Handle;
typedef WPI_Handle CS_Handle;
typedef CS_Handle CS_Property;
typedef CS_Handle CS_Listener;
typedef CS_Handle CS_ListenerPoller;

View File

@@ -6,15 +6,17 @@
#include <stdint.h>
#include "wpi/util/Handle.h"
/**
* @defgroup hal_types Type Definitions
* @ingroup hal_capi
* @{
*/
#define HAL_INVALID_HANDLE 0
#define HAL_INVALID_HANDLE WPI_INVALID_HANDLE
typedef int32_t HAL_Handle;
typedef WPI_Handle HAL_Handle;
typedef HAL_Handle HAL_AlertHandle;

View File

@@ -1,77 +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.
#pragma once
#include "wpi/hal/Types.h"
namespace wpi::hal {
/**
* A move-only C++ wrapper around a HAL handle.
* Will free the handle if FreeFunction is provided
*/
template <typename CType, void (*FreeFunction)(CType) = nullptr,
int32_t CInvalid = HAL_INVALID_HANDLE>
class Handle {
public:
Handle() = default;
/*implicit*/ Handle(CType val) : m_handle(val) {} // NOLINT
Handle(const Handle&) = delete;
Handle& operator=(const Handle&) = delete;
Handle(Handle&& rhs) : m_handle(rhs.m_handle) { rhs.m_handle = CInvalid; }
Handle& operator=(Handle&& rhs) {
if (this != &rhs) {
// FIXME: GCC gives the false positive "the address of <GetDefault> will never
// be NULL" because it doesn't realize the default template parameter can make
// GetDefault nullptr. Fixed in GCC 13.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94554
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105885
#if __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Waddress"
#endif // __GNUC__
if constexpr (FreeFunction != nullptr) {
#if __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
if (m_handle != CInvalid) {
FreeFunction(m_handle);
}
}
}
m_handle = rhs.m_handle;
rhs.m_handle = CInvalid;
return *this;
}
~Handle() {
// FIXME: GCC gives the false positive "the address of <GetDefault> will never
// be NULL" because it doesn't realize the default template parameter can make
// GetDefault nullptr. Fixed in GCC 13.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94554
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105885
#if __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Waddress"
#endif // __GNUC__
if constexpr (FreeFunction != nullptr) {
#if __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
if (m_handle != CInvalid) {
FreeFunction(m_handle);
}
}
}
operator CType() const { return m_handle; } // NOLINT
private:
CType m_handle = CInvalid;
};
} // namespace wpi::hal

View File

@@ -6,6 +6,7 @@
#include <stdint.h>
#include "wpi/util/Handle.h"
#include "wpi/util/string.h"
#ifdef __cplusplus
@@ -31,7 +32,7 @@ struct WPI_DataLog;
/** Typedefs */
typedef int NT_Bool;
typedef unsigned int NT_Handle;
typedef WPI_Handle NT_Handle;
typedef NT_Handle NT_ConnectionDataLogger;
typedef NT_Handle NT_DataLogger;
typedef NT_Handle NT_Entry;

View File

@@ -51,10 +51,10 @@ void ConnectionListenerTest::Connect(const char* address, unsigned int port4) {
TEST_F(ConnectionListenerTest, Polled) {
// set up the poller
NT_ListenerPoller poller = wpi::nt::CreateListenerPoller(server_inst);
ASSERT_NE(poller, 0u);
ASSERT_NE(poller, 0);
NT_Listener handle = wpi::nt::AddPolledListener(
poller, server_inst, wpi::nt::EventFlags::CONNECTION);
ASSERT_NE(handle, 0u);
ASSERT_NE(handle, 0);
// trigger a connect event
Connect("127.0.0.1", 10020);

View File

@@ -73,11 +73,11 @@ TEST_F(LocalStorageTest, GetTopic2) {
}
TEST_F(LocalStorageTest, GetTopicEmptyName) {
EXPECT_EQ(storage.GetTopic(""), 0u);
EXPECT_EQ(storage.GetTopic(""), 0);
}
TEST_F(LocalStorageTest, GetEntryEmptyName) {
EXPECT_EQ(storage.GetEntry(""), 0u);
EXPECT_EQ(storage.GetEntry(""), 0);
}
TEST_F(LocalStorageTest, GetEntryCached) {
@@ -538,11 +538,11 @@ TEST_F(LocalStorageTest, PublishUntyped) {
std::string_view{"cannot publish 'foo' with an unassigned "
"type or empty type string"}));
EXPECT_EQ(storage.Publish(fooTopic, NT_UNASSIGNED, "", {}, {}), 0u);
EXPECT_EQ(storage.Publish(fooTopic, NT_UNASSIGNED, "", {}, {}), 0);
}
TEST_F(LocalStorageTest, SetValueInvalidHandle) {
EXPECT_FALSE(storage.SetEntryValue(0u, {}));
EXPECT_FALSE(storage.SetEntryValue(0, {}));
}
class LocalStorageDuplicatesTest : public LocalStorageTest {

View File

@@ -6,10 +6,10 @@
#include "EdgeConfiguration.hpp"
#include "wpi/hal/Counter.h"
#include "wpi/hal/Types.hpp"
#include "wpi/units/angular_velocity.hpp"
#include "wpi/units/frequency.hpp"
#include "wpi/units/time.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -110,7 +110,7 @@ class Tachometer : public wpi::util::Sendable,
void InitSendable(wpi::util::SendableBuilder& builder) override;
private:
wpi::hal::Handle<HAL_CounterHandle, HAL_FreeCounter> m_handle;
wpi::util::Handle<HAL_CounterHandle, HAL_FreeCounter> m_handle;
int m_edgesPerRevolution;
int32_t m_channel;
};

View File

@@ -6,7 +6,7 @@
#include "EdgeConfiguration.hpp"
#include "wpi/hal/Counter.h"
#include "wpi/hal/Types.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -54,7 +54,7 @@ class UpDownCounter : public wpi::util::Sendable,
void InitSendable(wpi::util::SendableBuilder& builder) override;
private:
wpi::hal::Handle<HAL_CounterHandle, HAL_FreeCounter> m_handle;
wpi::util::Handle<HAL_CounterHandle, HAL_FreeCounter> m_handle;
int32_t m_channel;
};
} // namespace wpi

View File

@@ -8,7 +8,7 @@
#include <string_view>
#include "wpi/hal/Alert.h"
#include "wpi/hal/Types.hpp"
#include "wpi/util/Handle.hpp"
namespace wpi {
@@ -116,7 +116,7 @@ class Alert {
private:
Level m_type;
wpi::hal::Handle<HAL_AlertHandle, HAL_DestroyAlert> m_handle;
wpi::util::Handle<HAL_AlertHandle, HAL_DestroyAlert> m_handle;
};
} // namespace wpi

View File

@@ -8,10 +8,10 @@
#include "wpi/framework/IterativeRobotBase.hpp"
#include "wpi/hal/Notifier.hpp"
#include "wpi/hal/Types.hpp"
#include "wpi/internal/PeriodicPriorityQueue.hpp"
#include "wpi/units/frequency.hpp"
#include "wpi/units/time.hpp"
#include "wpi/util/Handle.hpp"
namespace wpi {
@@ -88,7 +88,7 @@ class TimedRobot : public IterativeRobotBase {
wpi::units::second_t offset = 0_s);
protected:
wpi::hal::Handle<HAL_NotifierHandle, HAL_DestroyNotifier> m_notifier;
wpi::util::Handle<HAL_NotifierHandle, HAL_DestroyNotifier> m_notifier;
std::chrono::microseconds m_startTime;
private:

View File

@@ -7,7 +7,7 @@
#include <stdint.h>
#include "wpi/hal/CANAPI.h"
#include "wpi/hal/Types.hpp"
#include "wpi/util/Handle.hpp"
namespace wpi {
@@ -155,6 +155,6 @@ class CAN {
HAL_CAN_DEV_MISCELLANEOUS;
private:
wpi::hal::Handle<HAL_CANHandle, HAL_CleanCAN> m_handle;
wpi::util::Handle<HAL_CANHandle, HAL_CleanCAN> m_handle;
};
} // namespace wpi

View File

@@ -8,7 +8,7 @@
#include "wpi/hal/I2C.h"
#include "wpi/hal/I2CTypes.h"
#include "wpi/hal/Types.hpp"
#include "wpi/util/Handle.hpp"
namespace wpi {
@@ -154,7 +154,7 @@ class I2C {
bool VerifySensor(int registerAddress, int count, const uint8_t* expected);
private:
wpi::hal::Handle<HAL_I2CPort, HAL_CloseI2C, HAL_I2C_PORT_INVALID> m_port;
wpi::util::Handle<HAL_I2CPort, HAL_CloseI2C, HAL_I2C_PORT_INVALID> m_port;
int m_deviceAddress;
};

View File

@@ -7,8 +7,8 @@
#include <string_view>
#include "wpi/hal/SerialPort.h"
#include "wpi/hal/Types.hpp"
#include "wpi/units/time.hpp"
#include "wpi/util/Handle.hpp"
namespace wpi {
@@ -253,7 +253,7 @@ class SerialPort {
void Reset();
private:
wpi::hal::Handle<HAL_SerialPortHandle, HAL_CloseSerial> m_portHandle;
wpi::util::Handle<HAL_SerialPortHandle, HAL_CloseSerial> m_portHandle;
};
} // namespace wpi

View File

@@ -7,7 +7,7 @@
#include <stdint.h>
#include "wpi/hal/AnalogInput.h"
#include "wpi/hal/Types.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -78,7 +78,7 @@ class AnalogInput : public wpi::util::Sendable,
private:
int m_channel;
wpi::hal::Handle<HAL_AnalogInputHandle, HAL_FreeAnalogInputPort> m_port;
wpi::util::Handle<HAL_AnalogInputHandle, HAL_FreeAnalogInputPort> m_port;
};
} // namespace wpi

View File

@@ -5,7 +5,7 @@
#pragma once
#include "wpi/hal/DIO.h"
#include "wpi/hal/Types.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -60,7 +60,7 @@ class DigitalInput : public wpi::util::Sendable,
private:
int m_channel;
wpi::hal::Handle<HAL_DigitalHandle, HAL_FreeDIOPort> m_handle;
wpi::util::Handle<HAL_DigitalHandle, HAL_FreeDIOPort> m_handle;
};
} // namespace wpi

View File

@@ -5,8 +5,8 @@
#pragma once
#include "wpi/hal/DIO.h"
#include "wpi/hal/Types.hpp"
#include "wpi/units/time.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -142,8 +142,8 @@ class DigitalOutput : public wpi::util::Sendable,
private:
int m_channel;
wpi::hal::Handle<HAL_DigitalHandle, HAL_FreeDIOPort> m_handle;
wpi::hal::Handle<HAL_DigitalPWMHandle> m_pwmGenerator;
wpi::util::Handle<HAL_DigitalHandle, HAL_FreeDIOPort> m_handle;
wpi::util::Handle<HAL_DigitalPWMHandle> m_pwmGenerator;
};
} // namespace wpi

View File

@@ -7,8 +7,8 @@
#include <stdint.h>
#include "wpi/hal/PWM.h"
#include "wpi/hal/Types.hpp"
#include "wpi/units/time.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -90,7 +90,7 @@ class PWM : public wpi::util::Sendable, public wpi::util::SendableHelper<PWM> {
private:
int m_channel;
wpi::hal::Handle<HAL_DigitalHandle, HAL_FreePWMPort> m_handle;
wpi::util::Handle<HAL_DigitalHandle, HAL_FreePWMPort> m_handle;
};
} // namespace wpi

View File

@@ -9,9 +9,9 @@
#include "wpi/hal/AddressableLED.h"
#include "wpi/hal/AddressableLEDTypes.h"
#include "wpi/hal/Types.hpp"
#include "wpi/util/Color.hpp"
#include "wpi/util/Color8Bit.hpp"
#include "wpi/util/Handle.hpp"
namespace wpi {
@@ -170,7 +170,7 @@ class AddressableLED {
std::span<const LEDData> ledData);
private:
wpi::hal::Handle<HAL_AddressableLEDHandle, HAL_FreeAddressableLED> m_handle;
wpi::util::Handle<HAL_AddressableLEDHandle, HAL_FreeAddressableLED> m_handle;
int m_channel;
int m_start{0};
int m_length{0};

View File

@@ -7,7 +7,7 @@
#include <vector>
#include "wpi/hal/PowerDistribution.h"
#include "wpi/hal/Types.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -346,7 +346,7 @@ class PowerDistribution : public wpi::util::Sendable,
void InitSendable(wpi::util::SendableBuilder& builder) override;
private:
wpi::hal::Handle<HAL_PowerDistributionHandle, HAL_CleanPowerDistribution>
wpi::util::Handle<HAL_PowerDistributionHandle, HAL_CleanPowerDistribution>
m_handle;
int m_module;
};

View File

@@ -5,9 +5,9 @@
#pragma once
#include "wpi/hal/DutyCycle.h"
#include "wpi/hal/Types.hpp"
#include "wpi/units/frequency.hpp"
#include "wpi/units/time.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -72,6 +72,6 @@ class DutyCycle : public wpi::util::Sendable,
private:
void InitDutyCycle();
int m_channel;
wpi::hal::Handle<HAL_DutyCycleHandle, HAL_FreeDutyCycle> m_handle;
wpi::util::Handle<HAL_DutyCycleHandle, HAL_FreeDutyCycle> m_handle;
};
} // namespace wpi

View File

@@ -5,8 +5,8 @@
#pragma once
#include "wpi/hal/Encoder.h"
#include "wpi/hal/Types.hpp"
#include "wpi/hardware/discrete/CounterBase.hpp"
#include "wpi/util/Handle.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
@@ -272,7 +272,7 @@ class Encoder : public CounterBase,
*/
double DecodingScaleFactor() const;
wpi::hal::Handle<HAL_EncoderHandle, HAL_FreeEncoder> m_encoder;
wpi::util::Handle<HAL_EncoderHandle, HAL_FreeEncoder> m_encoder;
};
} // namespace wpi

View File

@@ -0,0 +1,19 @@
// 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.
#pragma once
#include <stdint.h>
#define WPI_INVALID_HANDLE 0
/**
* Generic handle for all WPI handle-based interfaces.
*
* Handle data layout:
* - Bits 0-23: Type-specific
* - Bits 24-30: Type
* - Bit 31: Error
*/
typedef int32_t WPI_Handle; // NOLINT

View File

@@ -0,0 +1,61 @@
// 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.
#pragma once
#include <stdint.h>
#include <concepts>
#include "wpi/util/Handle.h"
namespace wpi::util {
/**
* A move-only C++ wrapper around a WPI handle.
* Will free the handle if FreeFunction is provided
*/
template <typename CType, auto FreeFunction = nullptr,
auto CInvalid = WPI_INVALID_HANDLE>
requires std::same_as<decltype(FreeFunction), std::nullptr_t> ||
std::invocable<decltype(FreeFunction), CType>
class Handle {
public:
Handle() = default;
/*implicit*/ Handle(CType val) : m_handle(val) {} // NOLINT
Handle(const Handle&) = delete;
Handle& operator=(const Handle&) = delete;
Handle(Handle&& rhs) : m_handle(rhs.m_handle) {
rhs.m_handle = static_cast<CType>(CInvalid);
}
Handle& operator=(Handle&& rhs) {
if (this != &rhs) {
if constexpr (std::invocable<decltype(FreeFunction), CType>) {
if (m_handle != static_cast<CType>(CInvalid)) {
FreeFunction(m_handle);
}
}
}
m_handle = rhs.m_handle;
rhs.m_handle = static_cast<CType>(CInvalid);
return *this;
}
~Handle() {
if constexpr (std::invocable<decltype(FreeFunction), CType>) {
if (m_handle != static_cast<CType>(CInvalid)) {
FreeFunction(m_handle);
}
}
}
operator CType() const { return m_handle; } // NOLINT
private:
CType m_handle = static_cast<CType>(CInvalid);
};
} // namespace wpi::util

View File

@@ -4,15 +4,7 @@
#pragma once
/**
* Generic handle for all WPI handle-based interfaces.
*
* Handle data layout:
* - Bits 0-23: Type-specific
* - Bits 24-30: Type
* - Bit 31: Error
*/
typedef unsigned int WPI_Handle; // NOLINT
#include "wpi/util/Handle.h"
/** An event handle. */
typedef WPI_Handle WPI_EventHandle; // NOLINT