WPILibC++ 2025.0.0-alpha-1-14-g3b6f38d
SimDataValue.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <memory>
8
9#include <wpi/Compiler.h>
10#include <wpi/UidVector.h>
11#include <wpi/spinlock.h>
12
15
16namespace hal {
17
18namespace impl {
19template <typename T, HAL_Value (*MakeValue)(T)>
21 public:
22 explicit SimDataValueBase(T value) : m_value(value) {}
23
25
26 T Get() const {
27 std::scoped_lock lock(m_mutex);
28 return m_value;
29 }
30
31 LLVM_ATTRIBUTE_ALWAYS_INLINE operator T() const { return Get(); } // NOLINT
32
33 void Reset(T value) {
34 std::scoped_lock lock(m_mutex);
35 DoReset();
36 m_value = value;
37 }
38
40
41 protected:
42 int32_t DoRegisterCallback(HAL_NotifyCallback callback, void* param,
43 HAL_Bool initialNotify, const char* name) {
44 std::unique_lock lock(m_mutex);
45 int32_t newUid = DoRegister(reinterpret_cast<RawFunctor>(callback), param);
46 if (newUid == -1) {
47 return -1;
48 }
49 if (initialNotify) {
50 // We know that the callback is not null because of earlier null check
51 HAL_Value value = MakeValue(m_value);
52 lock.unlock();
53 callback(name, param, &value);
54 }
55 return newUid;
56 }
57
58 void DoSet(T value, const char* name) {
59 std::scoped_lock lock(this->m_mutex);
60 if (m_value != value) {
61 m_value = value;
62 if (m_callbacks) {
63 HAL_Value halValue = MakeValue(value);
64 for (auto&& cb : *m_callbacks) {
65 reinterpret_cast<HAL_NotifyCallback>(cb.callback)(name, cb.param,
66 &halValue);
67 }
68 }
69 }
70 }
71
73};
74} // namespace impl
75
76/**
77 * Simulation data value wrapper. Provides callback functionality when the
78 * data value is changed.
79 *
80 * @tparam T value type (e.g. double)
81 * @tparam MakeValue function that takes a T and returns a HAL_Value
82 * @tparam GetName function that returns a const char* for the name
83 * @tparam GetDefault function that returns the default T (used only for
84 * default constructor)
85 */
86template <typename T, HAL_Value (*MakeValue)(T), const char* (*GetName)(),
87 T (*GetDefault)() = nullptr>
89 public:
90// FIXME: GCC 12.1 gives the false positive "the address of <GetDefault> will
91// never be NULL" because it doesn't realize the default template parameter can
92// make GetDefault nullptr. In C++20, replace "T (*GetDefault)() = nullptr" with
93// "T (*GetDefault)() = [] { return T(); }" and unconditionally call
94// GetDefault() to fix the warning.
95#if __GNUC__ >= 12
96#pragma GCC diagnostic push
97#pragma GCC diagnostic ignored "-Waddress"
98#endif // __GNUC__ >= 12
100 : impl::SimDataValueBase<T, MakeValue>(
101 GetDefault != nullptr ? GetDefault() : T()) {}
102#if __GNUC__ >= 12
103#pragma GCC diagnostic pop
104#endif // __GNUC__ >= 12
105 explicit SimDataValue(T value)
106 : impl::SimDataValueBase<T, MakeValue>(value) {}
107
109 HAL_NotifyCallback callback, void* param, HAL_Bool initialNotify) {
110 return this->DoRegisterCallback(callback, param, initialNotify, GetName());
111 }
112
114 this->DoSet(value, GetName());
115 }
116
118 Set(value);
119 return *this;
120 }
121};
122
123/**
124 * Define a name functor for use with SimDataValue.
125 * This creates a function named GetNAMEName() that returns "NAME".
126 * @param NAME the name to return
127 */
128#define HAL_SIMDATAVALUE_DEFINE_NAME(NAME) \
129 static LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr const char* \
130 Get##NAME##Name() { \
131 return #NAME; \
132 }
133
134/**
135 * Define a standard C API for simulation data.
136 *
137 * Functions defined:
138 * - int32 NS_RegisterCAPINAMECallback(
139 * int32_t index, HAL_NotifyCallback callback, void* param,
140 * HAL_Bool initialNotify)
141 * - void NS_CancelCAPINAMECallback(int32_t index, int32_t uid)
142 * - TYPE NS_GetCAPINAME(int32_t index)
143 * - void NS_SetCAPINAME(int32_t index, TYPE value)
144 *
145 * @param TYPE the underlying value type (e.g. double)
146 * @param NS the "namespace" (e.g. HALSIM)
147 * @param CAPINAME the C API name (usually first letter capitalized)
148 * @param DATA the backing data array
149 * @param LOWERNAME the lowercase name of the backing data variable
150 */
151#define HAL_SIMDATAVALUE_DEFINE_CAPI(TYPE, NS, CAPINAME, DATA, LOWERNAME) \
152 int32_t NS##_Register##CAPINAME##Callback( \
153 int32_t index, HAL_NotifyCallback callback, void* param, \
154 HAL_Bool initialNotify) { \
155 return DATA[index].LOWERNAME.RegisterCallback(callback, param, \
156 initialNotify); \
157 } \
158 \
159 void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t uid) { \
160 DATA[index].LOWERNAME.CancelCallback(uid); \
161 } \
162 \
163 TYPE NS##_Get##CAPINAME(int32_t index) { \
164 return DATA[index].LOWERNAME; \
165 } \
166 \
167 void NS##_Set##CAPINAME(int32_t index, TYPE LOWERNAME) { \
168 DATA[index].LOWERNAME = LOWERNAME; \
169 }
170
171/**
172 * Define a standard C API for simulation data (channel variant).
173 *
174 * Functions defined:
175 * - int32 NS_RegisterCAPINAMECallback(
176 * int32_t index, int32_t channel, HAL_NotifyCallback callback,
177 * void* param, HAL_Bool initialNotify)
178 * - void NS_CancelCAPINAMECallback(int32_t index, int32_t channel, int32_t uid)
179 * - TYPE NS_GetCAPINAME(int32_t index, int32_t channel)
180 * - void NS_SetCAPINAME(int32_t index, int32_t channel, TYPE value)
181 *
182 * @param TYPE the underlying value type (e.g. double)
183 * @param NS the "namespace" (e.g. HALSIM)
184 * @param CAPINAME the C API name (usually first letter capitalized)
185 * @param DATA the backing data array
186 * @param LOWERNAME the lowercase name of the backing data variable array
187 */
188#define HAL_SIMDATAVALUE_DEFINE_CAPI_CHANNEL(TYPE, NS, CAPINAME, DATA, \
189 LOWERNAME) \
190 int32_t NS##_Register##CAPINAME##Callback( \
191 int32_t index, int32_t channel, HAL_NotifyCallback callback, \
192 void* param, HAL_Bool initialNotify) { \
193 return DATA[index].LOWERNAME[channel].RegisterCallback(callback, param, \
194 initialNotify); \
195 } \
196 \
197 void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t channel, \
198 int32_t uid) { \
199 DATA[index].LOWERNAME[channel].CancelCallback(uid); \
200 } \
201 \
202 TYPE NS##_Get##CAPINAME(int32_t index, int32_t channel) { \
203 return DATA[index].LOWERNAME[channel]; \
204 } \
205 \
206 void NS##_Set##CAPINAME(int32_t index, int32_t channel, TYPE LOWERNAME) { \
207 DATA[index].LOWERNAME[channel] = LOWERNAME; \
208 }
209
210/**
211 * Define a standard C API for simulation data (no index variant).
212 *
213 * Functions defined:
214 * - int32 NS_RegisterCAPINAMECallback(
215 * HAL_NotifyCallback callback, void* param, HAL_Bool initialNotify)
216 * - void NS_CancelCAPINAMECallback(int32_t uid)
217 * - TYPE NS_GetCAPINAME(void)
218 * - void NS_SetCAPINAME(TYPE value)
219 *
220 * @param TYPE the underlying value type (e.g. double)
221 * @param NS the "namespace" (e.g. HALSIM)
222 * @param CAPINAME the C API name (usually first letter capitalized)
223 * @param DATA the backing data pointer
224 * @param LOWERNAME the lowercase name of the backing data variable
225 */
226#define HAL_SIMDATAVALUE_DEFINE_CAPI_NOINDEX(TYPE, NS, CAPINAME, DATA, \
227 LOWERNAME) \
228 int32_t NS##_Register##CAPINAME##Callback( \
229 HAL_NotifyCallback callback, void* param, HAL_Bool initialNotify) { \
230 return DATA->LOWERNAME.RegisterCallback(callback, param, initialNotify); \
231 } \
232 \
233 void NS##_Cancel##CAPINAME##Callback(int32_t uid) { \
234 DATA->LOWERNAME.CancelCallback(uid); \
235 } \
236 \
237 TYPE NS##_Get##CAPINAME(void) { \
238 return DATA->LOWERNAME; \
239 } \
240 \
241 void NS##_Set##CAPINAME(TYPE LOWERNAME) { \
242 DATA->LOWERNAME = LOWERNAME; \
243 }
244
245/**
246 * Define a stub standard C API for simulation data.
247 *
248 * Functions defined:
249 * - int32 NS_RegisterCAPINAMECallback(
250 * int32_t index, HAL_NotifyCallback callback, void* param,
251 * HAL_Bool initialNotify)
252 * - void NS_CancelCAPINAMECallback(int32_t index, int32_t uid)
253 * - TYPE NS_GetCAPINAME(int32_t index)
254 * - void NS_SetCAPINAME(int32_t index, TYPE value)
255 *
256 * @param TYPE the underlying value type (e.g. double)
257 * @param NS the "namespace" (e.g. HALSIM)
258 * @param CAPINAME the C API name (usually first letter capitalized)
259 * @param RETURN what to return from the Get function
260 */
261#define HAL_SIMDATAVALUE_STUB_CAPI(TYPE, NS, CAPINAME, RETURN) \
262 int32_t NS##_Register##CAPINAME##Callback( \
263 int32_t index, HAL_NotifyCallback callback, void* param, \
264 HAL_Bool initialNotify) { \
265 return 0; \
266 } \
267 \
268 void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t uid) {} \
269 \
270 TYPE NS##_Get##CAPINAME(int32_t index) { \
271 return RETURN; \
272 } \
273 \
274 void NS##_Set##CAPINAME(int32_t index, TYPE) {}
275
276/**
277 * Define a stub standard C API for simulation data (channel variant).
278 *
279 * Functions defined:
280 * - int32 NS_RegisterCAPINAMECallback(
281 * int32_t index, int32_t channel, HAL_NotifyCallback callback,
282 * void* param, HAL_Bool initialNotify)
283 * - void NS_CancelCAPINAMECallback(int32_t index, int32_t channel, int32_t uid)
284 * - TYPE NS_GetCAPINAME(int32_t index, int32_t channel)
285 * - void NS_SetCAPINAME(int32_t index, int32_t channel, TYPE value)
286 *
287 * @param TYPE the underlying value type (e.g. double)
288 * @param NS the "namespace" (e.g. HALSIM)
289 * @param CAPINAME the C API name (usually first letter capitalized)
290 * @param RETURN what to return from the Get function
291 */
292#define HAL_SIMDATAVALUE_STUB_CAPI_CHANNEL(TYPE, NS, CAPINAME, RETURN) \
293 int32_t NS##_Register##CAPINAME##Callback( \
294 int32_t index, int32_t channel, HAL_NotifyCallback callback, \
295 void* param, HAL_Bool initialNotify) { \
296 return 0; \
297 } \
298 \
299 void NS##_Cancel##CAPINAME##Callback(int32_t index, int32_t channel, \
300 int32_t uid) {} \
301 \
302 TYPE NS##_Get##CAPINAME(int32_t index, int32_t channel) { \
303 return RETURN; \
304 } \
305 \
306 void NS##_Set##CAPINAME(int32_t index, int32_t channel, TYPE) {}
307
308/**
309 * Define a stub standard C API for simulation data (no index variant).
310 *
311 * Functions defined:
312 * - int32 NS_RegisterCAPINAMECallback(
313 * HAL_NotifyCallback callback, void* param, HAL_Bool initialNotify)
314 * - void NS_CancelCAPINAMECallback(int32_t uid)
315 * - TYPE NS_GetCAPINAME(void)
316 * - void NS_SetCAPINAME(TYPE value)
317 *
318 * @param TYPE the underlying value type (e.g. double)
319 * @param NS the "namespace" (e.g. HALSIM)
320 * @param CAPINAME the C API name (usually first letter capitalized)
321 * @param RETURN what to return from the Get function
322 */
323#define HAL_SIMDATAVALUE_STUB_CAPI_NOINDEX(TYPE, NS, CAPINAME, RETURN) \
324 int32_t NS##_Register##CAPINAME##Callback( \
325 HAL_NotifyCallback callback, void* param, HAL_Bool initialNotify) { \
326 return 0; \
327 } \
328 \
329 void NS##_Cancel##CAPINAME##Callback(int32_t uid) {} \
330 \
331 TYPE NS##_Get##CAPINAME(void) { \
332 return RETURN; \
333 } \
334 \
335 void NS##_Set##CAPINAME(TYPE) {}
336
337} // namespace hal
#define LLVM_ATTRIBUTE_ALWAYS_INLINE
LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do so, mark a method "always...
Definition: Compiler.h:277
void(* HAL_NotifyCallback)(const char *name, void *param, const struct HAL_Value *value)
Definition: NotifyListener.h:9
Simulation data value wrapper.
Definition: SimDataValue.h:88
SimDataValue()
Definition: SimDataValue.h:99
LLVM_ATTRIBUTE_ALWAYS_INLINE SimDataValue & operator=(T value)
Definition: SimDataValue.h:117
LLVM_ATTRIBUTE_ALWAYS_INLINE int32_t RegisterCallback(HAL_NotifyCallback callback, void *param, HAL_Bool initialNotify)
Definition: SimDataValue.h:108
SimDataValue(T value)
Definition: SimDataValue.h:105
LLVM_ATTRIBUTE_ALWAYS_INLINE void Set(T value)
Definition: SimDataValue.h:113
Definition: SimCallbackRegistry.h:20
void Cancel(int32_t uid)
Definition: SimCallbackRegistry.h:28
LLVM_ATTRIBUTE_ALWAYS_INLINE void DoReset()
Definition: SimCallbackRegistry.h:54
wpi::recursive_spinlock m_mutex
Definition: SimCallbackRegistry.h:60
void(*)() RawFunctor
Definition: SimCallbackRegistry.h:22
int32_t DoRegister(RawFunctor callback, void *param)
Definition: SimCallbackRegistry.h:43
std::unique_ptr< CallbackVector > m_callbacks
Definition: SimCallbackRegistry.h:61
Definition: SimDataValue.h:20
void DoSet(T value, const char *name)
Definition: SimDataValue.h:58
LLVM_ATTRIBUTE_ALWAYS_INLINE void CancelCallback(int32_t uid)
Definition: SimDataValue.h:24
wpi::recursive_spinlock & GetMutex()
Definition: SimDataValue.h:39
int32_t DoRegisterCallback(HAL_NotifyCallback callback, void *param, HAL_Bool initialNotify, const char *name)
Definition: SimDataValue.h:42
SimDataValueBase(T value)
Definition: SimDataValue.h:22
T Get() const
Definition: SimDataValue.h:26
void Reset(T value)
Definition: SimDataValue.h:33
T m_value
Definition: SimDataValue.h:72
int32_t HAL_Bool
Definition: Types.h:73
WPILib Hardware Abstraction Layer (HAL) namespace.
Definition: InterruptManager.h:13
HAL Entry Value.
Definition: Value.h:20
recursive_spinlock1 recursive_spinlock
Definition: spinlock.h:141