ChimeraTK-DeviceAccess 03.27.00
Loading...
Searching...
No Matches
RawConverter.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3#pragma once
4
6
7#include <cstdint>
8#include <tuple>
9
11
12 /********************************************************************************************************************/
13
14 // Note: values must match index in SignificantBitsCaseTypes tuple
15 enum class SignificantBitsCase { bit8 = 0, bit16, bit32, bit64, generic };
16
17 /********************************************************************************************************************/
18
20
21 /********************************************************************************************************************/
22
39 template<typename UserType, typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned>
40 class Converter {
41 public:
43
44 UserType toCooked(RawType rawValue);
45
46 RawType toRaw(UserType cookedValue);
47
48 using raw_type = RawType;
49
50 private:
51 // The PromotedRawType has the same width as the RawType but the signedness according to isSigned. We will store the
52 // "promoted" raw value in it, i.e. the arbitrary bit width of the raw has been changed into a proper CPU data type.
53 using PromotedRawType = std::conditional_t<isSigned, std::make_signed_t<RawType>, RawType>;
54
55 // Use double as intermediate conversion target, unless user has requested float (to avoid unnecessary conversion
56 // to float via double).
57 using FloatIntermediate = std::conditional_t<std::is_same_v<UserType, float>, float, double>;
58
59 RawType _signBitMask, _usedBitMask, _unusedBitMask;
60 RawType _minRawValue, _maxRawValue;
61 PromotedRawType _minPromotedRawValue, _maxPromotedRawValue;
62
63 FloatIntermediate _conversionFactor, _inverseConversionFactor;
64
65 // We need the negative fractional bits as a positive value (needed for the bit shift). This field is unused unless
66 // fc == FractionalCase::fixedNegative or fixedNegativeFast.
67 uint32_t _nNegativeFractionalBits;
68 };
69
70 /********************************************************************************************************************/
71
76 public:
77 explicit ConverterLoopHelper(size_t implParameter);
78
79 virtual ~ConverterLoopHelper() = default;
80
98 virtual void doPostRead() = 0;
99
105 virtual void doPreWrite() = 0;
106
113 template<typename UserType, typename Accessor>
114 static std::unique_ptr<ConverterLoopHelper> makeConverterLoopHelper(
115 const ChimeraTK::NumericAddressedRegisterInfo& info, size_t channelIndex, size_t implParameter,
116 Accessor& accessor);
117
121 template<typename UserType, typename RawType, typename Accessor>
122 static std::unique_ptr<ConverterLoopHelper> makeConverterLoopHelperFixedRaw(
123 const ChimeraTK::NumericAddressedRegisterInfo& info, size_t channelIndex, size_t implParameter,
124 Accessor& accessor);
125
126 protected:
127 const size_t _implParameter;
128 };
129
130 /********************************************************************************************************************/
131
132 template<typename UserType, typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned,
133 typename Accessor>
135 public:
137 size_t implParameter, Converter<UserType, RawType, sc, fc, isSigned> converter, Accessor& accessor);
138
139 void doPostRead() override;
140
141 void doPreWrite() override;
142
143 private:
145 Accessor& _accessor;
146 };
147
148 /********************************************************************************************************************/
149
155 template<typename UserType, typename RawType, typename FUN>
156 void withConverter(const ChimeraTK::NumericAddressedRegisterInfo& info, size_t channelIndex, FUN&& fun);
157
158 /********************************************************************************************************************/
159 /********************************************************************************************************************/
160
161 /* Note: Only implementations and helper classes/functions below this point */
162
163 /********************************************************************************************************************/
164 /********************************************************************************************************************/
165
166 namespace detail {
167
168 /******************************************************************************************************************/
169
170 // Helper to get the total number of bits from an integer type. std::numeric_limits::digits just gives you the
171 // number of bits excluding the sign bit.
172 // Note: We should use the std::integral concept here, but clang 18 generates an error with it when using the
173 // template at least in one of the asserts below. Once we can use a newer clang version, we can change this back.
174 template<typename T>
175 constexpr int numberOfBits = std::numeric_limits<T>::digits + (std::is_signed_v<T> ? 1 : 0);
176
177 /******************************************************************************************************************/
178
179 // Note: order must match SignificantBitsCase enum
180 using SignificantBitsCaseTypes = std::tuple<uint8_t, uint16_t, uint32_t, uint64_t>;
181
182 /******************************************************************************************************************/
183
184 constexpr int getMinWidthsForFractionalCase(const FractionalCase& fc) {
185 switch(fc) {
187 return 32;
188 default:
189 return 8;
190 }
191 }
192
193 /******************************************************************************************************************/
194
195 template<typename RawType, typename F>
196 void callForSignificantBitsCase(const ChimeraTK::NumericAddressedRegisterInfo::ChannelInfo& info, F&& fun) {
197 switch(info.width) {
198 case 8:
199 if constexpr(numberOfBits<RawType> >= 8) {
200 std::forward<F>(fun).template operator()<SignificantBitsCase::bit8>();
201 return;
202 }
203 case 16:
204 if constexpr(numberOfBits<RawType> >= 16) {
205 std::forward<F>(fun).template operator()<SignificantBitsCase::bit16>();
206 return;
207 }
208 case 32:
209 if constexpr(numberOfBits<RawType> >= 32) {
210 std::forward<F>(fun).template operator()<SignificantBitsCase::bit32>();
211 return;
212 }
213 case 64:
214 if constexpr(numberOfBits<RawType> >= 64) {
215 std::forward<F>(fun).template operator()<SignificantBitsCase::bit64>();
216 return;
217 }
218 }
219 std::forward<F>(fun).template operator()<SignificantBitsCase::generic>();
220 }
221
222 /******************************************************************************************************************/
223
224 template<typename RawType, typename UserType, typename F>
225 void callForFractionalCase(const ChimeraTK::NumericAddressedRegisterInfo::ChannelInfo& info, F&& fun) {
226 switch(info.dataType) {
228 if(info.nFractionalBits == 0) {
229 std::forward<F>(fun).template operator()<FractionalCase::integer>();
230 return;
231 }
232 else if(info.nFractionalBits > 0) {
233 std::forward<F>(fun).template operator()<FractionalCase::fixedPositive>();
234 return;
235 }
236 else if(info.nFractionalBits < 0) {
237 if constexpr(std::is_integral_v<UserType>) {
238 if(numberOfBits<UserType> >= int(info.width) - info.nFractionalBits &&
239 std::is_signed_v<UserType> == info.signedFlag) {
240 // We can make an even faster conversion if we have an integral target UserType of the correct
241 // signedness and the bit shifted raw value still fits directly into it.
242 std::forward<F>(fun).template operator()<FractionalCase::fixedNegativeFast>();
243 return;
244 }
245 }
246 std::forward<F>(fun).template operator()<FractionalCase::fixedNegative>();
247 return;
248 }
249 break;
251 if constexpr(numberOfBits<RawType> >= 32) {
252 std::forward<F>(fun).template operator()<FractionalCase::ieee754_32>();
253 return;
254 }
255 break;
258 break;
259 }
260 throw std::logic_error("NOT IMPLEMENTED");
261 }
262
263 /******************************************************************************************************************/
264
265 template<typename UserType, typename RawType, typename F>
266 void callWithConverterParamsFixedRaw(
267 const ChimeraTK::NumericAddressedRegisterInfo& info, size_t channelIndex, F&& fun) {
268 // get number of bits from info and determine SignificantBitsCase
269 detail::callForSignificantBitsCase<RawType>(info.channels[channelIndex], [&]<SignificantBitsCase sc> {
270 // get number of fractional bits from info and determine FractionalCase
271 detail::callForFractionalCase<RawType, UserType>(info.channels[channelIndex], [&]<FractionalCase fc> {
272 if constexpr(numberOfBits<RawType> >= detail::getMinWidthsForFractionalCase(fc)) {
273 if constexpr(fc == FractionalCase::ieee754_32) {
274 // special case: IEEE754 is always signed, so we can avoid an additional code instance
275 std::forward<F>(fun).template operator()<RawType, sc, fc, true>();
276 }
277 else if constexpr(fc == FractionalCase::fixedNegativeFast) {
278 // special case: fixed negative fast has some special requirements on the types, so we try not to
279 // instantiate impossible combinations (to speed up compilation time)
280 assert(numberOfBits<UserType> >= int(info.channels[channelIndex].width) -
281 info.channels[channelIndex].nFractionalBits); // ensured by callForFractionalCase
282 assert(std::is_signed_v<UserType> ==
283 info.channels[channelIndex].signedFlag); // ensured by callForFractionalCase
284 std::forward<F>(fun).template operator()<RawType, sc, fc, std::is_signed_v<UserType>>();
285 }
286 else {
287 // Fractional/Integers: distinguish signed/unsigned and do the call
288 if(info.channels[channelIndex].signedFlag) {
289 std::forward<F>(fun).template operator()<RawType, sc, fc, true>();
290 }
291 else {
292 std::forward<F>(fun).template operator()<RawType, sc, fc, false>();
293 }
294 }
295 }
296 else {
297 throw ChimeraTK::logic_error(
298 std::format("Specified raw data width of {} bits does not fit into the significant bit "
299 "width of {} bits for register '{}', channel {}.",
300 numberOfBits<RawType>, detail::getMinWidthsForFractionalCase(fc),
301 std::string(info.getRegisterName()), channelIndex));
302 }
303 });
304 });
305 }
306
307 /******************************************************************************************************************/
308
309 template<typename UserType, typename F>
310 void callWithConverterParams(const ChimeraTK::NumericAddressedRegisterInfo& info, size_t channelIndex, F&& fun) {
311 // Special case for FundamentalType::nodata, i.e. RawType = ChimeraTK::Void. Use full template specialisation.
312 // This special case saves us a couple of unnecessary code instantiations and hence speeds up compile time.
314 std::forward<F>(fun)
316 return;
317 }
318
319 // get RawType from info
320 callForRawType(info.channels[channelIndex].getRawType(), [&](auto x) {
321 using RawType = std::make_unsigned_t<decltype(x)>;
322 if constexpr(std::is_same_v<UserType, ChimeraTK::Void>) {
323 // Special case for UserType = ChimeraTK::Void. Use full template specialisation. We do not care about
324 // details in the conversion, since there is only one possible value. This special case saves us a couple of
325 // unnecessary code instantiations and hence speeds up compile time.
326 std::forward<F>(fun)
327 .template operator()<RawType, SignificantBitsCase::generic, FractionalCase::integer, false>();
328 }
329 else {
330 // Regular case for non-void UserTypes
331 callWithConverterParamsFixedRaw<UserType, RawType>(info, channelIndex, fun);
332 }
333 });
334 }
335
336 /******************************************************************************************************************/
337
338 template<std::size_t... Is>
339 constexpr auto makeUnusedBitMaskTable(std::index_sequence<Is...>) {
340 return std::array<uint64_t, 65>{(Is < 64 ? (~uint64_t{} << Is) : 0)...};
341 }
342
343 /******************************************************************************************************************/
344
345 template<std::size_t... Is>
346 constexpr auto makeUsedBitMaskTable(std::index_sequence<Is...>) {
347 return std::array<uint64_t, 65>{(Is < 64 ? (~(~uint64_t{} << Is)) : ~uint64_t{})...};
348 }
349
350 /******************************************************************************************************************/
351
352 template<std::size_t... Is>
353 constexpr auto makeSignBitMaskTable(std::index_sequence<Is...>) {
354 return std::array<uint64_t, 65>{(Is > 0 ? (uint64_t{1} << (Is - 1)) : 0)...};
355 }
356
357 /******************************************************************************************************************/
358
359 constexpr auto unusedBitMaskTable = makeUnusedBitMaskTable(std::make_index_sequence<65>{});
360 constexpr auto usedBitMaskTable = makeUsedBitMaskTable(std::make_index_sequence<65>{});
361 constexpr auto signBitMaskTable = makeSignBitMaskTable(std::make_index_sequence<65>{});
362
363 /******************************************************************************************************************/
364
365 // returns the raw value "promoted" to the full RawType
366 template<typename PromotedRawType, typename RawType, SignificantBitsCase significantBitsCase>
367 constexpr PromotedRawType interpretArbitraryBitInteger(
368 RawType signBitMask, RawType usedBitMask, RawType unusedBitMask, RawType rawValue) {
369 static_assert(std::is_integral_v<RawType>);
370 static_assert(!std::is_signed_v<RawType>);
371
372 if constexpr(significantBitsCase == SignificantBitsCase::generic) {
373 if constexpr(std::is_signed_v<PromotedRawType>) {
374 if(!(rawValue & signBitMask)) { // sign bit not set: force unused bits to zero
375 return PromotedRawType(rawValue & usedBitMask);
376 }
377 // sign bit set: force unused bits to one
378 return PromotedRawType(rawValue | unusedBitMask);
379 }
380 else {
381 // unsigned value: force unused bits to zero
382 return PromotedRawType(rawValue & usedBitMask);
383 }
384 }
385 else {
386 // Here we make use of the ability of the CPU to actually understand a type with the given number of bits, so we
387 // can promote it to our PromotedRawType with standard cast operations. Keep in mind that this has nothing to do
388 // with the width of the RawType. Example: RawType is uint32_t but we have 8 significant bits (including sign
389 // bit). We will cast the raw value into (u)int8_t (depending on the selected signedness) and then into the
390 // PromotedRawType. This cuts away any extra bits and properly takes care of the sign.
391 using UnsignedType = std::tuple_element_t<int(significantBitsCase), SignificantBitsCaseTypes>;
392 using SignedType = std::make_signed_t<UnsignedType>;
393 if constexpr(std::is_signed_v<PromotedRawType>) {
394 return PromotedRawType(SignedType(rawValue));
395 }
396 else {
397 return PromotedRawType(UnsignedType(rawValue));
398 }
399 }
400 }
401
402 /******************************************************************************************************************/
403
404 } // namespace detail
405
406 /********************************************************************************************************************/
407 /********************************************************************************************************************/
408
409 template<typename UserType, typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned>
412 : _signBitMask(isSigned ? detail::signBitMaskTable[info.width] : 0),
413 _usedBitMask(detail::usedBitMaskTable[info.width]), _unusedBitMask(detail::unusedBitMaskTable[info.width]) {
414 // sanity check for parameters
417 constexpr uint32_t maxRawWidth = detail::numberOfBits<uint64_t>;
418 if(info.width > maxRawWidth) {
420 std::format("RawConverter cannot deal with a bit width of {} > {}.", info.width, maxRawWidth));
421 }
422 if(info.nFractionalBits > int32_t(info.width)) {
423 throw ChimeraTK::logic_error(std::format(
424 "RawConverter cannot deal with {} fractional bits (larger than total width).", info.nFractionalBits));
425 }
426 if(info.nFractionalBits < -int32_t(maxRawWidth - info.width)) {
427 throw ChimeraTK::logic_error(std::format(
428 "RawConverter cannot deal with {} fractional bits (too negative, result doesn't fit in {} bits).",
429 info.nFractionalBits, maxRawWidth));
430 }
431 }
432 // Fixed point conversion uses either floating-point conversion factors or bit shift
435 // This is used for toRaw conversions, where we never do bit shifts to get proper rounding
436 _inverseConversionFactor = std::pow(FloatIntermediate(2), info.nFractionalBits);
437 }
438 if constexpr(fc == FractionalCase::fixedPositive ||
439 ((fc == FractionalCase::fixedNegative) && std::is_floating_point_v<UserType>)) {
440 // This is used for toCooked conversions when floating point is involved (fixedNegativeFast has always integral
441 // UserTypes)
442 _conversionFactor = FloatIntermediate(1.) / _inverseConversionFactor;
443 }
444 else if constexpr(fc == FractionalCase::fixedNegative || fc == FractionalCase::fixedNegativeFast) {
445 // This is used for toCooked conversions when we can to a bit shift (int-to-int with negative fractional bits)
446 _nNegativeFractionalBits = -info.nFractionalBits;
447 }
448
449 // toRaw conversion needs to know minimum and maximum value range for clamping
450 // This must come last, since we already call toCooked (which does not need these values for clamping)
453 if constexpr(isSigned) {
454 _maxRawValue = _usedBitMask ^ _signBitMask;
455 _minRawValue = _signBitMask;
456 }
457 else {
458 _maxRawValue = _usedBitMask;
459 _minRawValue = 0;
460 }
461 _maxPromotedRawValue = _maxRawValue;
462 _minPromotedRawValue = detail::interpretArbitraryBitInteger<PromotedRawType, RawType, sc>(
463 _signBitMask, _usedBitMask, _unusedBitMask, _minRawValue);
464 }
465 else {
466 static_assert(fc == FractionalCase::ieee754_32);
467 _maxRawValue = RawType(std::bit_cast<uint32_t>(std::numeric_limits<float>::max()));
468 _minRawValue = RawType(std::bit_cast<uint32_t>(std::numeric_limits<float>::lowest()));
469 }
470 }
471
472 /********************************************************************************************************************/
473
474 template<typename UserType, typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned>
476 if constexpr(fc == FractionalCase::integer) {
477 auto promotedRawValue = detail::interpretArbitraryBitInteger<PromotedRawType, RawType, sc>(
478 _signBitMask, _usedBitMask, _unusedBitMask, rawValue);
479 return numericToUserType<UserType>(promotedRawValue);
480 }
481 else if constexpr(fc == FractionalCase::fixedPositive ||
482 (fc == FractionalCase::fixedNegative && std::is_floating_point_v<UserType>)) {
483 // Positive fractional bits will always convert through an intermediate float, to have proper rounding.
484 // Negative fractional bits can use the same code path efficiently, if the UserType is floating point.
485 auto promotedRawValue = detail::interpretArbitraryBitInteger<PromotedRawType, RawType, sc>(
486 _signBitMask, _usedBitMask, _unusedBitMask, rawValue);
487 return numericToUserType<UserType>(FloatIntermediate(promotedRawValue) * _conversionFactor);
488 }
489 else if constexpr(fc == FractionalCase::fixedNegative) {
490 // In signed case, make sure to fill up the leading 1s if negative.
491 // @TODO Write test for signed negative case!!
492 using IntIntermediate = std::conditional_t<isSigned, int64_t, uint64_t>;
493 auto promotedRawValue = IntIntermediate(detail::interpretArbitraryBitInteger<PromotedRawType, RawType, sc>(
494 _signBitMask, _usedBitMask, _unusedBitMask, rawValue));
495 auto intermediateUnsigned = uint64_t(promotedRawValue);
496 intermediateUnsigned <<= _nNegativeFractionalBits;
497 return numericToUserType<UserType>(IntIntermediate(intermediateUnsigned));
498 }
499 else if constexpr(fc == FractionalCase::fixedNegativeFast) {
500 // This is the shortcut which avoids promoting to a 64 bit intermediate type and the final conversion to the
501 // UserType. Since we know, that the UserType is integral and has a wide-enough bit width, we can directly promote
502 // into the UserType and apply the bit shift there.
503 static_assert(std::is_integral_v<UserType>);
504 static_assert(std::is_signed_v<UserType> == std::is_signed_v<PromotedRawType>);
505 auto promotedRawValue = UserType(detail::interpretArbitraryBitInteger<PromotedRawType, RawType, sc>(
506 _signBitMask, _usedBitMask, _unusedBitMask, rawValue));
507 auto intermediateUnsigned = std::make_unsigned_t<UserType>(promotedRawValue);
508 intermediateUnsigned <<= _nNegativeFractionalBits;
509 return UserType(intermediateUnsigned);
510 }
511 else {
512 static_assert(fc == FractionalCase::ieee754_32);
513 static_assert(detail::numberOfBits<RawType> >= 32);
514 static_assert(std::numeric_limits<float>::is_iec559);
515 auto promotedRawValue = detail::interpretArbitraryBitInteger<PromotedRawType, RawType, sc>(
516 _signBitMask, _usedBitMask, _unusedBitMask, rawValue);
517 return numericToUserType<UserType>(std::bit_cast<float>(uint32_t(promotedRawValue)));
518 }
519 }
520
521 /********************************************************************************************************************/
522
523 template<typename UserType, typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned>
525 PromotedRawType promotedRawValue;
526
527 if constexpr(fc == FractionalCase::integer) {
528 promotedRawValue = userTypeToNumeric<PromotedRawType>(cookedValue);
529 }
530 else if constexpr(fc == FractionalCase::fixedPositive || fc == FractionalCase::fixedNegative ||
532 // All fractional bit cases will always convert through an intermediate float, to have proper rounding.
533 promotedRawValue = userTypeToNumeric<PromotedRawType>(
534 userTypeToNumeric<FloatIntermediate>(cookedValue) * _inverseConversionFactor);
535 }
536 else {
537 static_assert(fc == FractionalCase::ieee754_32);
538 static_assert(std::numeric_limits<RawType>::digits >= 32);
539 static_assert(std::numeric_limits<float>::is_iec559);
540 promotedRawValue = PromotedRawType(std::bit_cast<uint32_t>(userTypeToNumeric<float>(cookedValue)));
541 // no need to apply usedBitMask, and no need for a range check (already done in userTypeToNumeric)
542 return RawType(promotedRawValue);
543 }
544
545 // Range check for the conversion to the arbitrary bit width (by applying the _usedBitMask). The range change in the
546 // userTypeToNumeric calls above was insufficient in cases we have e.g. 18 bits.
547 if(promotedRawValue < _minPromotedRawValue) {
548 return _minRawValue;
549 }
550 if(promotedRawValue > _maxPromotedRawValue) {
551 return _maxRawValue;
552 }
553 return RawType(promotedRawValue) & _usedBitMask;
554 }
555
556 /********************************************************************************************************************/
557 /********************************************************************************************************************/
558
559 template<typename UserType, typename Accessor>
560 std::unique_ptr<ConverterLoopHelper> ConverterLoopHelper::makeConverterLoopHelper(
561 const ChimeraTK::NumericAddressedRegisterInfo& info, size_t channelIndex, size_t implParameter,
562 Accessor& accessor) {
563 std::unique_ptr<ConverterLoopHelper> rv;
564
565 detail::callWithConverterParams<UserType>(
566 info, channelIndex, [&]<typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned> {
567 Converter<UserType, RawType, sc, fc, isSigned> converter(info.channels[channelIndex]);
568 rv = std::make_unique<ConverterLoopHelperImpl<UserType, RawType, sc, fc, isSigned, Accessor>>(
569 implParameter, converter, accessor);
570 });
571
572 assert(rv != nullptr);
573 return rv;
574 }
575
576 /********************************************************************************************************************/
577
578 template<typename UserType, typename RawType, typename Accessor>
579 std::unique_ptr<ConverterLoopHelper> ConverterLoopHelper::makeConverterLoopHelperFixedRaw(
580 const ChimeraTK::NumericAddressedRegisterInfo& info, size_t channelIndex, size_t implParameter,
581 Accessor& accessor) {
582 std::unique_ptr<ConverterLoopHelper> rv;
583
584 RawConverter::detail::callWithConverterParamsFixedRaw<UserType, RawType>(info, channelIndex,
585 [&]<typename RawTypeAgain, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
586 bool isSigned> {
587 static_assert(std::is_same_v<RawTypeAgain, RawType>);
589 info.channels[0]);
590 rv = std::make_unique<RawConverter::ConverterLoopHelperImpl<UserType, std::make_unsigned_t<RawTypeAgain>, sc,
591 fc, isSigned, Accessor>>(implParameter, converter, accessor);
592 });
593
594 assert(rv != nullptr);
595 return rv;
596 }
597
598 /********************************************************************************************************************/
599
600 inline ConverterLoopHelper::ConverterLoopHelper(size_t implParameter) : _implParameter(implParameter) {}
601
602 /********************************************************************************************************************/
603
604 template<typename UserType, typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned,
605 typename Accessor>
607 size_t implParameter, Converter<UserType, RawType, sc, fc, isSigned> converter, Accessor& accessor)
608 : ConverterLoopHelper(implParameter), _converter(converter), _accessor(accessor) {}
609
610 /********************************************************************************************************************/
611
612 template<typename UserType, typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned,
613 typename Accessor>
615 _accessor.template doPostReadImpl<UserType, RawType, sc, fc, isSigned>(_converter, _implParameter);
616 }
617
618 /********************************************************************************************************************/
619
620 template<typename UserType, typename RawType, SignificantBitsCase sc, FractionalCase fc, bool isSigned,
621 typename Accessor>
623 _accessor.template doPreWriteImpl<UserType, RawType, sc, fc, isSigned>(_converter, _implParameter);
624 }
625
626 /********************************************************************************************************************/
627
628 template<typename UserType, typename RawType, typename FUN>
629 void withConverter(const ChimeraTK::NumericAddressedRegisterInfo& info, size_t channelIndex, FUN&& fun) {
630 detail::callWithConverterParamsFixedRaw<UserType, RawType>(
631 info, channelIndex, [&]<typename RawTypeAgain, SignificantBitsCase sc, FractionalCase fc, bool isSigned> {
632 static_assert(std::is_same_v<RawTypeAgain, RawType>);
633 Converter<UserType, RawType, sc, fc, isSigned> converter(info.channels[channelIndex]);
634 std::forward<FUN>(fun).operator()(converter);
635 });
636 }
637
638 /********************************************************************************************************************/
639 /********************************************************************************************************************/
640
644 template<typename UserType, typename RawType>
645 requires(std::is_same_v<UserType, ChimeraTK::Void> || std::is_same_v<RawType, ChimeraTK::Void>)
647 public:
649
650 UserType toCooked(RawType rawValue);
651
652 RawType toRaw(UserType cookedValue);
653 };
654
655 /********************************************************************************************************************/
656
657 template<typename UserType, typename RawType>
658 requires(std::is_same_v<UserType, ChimeraTK::Void> || std::is_same_v<RawType, ChimeraTK::Void>)
663
664 /********************************************************************************************************************/
665
666 template<typename UserType, typename RawType>
667 requires(std::is_same_v<UserType, ChimeraTK::Void> || std::is_same_v<RawType, ChimeraTK::Void>)
671
672 /********************************************************************************************************************/
673
674} // namespace ChimeraTK::RawConverter
FundamentalType fundamentalType() const
Get the fundamental data type.
std::vector< ChannelInfo > channels
Define per-channel information (bit interpretation etc.), 1D/scalars have exactly one entry.
Converter class for conversions from raw to cooked values.
UserType toCooked(RawType rawValue)
RawType toRaw(UserType cookedValue)
Abstract base class to implement erasure of the exact Converter type.
static std::unique_ptr< ConverterLoopHelper > makeConverterLoopHelper(const ChimeraTK::NumericAddressedRegisterInfo &info, size_t channelIndex, size_t implParameter, Accessor &accessor)
Create ConverterLoopHelper with the Converter object matching the given RegisterInfo and channel.
virtual void doPostRead()=0
Call doPostReadImpl on the accessor passed to makeConverterLoopHelper().
static std::unique_ptr< ConverterLoopHelper > makeConverterLoopHelperFixedRaw(const ChimeraTK::NumericAddressedRegisterInfo &info, size_t channelIndex, size_t implParameter, Accessor &accessor)
More efficient version of makeConverterLoopHelper if the raw type is already known in the calling cod...
virtual void doPreWrite()=0
Call doPreWriteImpl on the accessor passed to makeConverterLoopHelper().
ConverterLoopHelperImpl(size_t implParameter, Converter< UserType, RawType, sc, fc, isSigned > converter, Accessor &accessor)
void doPreWrite() override
Call doPreWriteImpl on the accessor passed to makeConverterLoopHelper().
void doPostRead() override
Call doPostReadImpl on the accessor passed to makeConverterLoopHelper().
Wrapper Class for void.
Definition Void.h:15
Exception thrown when a logic error has occured.
Definition Exception.h:51
Concept requiring a type to be one of the supported ChimeraTK RawTypes.
void withConverter(const ChimeraTK::NumericAddressedRegisterInfo &info, size_t channelIndex, FUN &&fun)
Create Converter matching the given register info and channel, and call the functor object passing th...
void callForRawType(const DataType &type, LAMBDATYPE lambda)
callForRawType() is similar to callForType(), just with a subset of supported data types which can be...
uint32_t width
Number of significant bits in the register.