CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
bitpack.hh
1#pragma once
2
3/**
4 * \defgroup bitpacks Bitpacks
5 *
6 * \section bitpacks_intro Introduction
7 *
8 * `Bitpack`-s are a way to handle situations where many flags and/or fields of
9 * arbitrary widths are packed into an underlying integer (such as a `uint32_t`
10 * and referred to as a `Bitpack`'s `Storage` type) at arbitrary bit alignments.
11 * This is similar to C and C++'s bitfields, but more portable, more versatile,
12 * and with a type-centric interface. They encapsulate the bitwise AND, OR, and
13 * shift operations required to get at spans of bits within a word. They are
14 * meant to be particularly useful for handling memory mapped I/O (MMIO)
15 * registers for hardware drivers.
16 *
17 * Those readers interested in formal details are invited to see \ref
18 * bitpacks_formal.
19 *
20 * `Bitpack`-s support the following features:
21 *
22 * * Arbitrary width numeric fields at arbitrary bit position within an
23 * underlying integer type.
24 *
25 * * Arbitrary width enumeration fields, again at arbitrary position.
26 *
27 * * `const` fields to represent, for example, fields mutable only by hardware.
28 *
29 * * `volatile` `Bitpacks` require overt reads and writes, making it easier to
30 * map program source to memory or register updates. Individual methods'
31 * documentation will call out special handling of `volatile` forms.
32 *
33 * * a type-centric perspective on fields, with type-based accessors (getters,
34 * setters, read-modify-write helpers, &c).
35 *
36 * * usable in `constexpr` contexts.
37 *
38 * \subsection bitpacks_intro_defining Defining Bitpacks and Fields
39 *
40 * To define a bitpack, begin by defining a `class` (or `struct`) that inherits
41 * from the `Bitpack` template given below, specifying the underlying `Storage`
42 * type as the template parameter. It usually suffices to pick one of the
43 * `uintNN_t` `cstdint` types. For reasons of C++, one often wants a bit of
44 * syntax at the top of these definitions, which is encapsulated in the
45 * `BITPACK_USUAL_PREFIX` macro; add that at the top of your derived class. We
46 * then need to specify fields of the bitpack in question.
47 *
48 * For each field, this means, ultimately, defining a call to the
49 * `Bitpack::member` method that users of the bitpack can call to access that
50 * field. While it is perfectly sensible to do so directly, this file also
51 * offers a large number of utilities and affordances which capture common
52 * occurrences. We will need to specify the type of the field as seen from C++,
53 * though in many cases one will simultaneously define the field and its type.
54 * Further, one will need to specify the `FieldInfo` for the field: this is a
55 * structure holding, in order, the minimum and maximum (both inclusive) bit
56 * positions occupied by the field in the underlying word, and a (default false)
57 * flag to indicate that the field is constant; many of our macros will simply
58 * let us write the values of these properties in order as arguments.
59 *
60 * At the lowest level of assistance, the `BITPACK_MEMBER_ADD` macro
61 * encapsulates the C++ syntax for a method wrapping a call to `member`. It
62 * takes the name of the method to define, the type of the field, and the values
63 * to use for the `FieldInfo`.
64 *
65 * \remark
66 * \parblock
67 *
68 * For example, `BITPACK_MEMBER_ADD(ticks, uint8_t, 3, 9);` defines a method
69 * `ticks()` that gives `uint8_t`-flavored access to a field spanning bits 3 to
70 * 9 of the bitpack's underlying word. Callers of this method get a `Proxy` of
71 * the bitpack that can be used to get, set, or alter (read-modify-write) that
72 * field and will see the field's value as a `uint8_t`.
73 *
74 * \endparblock
75 *
76 * Most of the time, though, just as defining a bitpack creates a new C++ type,
77 * one can think of a field within a bitpack as having a unique type within its
78 * containing bitpack. As such, most of our definition utility macros serve to
79 * define a field while simultaneously defining a type which is both used as the
80 * type of values of the field and as a name for the field. Behind the scenes,
81 * there is some C++ machinery to provide a `Bitpack::member<FieldType>`
82 * template method that can look up a field's `FieldInfo` from its type; our
83 * macros encapsulate that machinery.
84 *
85 * Often, fields take on enumerated values, for which C++ `enum class` types are
86 * usually reasonably convenient representations. The
87 * `BITPACK_MEMBER_ADD_ENUM` macro encapsulates the syntax for defining an
88 * `enum class` (with given underlying type) and an associated `FieldInfo`.
89 *
90 * \remark
91 * \parblock
92 *
93 * For example, this block of code...
94 *
95 * BITPACK_MEMBER_ADD_ENUM(Drivers, uint8_t, 12, 13)
96 * {
97 * None = 0,
98 * UpOnly = 1,
99 * DownOnly = 2,
100 * Both = 3,
101 * };
102 *
103 * defines an `enum class Drivers` within the bitpack containing it (multiple
104 * bitpack definitions may each have their own `Drivers` type within, but each
105 * such may have only one). This enumeration has an underlying type of
106 * `uint8_t` and four defined values: `Drivers::None`, `Drivers::UpOnly`, &c.
107 * The numeric values of the enumerators are used as values of the field within
108 * the bitpack. At the same time, the macro will have defined a `FieldInfo`
109 * calling for a non-constant, two-bit field at positions 12 and 13. and
110 * associated this with the `Drivers` type, so that a call to
111 * `member<Drivers>()` within the bitpack will return a `Proxy` for that field
112 * which works with `Drivers`-typed values.
113 *
114 * \endparblock
115 *
116 * It is often useful to have single-bit fields with custom names for the
117 * 0/`false` and 1/`true` values. This is provided by the
118 * `BITPACK_MEMBER_ADD_ENUM_BOOL` macro; some popular options for names are
119 * provided as wrappers thereof.
120 *
121 * Sometimes fields are numeric rather than enumerative, such as clock dividers
122 * or event counters. `Bitpack`-s have a `Numeric<Base>` template class to
123 * facilitate creating bespoke C++ types for such fields, and the
124 * `BITPACK_MEMBER_ADD_NUMERIC` macro encapsulates the syntactic clutter of its
125 * use.
126 *
127 * \remark
128 * \parblock
129 *
130 * For example, `BITPACK_MEMBER_ADD_NUMERIC(Ticks, uint16_t, 14, 25, true);`
131 * defines a type `Ticks`, which wraps a `uint16_t`, within the bitpack
132 * containing it. Like with `BITPACK_MEMBER_ADD_ENUM`, this also defines a
133 * `FieldInfo` and associates it with the `Ticks` type, so that
134 * `member<Ticks>()` within the bitpack will return a proxy of this field. The
135 * `true` at the end sets the `FieldInfo` `isConst` flag and so will inhibit
136 * attempts to use the `member()` field proxy to change its value.
137 *
138 * \endparblock
139 *
140 * \subsection bitpacks_intro_proxies Using Bitpacks
141 *
142 * Having defined a bitpack and its fields and internal machinery, one can now
143 * make use of it! Most often, bitpacks are used directly by external code;
144 * that is, the proxies of its fields are public rather than being used by
145 * methods of the bitpack class itself (though, of course, that also works).
146 * As such, C++'s somewhat inflexible syntax rears its head on occasion, and
147 * bitpacks offer macros in an attempt to compensate.
148 *
149 * As a consequence of defining types (of fields) within other types (their
150 * contianing bitpack class), the former are in scope outside the latter only
151 * when qualified with the name of the latter. Having to always write out the
152 * full `MyBitpackClass::TheFieldType` would be exhausting and distracting.
153 * Conveniently, with a bitpack value in hand, `decltype` gives us a generic way
154 * to refer to its type and can be used to qualify the names of contained types.
155 * The `BITPACK_MEMBER_DECLTYPE(b, T)` macro gets the `Proxy` of the `T`-typed
156 * field within the bitpack value `b` without needing to spell out the latter's
157 * type. If the type of `b` is dependent (for example, is `auto` or is or
158 * involves a template argument), use `BITPACK_MEMBER_DEPENDENT(b, T)` instead.
159 * See \ref bitpacks_macros_member .
160 *
161 * `Bitpack::Field::Proxy`-s offer six core methods:
162
163 * 1. Projection ("getter") of the field's current value within the containing
164 * bitpack. Proxies have implicit conversions to their field's type, so in
165 * many cases, projection is syntactiaclly free, When it must be made
166 * explicit, projection is available as the `raw()` method on the proxy.
167 * Proxies of field types with underlying types (specifically, `enum` and
168 * `enum class` types and types derived from the `Bitpack::Numeric` template
169 * class) also have a `rawer()` method that will return the field's value as
170 * this underlying type.
171
172 * 2. Assignment ("setter"), in the form of an overloaded `=` operator. This
173 * mutates the underlying `Storage` value such that the field being proxied
174 * now has the given value. All other bits in the bitpack are unaltered.
175 *
176 * 3. Modification ("read modify write", "RMW"), in the form of the `alter()`
177 * method. `alter()` takes a callback, which should take one argument, whose
178 * type is the field's, and return another value of the same type. The
179 * callback will be given the field's current value in the containing bitpack
180 * and the containing bitpack will be updated so that the field's value is as
181 * returned from the callback.
182 *
183 * 4. Cloning with override, in the form of the `with()` methods. These return
184 * a copy of the bitpack with the proxied field changed as directed.
185 * `with()` can be given the new field value directly, or it may be given a
186 * callback that takes the field's current value and returns the desired new
187 * value.
188 *
189 * 5. Comparison. For convenience, `Proxy`-s overload the spaceship operator,
190 * `<=>`, and so implicitly also provide `<`, `<=`, `==`, `!=`, `>=`, and `>`
191 * between pairs of `Proxy`-s of the same field or between a `Proxy` and a
192 * value of its field's type.
193 *
194 * 6. Assignment from zero, in the form of the `assign_from()` method.
195 * `assign_from` takes a callback which should return a value of the field's
196 * type when given the zero value of that type. This is sort of like
197 * `alter()`, except that it does not extract the field's current value
198 * first. Mostly, this is useful for polymorpic type shenanigans.
199 *
200 * Atop this core, there are many convenience macros for working with bitpacks
201 * and field proxies.
202 *
203 * Because `Bitpack`-s encourage the use of types and named values defined
204 * within derived classes, code using `Bitpack`-s often needs to use qualified
205 * names or wrap values in type constructors (in addition to qualifying the
206 * field type with the bitpack's type when constructing the field proxy as in
207 * the `BITPACK_MEMBER_` macros above). The first two families of macros atop
208 * proxies help with these cases.
209 *
210 * * The family of \ref bitpacks_macros_proxyop_qualify is built around the
211 * `BITPACK_OPERATE_QUALIFY(proxy, operator, value)` macro, which qualifies
212 * `value` with the field type of the proxy `proxy` and relates the proxy and
213 * qualified value with `operator`, which is often `=` for assignment or `==`
214 * for equality testing. For example,
215 * `BITPACK_OPERATE_QUALIFY(p, =, UpOnly)`, with `p` a `Proxy` of a
216 * `Drivers`-typed field (recall above), would assign the field to `UpOnly`
217 * without needing to use a fully qualified form like
218 * `MyBitpack::Drivers::UpOnly`.
219 *
220 * * The family of \ref bitpacks_macros_proxyop_construct is built around the
221 * `BITPACK_OPERATE_WRAP(proxy, operator, value)` macro. Here, the `value` is
222 * passed to a single-argument constructor of the proxy's field type. This
223 * works well for `Numeric` types: `BITPACK_OPERATE_WRAP(p, ==, 7)` will
224 * compare the value of the field being proxied by `p` with the value `7`
225 * converted to that field's type (by said type's constructor).
226 *
227 * Each of these families have further wrappers around their center macros:
228 *
229 * * `BITPACK_OPERATE_{QUALIFY,WRAP}_{DECLTYPE,DEPENDENT}(b, T, operator, v)`
230 * fuse `BITPACK_OPERATE_{QUALIFY,WRAP}` and
231 * `BITPACK_MEMBER_{DECLTYPE,DEPENDENT}`, building the `Proxy`
232 * of the `T`-typed field in the bitpack value `b`, rather than requiring it
233 * to be explicitly built first.
234 *
235 * * For the case where the type `T` does not need qualification when finding
236 * its associated field in `b`, use the
237 * `BITPACK_OPERATE_{QUALIFY,WRAP}_TYPE(b, T, operator, v)` macro (which can
238 * use `b.member<T>()`, rather than a `BITPACK_MEMBER_` macro, internally).
239 *
240 * * `BITPACK_WITH_{QUALIFY_WRAP}(proxy, value)` and
241 * `BITPACK_WITH_{QUALIFY,WRAP}_{DECLTYPE,DEPENDENT,TYPE}(b, T, v)` are
242 * specializations of their `BITPACK_OPERATE_...` siblings with `.with` as the
243 * operator, as this is a common enough occurrence.
244 *
245 * The family of \ref bitpacks_macros_proxyop_directed is somewhat dual: rather
246 * than using the field type to in some way influence the meaning of a `value`,
247 * this family uses the type of the value to find the appropriate field and a
248 * proxy thereof within a bitpack value. This can be convenient when a field
249 * value is already available. The centerpiece of this family is the
250 * `BITPACK_OPERATE_VALUE(b, operator, value)` macro, which relates with
251 * `operator` a proxy of the field in the bitpack value `b` whose type is that
252 * of `value` and `value` itself. The
253 * `BITPACK_OPERATE_{DECLTYPE,DEPENDENT}(b, operator, value)` wrappers qualify
254 * `value` with the type of the bitpack value `b`. As above, there are also
255 * `_WITH` specializations of the `_OPERATE` forms.
256 *
257 * There are other macros likely of more niche interest; see...
258 *
259 * * \ref bitpacks_macros_proxyop_enum and
260 * * \ref bitpacks_macros_vararg .
261 *
262 * \section bitpacks_formal A More Formal Perspective
263 *
264 * More formally, `Bitpack`-s are a kind of aggregate structure within a numeric
265 * word and offer lens-like, typed access to contiguous spans of bits therein,
266 * offering a view of bits as a product of typed fields, occuping a disjoint,
267 * contiguous span of bits within that word.
268 *
269 * The static information about a field is represented by a specialization of
270 * the `Bitpack::Field<Type, Info>` template. `Type` is the exposed C++ type of
271 * the field in question, and `Info` is a (necessarily constexpr) value of the
272 * `FieldInfo` type, which holds the bit indicies of the field's span within the
273 * `Storage` type. `FieldInfo` also has an `isConst` flag, used to specify that
274 * the field is not mutable from software even if the larger bitpack is
275 * non-constant.
276 *
277 * Our lenses, the centerpiece of the whole thing, are expressed as "proxy"
278 * objects, instances of specializations of the [Bitpack::Field<Type,
279 * Info>::Proxy<DerivedBitpack, RefType>](#Bitpack::Field::Proxy) class, which
280 * wrap references to the underlying `Storage`-typed word in a `Bitpack` class
281 * (or a derived class). The type of such references, `RefType`, inherits any
282 * `const` and/or `volatile` qualification of the user's handle to the bitpack,
283 * and are additionally `const` qualified if the associated `FieldInfo` has
284 * `isConst` asserted.
285 *
286 * @{
287 */
288
289#include <__macro_map.h>
290#include <cdefs.h>
291#include <concepts>
292#include <limits.h>
293#include <stddef.h>
294#include <type_traits>
295
296/*
297 * Mark the Bitpack class declaration with cdef.h's `CHERIOT_EXPERIMENTAL()` --
298 * that is, as eliciting a warning if CHERIOT_EXPERIMENTAL_APIS_WARN is defined
299 * -- unless CHERIOT_EXPERIMENTAL_NOWARN_BITPACKS is defined.
300 */
301#if defined(CHERIOT_EXPERIMENTAL) && \
302 !defined(CHERIOT_EXPERIMENTAL_NOWARN_BITPACKS)
303# define BITPACK_DECL_ANNOTATION \
304 CHERIOT_EXPERIMENTAL( \
305 "Bitpacks are an experimental CHERIoT RTOS feature")
306#else
307# define BITPACK_DECL_ANNOTATION
308#endif
309
310/**
311 * The `Bitpack` structure itself, templated on the underlying Storage type.
312 *
313 * This should be the sole (non-empty) superclass of a type representing a
314 * bitfield-esque composite structure.
315 */
316template<typename StorageParam>
317 requires std::is_unsigned_v<StorageParam>
318class BITPACK_DECL_ANNOTATION Bitpack
319{
320 public:
321 /// Expose the underlying storage type
322 using Storage = StorageParam;
323
324 private:
325 Storage value;
326
327 public:
328 /// Construct a `Bitpack` value with an underlying Storage of all zero bits
329 constexpr Bitpack() : value(0) {}
330
331 /**
332 * Construct a `Bitpack` value from a value of its underlying Storage type.
333 *
334 * This is marked `explicit` to make things like "device->register = 0x7;"
335 * slightly harder to spell, with the intent of encouraging use of named
336 * constants.
337 */
338 constexpr explicit Bitpack(Storage v) : value(v) {}
339
340 /**
341 * Assign a whole `Bitpack` at once given a non-volatile `Bitpack` of the
342 * same (or convertible, including derived) type.
343 *
344 * To assign from a `volatile` `Bitpack`, perform an explicit `.read()`
345 * first.
346 *
347 * "Deducing this" gives us CRTP-esque behavior without, well, the CRTP, so
348 * this will not accept an unrelated derived `Bitpack` class. (But will
349 * accept types that can be converted, so assigning to a `Bitpack`, as
350 * opposed to a derived class, will accept any derived class, for example.
351 * Probably best avoid that.)
352 */
353 template<typename Self>
354 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
355 constexpr Self &&operator=(this Self &&self,
356 const std::remove_cvref_t<Self> &b)
357 {
358 self.value = b.value;
359 return self;
360 }
361
362 /**
363 * Compare `Bitpack`-s for equality (reflecting that of `Storage`).
364 *
365 * Neither side may be volatile; use explicit `.read()`-s first.
366 */
367 template<typename Self>
368 requires(!std::is_volatile_v<Self>)
369 constexpr bool operator==(this Self &&self,
370 const std::remove_cvref_t<Self> &b)
371 {
372 return self.value == b.value;
373 }
374
375 /**
376 * Spaceship operator on `Bitpack`-s (reflecing that of `Storage`).
377 *
378 * Neither side may be volatile; use explicit `.read()`-s first.
379 */
380 template<typename Self>
381 requires(!std::is_volatile_v<Self>)
382 constexpr auto operator<=>(this Self &&self,
383 const std::remove_cvref_t<Self> &b)
384 {
385 return self.value <=> b.value;
386 }
387
388 /**
389 * Extract the underlying `Storage` value.
390 *
391 * The `Bitpack` must not be `volatile`. Use `.read()` first.
392 */
393 constexpr explicit operator Storage() const
394 {
395 return this->value;
396 }
397
398 /**
399 * A shorter way of spelling `static_cast<Storage>(...)`.
400 *
401 * The `Bitpack` must not be `volatile`. Use `.read()` first.
402 */
403 [[nodiscard]] constexpr Storage raw() const
404 {
405 return static_cast<Storage>(*this);
406 }
407
408 /**
409 * Return a snapshot of the underlying `Storage`.
410 *
411 * Notably, this can be used to get a `const` `Bitpack` from a `volatile`
412 * `Bitpack` (`const` or not).
413 */
414 template<typename Self>
415 const auto read(this Self &&self)
416 {
417 return std::remove_cvref_t<Self>{self.value};
418 }
419
420 /// Convenience wrapper for the types of numeric fields.
421 template<typename T>
422 requires std::is_unsigned_v<T>
423 struct Numeric
424 {
425 /// Export the underlying numeric type
426 using NumericType = T;
427
428 /*
429 * The field type must be smaller than the `Bitpack`'s underlying
430 * `Storage`.
431 */
432 static_assert(sizeof(T) <= sizeof(Storage));
433
434 T value;
435
436 constexpr Numeric(T v) : value(v) {}
437
438 constexpr operator T() const
439 {
440 return this->value;
441 }
442
443 [[nodiscard]] constexpr T raw() const
444 {
445 return static_cast<T>(this->value);
446 }
447 };
448
449 /**
450 * Information about a field within a `Bitpack`.
451 *
452 * Fields are contiguous spans of bits and so have a lowest and highest bit
453 * position within the underlying Storage word.
454 *
455 * Fields may be marked as constant to dissuade software from attempting to
456 * change their value. Do note, though, that any store of an underlying
457 * Storage word will also, necessarily, include the bits for constant
458 * Fields. It is likely generally more advisable to not mix constant and
459 * non-constant fields within the same Storage word or `Bitpack`.
460 */
462 {
463 /// Minimum 0-indexed bit position occupied by this field
464 size_t minIndex;
465 /// Maximum 0-indexed bit position occupied by this field
466 size_t maxIndex;
467 /**
468 * Should this field be proxied as constant (and so mutation be slightly
469 * less ergonomic)?
470 */
471 bool isConst = false;
472
473 bool operator==(const FieldInfo &) const = default;
474
475 /// Return the field width, in bits.
476 [[nodiscard]] constexpr size_t width() const
477 {
478 return maxIndex - minIndex + 1;
479 }
480
481 /**
482 * Return the maximum value this field can hold, if it is interpreted as
483 * holding an unsigned number.
484 */
485 [[nodiscard]] constexpr Storage maximum_as_unsigned() const
486 {
487 return (static_cast<Storage>(1U) << width()) - 1;
488 }
489 };
490
491 /**
492 * A particular Field within a `Bitpack`.
493 *
494 * Fields are templated on the type as seen by external code and the
495 * FieldInfo data giving the field's location and other properties.
496 */
497 template<typename TypeParam, FieldInfo InfoParam>
498 struct Field
499 {
500 /// Expose the type parameter
501 using Type = TypeParam;
502 /// Expose the FieldInfo parameter
503 static constexpr FieldInfo Info = InfoParam;
504
505 // The field's span of bits must start before it ends
506 static_assert(Info.maxIndex >= Info.minIndex,
507 "Field span ends before it begins");
508
509 static_assert(!std::is_base_of_v<Numeric<bool>, Type> ||
510 (Info.maxIndex == Info.minIndex),
511 "Numeric<bool> fields should be exactly one bit wide");
512
513 static_assert(
514 !std::is_enum_v<Type> ||
515 !requires {
516 { std::underlying_type_t<Type>() } -> std::same_as<bool>;
517 } || (Info.maxIndex == Info.minIndex),
518 "Enum fields with underlying type bool should be exactly "
519 "one bit wide");
520
521 static_assert((Info.maxIndex - Info.minIndex + 1) <=
522 CHAR_BIT * sizeof(Type),
523 "Field type is narrower than specified field bit width");
524
525 static_assert((Info.maxIndex - Info.minIndex + 1) <
526 CHAR_BIT * sizeof(Storage),
527 "Field width is not smaller than Bitpack's Storage type");
528
529 /// A span of set bits, starting at index 0, and of the field's width.
530 static constexpr Storage ValueMask =
531 (1U << (Info.maxIndex - Info.minIndex + 1)) - 1;
532
533 /// The mask of bits occupied by this value (occupied bits are set).
534 static constexpr Storage FieldMask = ValueMask << Info.minIndex;
535
536 /// Extract a Field from a raw Storage value
537 constexpr static Type raw_view(Storage storage)
538 {
539 return static_cast<Type>((storage >> Info.minIndex) & ValueMask);
540 }
541
542 /// Compute the underlying Storage transform for a Field update
543 constexpr static Storage raw_with(Storage lhs, Storage rhs)
544 {
545 return (lhs & ~FieldMask) | ((rhs & ValueMask) << Info.minIndex);
546 }
547
548 /// Update for fields whose type can be static_cast to Storage
549 constexpr static Storage raw_with(Storage lhs, Type rhs)
550 requires requires { static_cast<Storage>(std::declval<Type>()); }
551 {
552 return raw_with(lhs, static_cast<Storage>(rhs));
553 }
554
555 /// Update for fields that are themselves `Bitpacks` at smaller types
556 template<typename OtherStorage>
557 requires requires { sizeof(OtherStorage) < sizeof(Storage); }
558 constexpr static Storage raw_with(Storage lhs,
560 {
561 return raw_with(lhs, static_cast<OtherStorage>(rhs));
562 }
563
564 /**
565 * A proxy for this Field within the `Bitpack`'s `Storage`.
566 *
567 * This provides getters, setters, and mutators phrased in terms of the
568 * `Field`'s exposed `Type` rather than the raw bits within the
569 * `Bitpack`'s underlying `Storage` word.
570 *
571 * Proxies are templated on their containing derived type (so that they
572 * can act "in situ" in the class hierarchy, consuming and returning the
573 * same type, which must have `Bitpack` as its base type) and the type
574 * of the reference they hold to their containing `Bitpack`'s underlying
575 * `Storage` word (so that they can properly propagate `volatile` and
576 * `const` qualifications from the `Bitpack` value and the `Field`'s
577 * properties to perceived type).
578 */
579 template<typename DerivedBitpack, typename RefTypeParam>
580 requires std::is_base_of_v<Bitpack, DerivedBitpack> &&
581 std::is_lvalue_reference_v<RefTypeParam> &&
582 std::is_same_v<Storage, std::remove_cvref_t<RefTypeParam>>
583 class Proxy
584 {
585 /// A qualified reference to the containing `Bitpack`'s `Storage`.
586 RefTypeParam ref;
587
588 public:
589 /// Name the Field of which we are a proxy
590 using Field = Field;
591
592 /// Expose the qualified reference type we hold to the Storage
593 using RefType = RefTypeParam;
594
595 /// Expose the type of the bitpack into which we are a Field::Proxy
596 using ContainingBitpack = DerivedBitpack;
597
598 /**
599 * Construct a proxy given a reference to the storage word.
600 *
601 * May take any reference type implicitly convertible to RefType,
602 * not just RefType itself.
603 */
604 template<typename R>
605 constexpr Proxy(R &r) : ref(r.value)
606 {
607 }
608
609 /**
610 * Compute and store an updated `Bitpack` value with a new value for
611 * this field (of the field type itself).
612 *
613 * For `volatile` `Bitpack`s, this will perform a load and store.
614 */
615 template<typename Self>
616 requires(
617 !std::is_const_v<std::remove_reference_t<RefTypeParam>>)
618 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
619 constexpr Self &&operator=(this Self &&self, Field::Type rhs)
620 {
621 self.ref = raw_with(self.ref, rhs);
622 return self;
623 }
624
625 /**
626 * Compute and store an updated `Bitpack` value with a new value for
627 * this field, when this field's type is a Numeric wrapper, and the
628 * RHS is the underlying type inside the Numeric wrapper.
629 *
630 * For `volatile` `Bitpack`s, this will perform a load and store.
631 */
632 template<typename Self, typename RHS>
633 requires(
634 !std::is_const_v<std::remove_reference_t<RefTypeParam>> &&
635 std::is_same_v<Field::Type, Numeric<RHS>>)
636 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
637 constexpr Self &&operator=(this Self &&self, RHS rhs)
638 {
639 self.ref = raw_with(self.ref, Field::Type{rhs});
640 return self;
641 }
642
643 /**
644 * Compute and store an updated `Bitpack` value with a new value for
645 * this field, when this field's type is an enum class and the
646 * RHS is the underlying type of that enum.
647 *
648 * For `volatile` `Bitpack`s, this will perform a load and store.
649 */
650 template<typename Self, typename RHS>
651 requires(
652 !std::is_const_v<std::remove_reference_t<RefTypeParam>> &&
653 std::is_scoped_enum_v<Field::Type> &&
654 std::is_same_v<std::underlying_type_t<Field::Type>, RHS>)
655 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
656 constexpr Self &&operator=(this Self &&self, RHS rhs)
657 {
658 self.ref = raw_with(self.ref, Field::Type{rhs});
659 return self;
660 }
661
662 /**
663 * Explicit conversion of a Field::Proxy to the field value.
664 *
665 * The Proxy must not have a `volatile` reference to Storage.
666 * Use `.read()` on the containing `Bitpack`, first.
667 */
668 template<typename Self>
669 requires(
670 !std::is_volatile_v<std::remove_reference_t<RefTypeParam>>)
671 constexpr operator Field::Type(this Self &&self)
672 {
673 return raw_view(self.ref);
674 }
675
676 /// A shorter way of spelling static_cast<Field::Type>()...
677 [[nodiscard]] constexpr Field::Type raw() const
678 {
679 return static_cast<Field::Type>(*this);
680 }
681
682 /// Get the value of this enum-typed field as its underlying type.
683 [[nodiscard]] constexpr auto rawer() const
684 requires(std::is_enum_v<typename Field::Type>)
685 {
686 return std::to_underlying(this->raw());
687 }
688
689 /**
690 * Get the value of this Numeric-typed field as its underlying type.
691 */
692 [[nodiscard]] constexpr auto rawer() const
693 requires(std::is_base_of_v<
694 Numeric<typename std::remove_cvref_t<
695 typename Field::Type>::NumericType>,
696 typename std::remove_cvref_t<typename Field::Type>>)
697 {
698 return this->raw().raw();
699 }
700
701 /**
702 * Construct a new `Bitpack` value with an updated value of this
703 * field.
704 *
705 * This is emphatically not `volatile`-qualified, and has no
706 * `volatile`-qualified overload. `.read()` the containing
707 * `Bitpack`, first.
708 */
709 [[nodiscard]] constexpr DerivedBitpack with(Field::Type rhs) const
710 {
711 return DerivedBitpack{raw_with(this->ref, rhs)};
712 }
713
714 /**
715 * Construct a new `Bitpack` value with an updated value of this
716 * field as a function of its current value.
717 *
718 * This is emphatically not `volatile`-qualified, and has no
719 * `volatile`-qualified overload. `.read()` the containing
720 * `Bitpack`, first.
721 */
722 constexpr DerivedBitpack with(auto &&f) const
723 requires std::
724 is_invocable_r_v<Field::Type, decltype(f), Field::Type>
725 {
726 /*
727 * Perform one read and use it twice to avoid double-tapping
728 * volatile storage!
729 */
730 Storage storage = this->ref;
731 return DerivedBitpack{raw_with(storage, f(raw_view(storage)))};
732 }
733
734 /**
735 * Compute and store an updated `Bitpack` value with a new value for
736 * this field as a function of its current value.
737 *
738 * For `volatile` `Bitpack`s, this will perform a load and store.
739 */
740 template<typename Self>
741 constexpr void alter(this Self &&self, auto &&f)
742 requires(
743 std::
744 is_invocable_r_v<Field::Type, decltype(f), Field::Type> &&
745 !std::is_const_v<std::remove_reference_t<RefTypeParam>>)
746 {
747 Storage storage = self.ref;
748 self.ref = raw_with(storage, f(raw_view(storage)));
749 }
750
751 /**
752 * Convenience function for unconditionally assigning a field when
753 * it helps to have a zero value of that field's type.
754 *
755 * Use `.alter()` if the current value of the field is required.
756 *
757 * Because this requires a read of the whole `Bitpack` to update,
758 * we refuse to operate on `volatile` values, and because it's an
759 * assignment, we don't operate on `const` values either.
760 */
761 constexpr void assign_from(auto &&f)
762 requires(
763 std::is_invocable_r_v<Field::Type, decltype(f), Field::Type>)
764 {
765 this->ref = raw_with(this->ref, f(raw_view(0)));
766 }
767
768 constexpr auto operator<=>(Proxy rhs) const
769 requires(requires(Field::Type v) {
770 { v <=> v };
771 })
772 {
773 return raw() <=> rhs.raw();
774 }
775
776 constexpr auto operator<=>(Field::Type rhs) const
777 requires(requires(Field::Type v) {
778 { v <=> v };
779 })
780 {
781 return raw() <=> rhs;
782 }
783
784 constexpr bool operator==(Proxy rhs) const
785 requires(requires(Field::Type v) {
786 { v == v } -> std::same_as<bool>;
787 })
788 {
789 return raw() == rhs.raw();
790 }
791
792 constexpr bool operator==(Field::Type rhs) const
793 requires(requires(Field::Type v) {
794 { v == v } -> std::same_as<bool>;
795 })
796 {
797 return raw() == rhs;
798 }
799 };
800
801 template<bool C, typename T>
802 requires std::is_lvalue_reference_v<T>
803 using ConditionalConstRef = std::
804 conditional_t<C, std::add_const_t<std::remove_reference_t<T>> &, T>;
805
806 /*
807 * Guide template deduction to conclude that a Field has a RefType
808 * that is as CV-qualified as the `Bitpack` of which it is a part, with
809 * the further possibility of const-qualification for fields annotated
810 * as constant.
811 *
812 * Yes, the `decltype(())` is deliberate: we want the lvalue expression
813 * rather than the prvalue expression.
814 */
815 template<typename BitpackType>
816 Proxy(BitpackType &r)
817 -> Proxy<std::remove_cvref_t<BitpackType>,
818 ConditionalConstRef<Info.isConst, decltype((r.value))>>;
819 };
820
821 protected:
822 /**
823 * Build a Field::Proxy of an explicitly given type and info.
824 *
825 * This is protected so that it is available to subclasses but not more
826 * generally.
827 */
828 template<typename FieldType, FieldInfo Info, typename Self>
829 constexpr auto member(this Self &&self)
830 {
831 return typename Field<FieldType, Info>::Proxy(self);
832 }
833
834 public:
835 /**
836 * Build a Field::Proxy proxy by asking the derived Self class for a
837 * FieldInfo structure computed from template expansion of
838 * Self::field_info_for_type<FieldType>().
839 */
840 template<typename FieldType, typename Self>
841 requires std::is_convertible_v<
842 decltype(std::remove_cvref_t<Self>::template field_info_for_type<
843 FieldType>()),
844 const FieldInfo>
845 constexpr auto member(this Self &&self)
846 {
847 /*
848 * Find the information for the field type; "deducing this" gives us
849 * access to the derived class's type without need for the CRTP.
850 */
851 static constexpr FieldInfo Info =
852 std::remove_cvref_t<Self>::template field_info_for_type<FieldType>();
853
854 return self.template member<FieldType, Info>();
855 }
856
857 /**
858 * Fetch the value of a field in this `Bitpack` based on the field type.
859 *
860 * For `volatile` `Bitpack`s, this will perform a load.
861 */
862 template<typename FieldType, typename Self>
863 constexpr FieldType get(this Self &&self)
864 {
865 return self.template member<FieldType>();
866 }
867
868 /**
869 * Compute a new `Bitpack` value with an updated field of a given type.
870 *
871 * For `volatile` `Bitpack`s, this will perform a load.
872 */
873 template<typename FieldType, typename Self>
874 constexpr std::remove_cvref_t<Self> with(this Self &&self, FieldType v)
875 {
876 return self.template member<FieldType>().with(v);
877 }
878
879 /**
880 * Compute a new `Bitpack` value with an updated field of a given type.
881 *
882 * For `volatile` `Bitpack`s, this will perform a load and store.
883 */
884 template<typename FieldType, typename Self>
885 constexpr void set(this Self &&self, FieldType v)
886 {
887 self.template member<FieldType>() = v;
888 }
889
890 /**
891 * Convenience function for unconditionally changing several sub-fields at
892 * once. Intended particularly for use with `volatile` `Bitpack`-s, to help
893 * ensure only one read and one write takes place, as the callback operates
894 * on a non-`volatile` `Bitpack`.
895 */
896 template<typename Self>
897 constexpr void alter(this Self &&self, auto &&f)
898 requires std::is_invocable_r_v<std::remove_cvref_t<Self>,
899 decltype(f),
900 std::remove_cvref_t<Self>>
901 {
902 std::remove_cvref_t<Self> value{self.value};
903 self = f(value);
904 }
905
906 /**
907 * Convenience function for unconditionally assigning an entire `Bitpack`
908 * from a computed value. The function receives a zero-valued instance of
909 * the `Bitpack` type.
910 */
911 template<typename Self>
912 constexpr void assign_from(this Self &&self, auto &&f)
913 requires std::is_invocable_r_v<std::remove_cvref_t<Self>,
914 decltype(f),
915 std::remove_cvref_t<Self>>
916 {
917 self = f(std::remove_cvref_t<Self>(0));
918 }
919};
920
921/**
922 * It is occasionally useful to derive one bitpack from another. Using
923 * BitpackDerived<B> as the sole (non-empty) base class of an aggregate type
924 * reduces the syntactic clutter of deriving from the bitpack B.
925 *
926 * See `BITPACK_DERIVED_PREFIX` and `BITPACK_DERIVED_FIELD_INFO_FOR_TYPE`.
927 */
928template<typename B>
929 requires std::derived_from<B, Bitpack<typename B::Storage>>
931{
932 using B::B;
933 using B::operator=;
934
935 protected:
936 using ParentBitpack = B;
937};
938
939/**
940 * \defgroup bitpacks_macros Convenience macros
941 * @{
942 */
943
944/**
945 * \defgroup bitpacks_macros_defn Field definition macros
946 * @{
947 */
948
949/**
950 * Capture the incantations often at the top of a `Bitpack` structure,
951 * especially one with type-directed field proxies.
952 */
953#define BITPACK_USUAL_PREFIX \
954 using Bitpack::Bitpack; \
955 using Bitpack::operator=; \
956 template<typename FieldType> \
957 static constexpr FieldInfo field_info_for_type() = delete;
958
959/**
960 * Define a named accessor for a field.
961 *
962 * Takes the name of the accessor, the type to use for the proxy, and the fields
963 * of the FieldInfo (as varargs). If the latter is omitted, this defines an
964 * alias for a field whose type is sufficient to resolve the FieldInfo through
965 * field_info_for_type.
966 */
967#define BITPACK_MEMBER_ADD(name, Type, ...) \
968 template<typename Self> \
969 constexpr auto name(this Self &&self) \
970 { \
971 return self.template member<Type __VA_OPT__(, {__VA_ARGS__})>(); \
972 }
973
974/**
975 * Encapsulate the gyrations required to define an `enum class`-typed field and
976 * its associated `field_info_for_type` within a `Bitpack`. Takes the name of
977 * the `enum` class to define, its underlying type, and then the fields of a
978 * `FieldInfo` as varargs; the enumerators should follow the macro invocation,
979 * surrounded in curly braces and terminated with a semicolon.
980 */
981#define BITPACK_MEMBER_ADD_ENUM(Type, Base, ...) \
982 enum class Type : Base; \
983 template<> \
984 constexpr FieldInfo field_info_for_type<Type>() \
985 { \
986 constexpr auto info = FieldInfo{__VA_ARGS__}; \
987 static_assert(requires { Field<Type, info>(); }); \
988 return info; \
989 } \
990 enum class Type : Base
991
992/**
993 * Define a new scoped enumeration type whose underlying type is bool, with the
994 * given false and true value names, at the given bit position.
995 *
996 * FieldInfo fields other than minIndex and maxIndex may be provided as
997 * additional arguments.
998 *
999 * Contrast `BITPACK_MEMBER_ADD_BOOL`, which does not introduce the custom
1000 * enumerator values like our `FalseVal` and `TrueVal`.
1001 *
1002 * See `BITPACK_MEMBER_ADD_ENUM`, which this uses, for more details.
1003 */
1004#define BITPACK_MEMBER_ADD_ENUM_BOOL(Type, FalseVal, TrueVal, BitIndex, ...) \
1005 BITPACK_MEMBER_ADD_ENUM( \
1006 Type, bool, BitIndex, BitIndex __VA_OPT__(, __VA_ARGS__)) \
1007 { \
1008 FalseVal = false, TrueVal = true, \
1009 }
1010
1011/**
1012 * Define a new boolean scoped enumeration with values named "Cleared" (0)
1013 * and "Asserted" (1) at the given bit index.
1014 */
1015#define BITPACK_MEMBER_ADD_ENUM_BOOL_CLEARED_ASSERTED(Type, BitIndex, ...) \
1016 BITPACK_MEMBER_ADD_ENUM_BOOL( \
1017 Type, Cleared, Asserted, BitIndex __VA_OPT__(, __VA_ARGS__))
1018
1019/**
1020 * Define a new boolean scoped enumeration with values named "Disabled" (0)
1021 * and "Enabled" (1) at the given bit index.
1022 */
1023#define BITPACK_MEMBER_ADD_ENUM_BOOL_DISABLED_ENABLED(Type, BitIndex, ...) \
1024 BITPACK_MEMBER_ADD_ENUM_BOOL( \
1025 Type, Disabled, Enabled, BitIndex __VA_OPT__(, __VA_ARGS__))
1026
1027/**
1028 * Encapsulate the gyrations required to define a `Numeric`-typed field and its
1029 * associated `field_info_for_type` within a `Bitpack`. Takes the name of the
1030 * struct to define, the `Numeric`'s underlying type, and then the fields of a
1031 * `FieldInfo` as varargs.
1032 */
1033#define BITPACK_MEMBER_ADD_NUMERIC(Type, Base, ...) \
1034 struct Type : Numeric<Base> \
1035 { \
1036 using Numeric::Numeric; \
1037 }; \
1038 template<> \
1039 constexpr FieldInfo field_info_for_type<Type>() \
1040 { \
1041 constexpr auto info = FieldInfo{__VA_ARGS__}; \
1042 static_assert(requires { Field<Type, info>(); }); \
1043 return info; \
1044 }
1045
1046/**
1047 * Define a new type wrapper around bool for a 1-bit field at a given BitIndex.
1048 *
1049 * FieldInfo fields other than minIndex and maxIndex may be provided as
1050 * additional arguments.
1051 *
1052 * By contrast to `BITPACK_MEMBER_ADD_ENUM_BOOL`, the values introduced here are
1053 * `Type{false}` and `Type{true}` rather than custom enumerators.
1054 *
1055 * See `BITPACK_MEMBER_ADD_NUMERIC`, which this uses, for more details.
1056 */
1057#define BITPACK_MEMBER_ADD_BOOL(Type, BitIndex, ...) \
1058 BITPACK_MEMBER_ADD_NUMERIC( \
1059 Type, bool, BitIndex, BitIndex __VA_OPT__(, __VA_ARGS__))
1060
1061/// @}
1062
1063/**
1064 * \defgroup bitpacks_macros_member Type-qualifying member accessor macros
1065 * @{
1066 */
1067
1068/**
1069 * A convenience macro that presumes the type `T` is defined within the
1070 * `Bitpack` `b` and finds such a field's proxy.
1071 *
1072 * There is no `BITPACK_MEMBER_TYPE(b, T)` analogue, because that's just
1073 * `b.member<T>()`.
1074 */
1075#define BITPACK_MEMBER_DECLTYPE(b, T) \
1076 (b).template member<std::remove_reference_t<decltype(b)>::T>()
1077
1078/**
1079 * Like BITPACK_MEMBER_DECLTYPE, but with additional an "typename" keyword so we
1080 * can use a dependently-typed bitpack `b` (say, the type of `b` is `auto` or,
1081 * more generally, involves a template argument).
1082 */
1083#define BITPACK_MEMBER_DEPENDENT(b, T) \
1084 (b).template member<typename std::remove_reference_t<decltype(b)>::T>()
1085
1086/// @}
1087
1088/**
1089 * \defgroup bitpacks_macros_derived Derived bitpack definition macros
1090 * @{
1091 */
1092
1093/**
1094 * Capture the incantations often at the top of a `BitpackDerived` structure.
1095 * especially one with type-directed field proxys.
1096 */
1097#define BITPACK_DERIVED_PREFIX \
1098 using BitpackDerived<ParentBitpack>::BitpackDerived; \
1099 using BitpackDerived<ParentBitpack>::operator=; \
1100 /* Inherit field info for most types from parent bitpack */ \
1101 template<typename FieldType> \
1102 static constexpr FieldInfo field_info_for_type() \
1103 { \
1104 return ParentBitpack::field_info_for_type<FieldType>(); \
1105 }
1106
1107/// Modify the field information for a type in a derived bitpack.
1108#define BITPACK_DERIVED_FIELD_INFO_FOR_TYPE(Type, lambda) \
1109 template<> \
1110 constexpr FieldInfo field_info_for_type<Type>() \
1111 { \
1112 static_assert( \
1113 std::is_invocable_r_v<void, decltype(lambda), FieldInfo &>); \
1114 auto fi = ParentBitpack::field_info_for_type<Type>(); \
1115 lambda(fi); \
1116 return fi; \
1117 }
1118
1119/**
1120 * Modify the constness field information for a type in a derived bitpack.
1121 * This is a thin wrapper around BITPACK_DERIVED_FIELD_INFO_FOR_TYPE.
1122 */
1123#define BITPACK_DERIVED_FIELD_CONST_FOR_TYPE(Type, c) \
1124 BITPACK_DERIVED_FIELD_INFO_FOR_TYPE(Type, [](auto &fi) { fi.isConst = c; })
1125
1126/// @}
1127
1128/**
1129 * \defgroup bitpacks_macros_proxyop_qualify Type-qualifying proxy operator
1130 * macros
1131 * @{
1132 */
1133
1134/**
1135 * Operate between a `Field::Proxy` `proxy` and a given `value`, which will be
1136 * qualifed with `proxy`'s `Field::Type` name.
1137 *
1138 * This will not work on `volatile` bitpacks; use `.read` first.
1139 */
1140#define BITPACK_OPERATE_QUALIFY(proxy, operator, value) \
1141 ({ \
1142 using F = decltype(proxy)::Field::Type; \
1143 (proxy) operator(F::value); \
1144 })
1145
1146/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_QUALIFY
1147#define BITPACK_OPERATE_QUALIFY_DECLTYPE(b, T, operator, v) \
1148 BITPACK_OPERATE_QUALIFY(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1149
1150/// A fusion of BITPACK_MEMBER_DEPENDENT and BITPACK_OPERATE_QUALIFY
1151#define BITPACK_OPERATE_QUALIFY_DEPENDENT(b, T, operator, v) \
1152 BITPACK_OPERATE_QUALIFY(BITPACK_MEMBER_DEPENDENT(b, T), operator, v)
1153
1154/// A fusion of .member<>() and BITPACK_OPERATE_QUALIFY
1155#define BITPACK_OPERATE_QUALIFY_TYPE(b, T, operator, v) \
1156 BITPACK_OPERATE_QUALIFY((b).template member<T>(), operator, v)
1157
1158/// A specialization of BITPACK_OPERATE_QUALIFY using `.with` as the operator
1159#define BITPACK_WITH_QUALIFY(proxy, value) \
1160 BITPACK_OPERATE_QUALIFY(proxy, .with, value)
1161
1162/**
1163 * A specialization of BITPACK_OPERATE_QUALIFY_DECLTYPE using `.with` as the
1164 * operator
1165 */
1166#define BITPACK_WITH_QUALIFY_DECLTYPE(b, T, v) \
1167 BITPACK_OPERATE_QUALIFY_DECLTYPE(b, T, .with, v)
1168
1169/**
1170 * A specialization of BITPACK_OPERATE_QUALIFY_DEPENDENT using `.with` as the
1171 * operator
1172 */
1173#define BITPACK_WITH_QUALIFY_DEPENDENT(b, T, v) \
1174 BITPACK_OPERATE_QUALIFY_DEPENDENT(b, T, .with, v)
1175
1176/**
1177 * A specialization of BITPACK_OPERATE_QUALIFY_TYPE using `.with` as the
1178 * operator
1179 */
1180#define BITPACK_WITH_QUALIFY_TYPE(b, T, v) \
1181 BITPACK_OPERATE_QUALIFY_TYPE(b, T, .with, v)
1182
1183/// @}
1184
1185/**
1186 * \defgroup bitpacks_macros_proxyop_construct Type-constructing proxy operator
1187 * macros
1188 *
1189 * @{
1190 */
1191
1192/**
1193 * Operate between a `Field::Proxy` `proxy` and a given `value`, which will be
1194 * wrapped with `proxy`'s `Field::Type`'s constructor.
1195 *
1196 * This will not work on `volatile` bitpacks; use `.read` first.
1197 */
1198#define BITPACK_OPERATE_WRAP(proxy, operator, value) \
1199 ({ \
1200 using F = decltype(proxy)::Field::Type; \
1201 (proxy) operator(F{value}); \
1202 })
1203
1204/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_WRAP
1205#define BITPACK_OPERATE_WRAP_DECLTYPE(b, T, operator, v) \
1206 BITPACK_OPERATE_WRAP(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1207
1208/// A fusion of BITPACK_MEMBER_DEPENDENT and BITPACK_OPERATE_WRAP
1209#define BITPACK_OPERATE_WRAP_DEPENDENT(b, T, operator, v) \
1210 BITPACK_OPERATE_WRAP(BITPACK_MEMBER_DEPENDENT(b, T), operator, v)
1211
1212/// A fusion of .member<>() and BITPACK_OPERATE_WRAP
1213#define BITPACK_OPERATE_WRAP_TYPE(b, T, operator, v) \
1214 BITPACK_OPERATE_WRAP((b).template member<T>(), operator, v)
1215
1216/// A specialization of BITPACK_OPERATE_WRAP using `.with` as the operator
1217#define BITPACK_WRAP_WITH(proxy, value) \
1218 BITPACK_OPERATE_WRAP(proxy, .with, value)
1219
1220/**
1221 * A specialization of BITPACK_OPERATE_WRAP_DECLTYPE using `.with` as the
1222 * operator.
1223 */
1224#define BITPACK_WITH_WRAP_DECLTYPE(b, T, v) \
1225 BITPACK_OPERATE_WRAP_DECLTYPE(b, T, .with, v)
1226
1227/**
1228 * A specialization of BITPACK_OPERATE_WRAP_DEPENDENT using `.with` as the
1229 * operator
1230 */
1231#define BITPACK_WITH_WRAP_DEPENDENT(b, T, v) \
1232 BITPACK_OPERATE_WRAP_DEPENDENT(b, T, .with, v)
1233
1234/// A specialization of BITPACK_OPERATE_WRAP_TYPE using `.with` as the operator
1235#define BITPACK_WITH_WRAP_TYPE(b, T, v) \
1236 BITPACK_OPERATE_WRAP_TYPE(b, T, .with, v)
1237
1238/// @}
1239
1240/**
1241 * \defgroup bitpacks_macros_proxyop_directed Type-directed proxy operator
1242 * macros
1243 *
1244 * @{
1245 */
1246
1247/**
1248 * Given a bitpack `b` -- not a Proxy of a Field therein -- and a value, operate
1249 * between the bitpack's view of that value's type and the value.
1250 */
1251#define BITPACK_OPERATE_VALUE(b, operator, value) \
1252 ({ (b).template member<decltype(value)>() operator(value); })
1253
1254/**
1255 * Given a bitpack `b` -- not a Proxy of a Field therein -- qualify the given
1256 * value with the bitpack's type and then operate between the bitpack's Proxy of
1257 * that qualified value's type and said value.
1258 */
1259#define BITPACK_OPERATE_VALUE_DECLTYPE(b, operator, value) \
1260 BITPACK_OPERATE_VALUE( \
1261 b, operator, std::remove_reference_t<decltype(b)>::value)
1262
1263/**
1264 * BITPACK_OPERATE_VALUE with dependent qualification for the type of the
1265 * bitpack.
1266 */
1267#define BITPACK_OPERATE_VALUE_DEPENDENT(b, operator, value) \
1268 BITPACK_OPERATE_VALUE( \
1269 b, operator, typename std::remove_reference_t<decltype(b)>::value)
1270
1271/// A specialization of BITPACK_OPERATE_VALUE using `.with` as the operator
1272#define BITPACK_WITH_VALUE(b, value) BITPACK_OPERATE_VALUE(b, .with, value)
1273
1274/**
1275 * A specialization of BITPACK_OPERATE_VALUE_DECLTYPE using `.with` as the
1276 * operator
1277 */
1278#define BITPACK_WITH_VALUE_DECLTYPE(b, value) \
1279 BITPACK_OPERATE_VALUE_DECLTYPE(b, .with, value)
1280
1281/**
1282 * A specialization of BITPACK_OPERATE_VALUE_DEPENDENT using `.with` as the
1283 * operator.
1284 */
1285#define BITPACK_WITH_VALUE_DEPENDENT(b, value) \
1286 BITPACK_OPERATE_VALUE_DEPENDENT(b, .with, value)
1287
1288/// @}
1289
1290/**
1291 * \defgroup bitpacks_macros_proxyop_enum "using enum" proxy operator macros
1292 *
1293 * @{
1294 */
1295
1296/**
1297 * Convenience for scoped enum fields, bringing the enumerators of the field
1298 * type into scope while evaluating the value.
1299 *
1300 * Note that this uses `using enum` internally, and so cannot work with
1301 * `Proxy`-s whose Field::Type-s are dependent. Alas, this precludes the
1302 * existence of a `BITPACK_*_ENUM_DEPENDENT` family of helpers.
1303 *
1304 * Probably prefer BITPACK_OPERATE_QUALIFY if you do not need repeated use of
1305 * the enumeration's values within the passed `value`.
1306 */
1307#define BITPACK_OPERATE_ENUM(proxy, operator, value) \
1308 ({ \
1309 using E = decltype(proxy)::Field::Type; \
1310 static_assert(std::is_enum_v<E>); \
1311 using enum E; \
1312 (proxy) operator(value); \
1313 })
1314
1315/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_ENUM
1316#define BITPACK_OPERATE_ENUM_DECLTYPE(b, T, operator, v) \
1317 BITPACK_OPERATE_ENUM(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1318
1319/// A fusion of .member<>() and BITPACK_OPERATE_ENUM
1320#define BITPACK_OPERATE_ENUM_TYPE(b, T, operator, v) \
1321 BITPACK_OPERATE_ENUM((b).template member<T>(), operator, v)
1322
1323/// A specialization of BITPACK_OPERATE_ENUM using `.with` as the operator
1324#define BITPACK_WITH_ENUM(proxy, value) \
1325 BITPACK_OPERATE_ENUM(proxy, .with, value)
1326
1327/**
1328 * A specialization of BITPACK_OPERATE_ENUM_DECLTYPE using `.with` as the
1329 * operator.
1330 */
1331#define BITPACK_WITH_ENUM_DECLTYPE(b, T, v) \
1332 BITPACK_OPERATE_ENUM_DECLTYPE(b, T, .with, v)
1333
1334/// A specialization of BITPACK_OPERATE_ENUM_TYPE using `.with` as the operator
1335#define BITPACK_WITH_ENUM_TYPE(b, T, v) \
1336 BITPACK_OPERATE_ENUM_TYPE(b, T, .with, v)
1337
1338/// @}
1339
1340/**
1341 * \defgroup bitpacks_macros_field_metadata Field metadata helpers
1342 *
1343 * @{
1344 */
1345
1346/**
1347 * Given a Proxy to a Field in a given Bitpack, return that Field's mask as an
1348 * instance of that Bitpack. That is, return a Bitpack containing exactly all
1349 * of and only that Field's constituent bits asserted.
1350 */
1351#define BITPACK_FIELD_MASK(proxy) \
1352 (static_cast<decltype(proxy)::ContainingBitpack>( \
1353 decltype(proxy)::Field::FieldMask))
1354
1355/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_FIELD_MASK
1356#define BITPACK_FIELD_MASK_DECLTYPE(b, T) \
1357 BITPACK_FIELD_MASK(BITPACK_MEMBER_DECLTYPE(b, T))
1358
1359/// A fusion of BITPACK_MEMBER_DEPENDENT and BITPACK_FIELD_MASK
1360#define BITPACK_FIELD_MASK_DEPENDENT(b, T) \
1361 BITPACK_FIELD_MASK(BITPACK_MEMBER_DEPENDENT(b, T))
1362
1363/**
1364 * Given a Proxy to a Field in a given Bitpack, return the 0-based index of that
1365 * Field's constituent bits. This is mostly of interest to assembler.
1366 */
1367#define BITPACK_FIELD_SHIFT(proxy) (decltype(proxy)::Field::Info.minIndex)
1368
1369/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_FIELD_SHIFT
1370#define BITPACK_FIELD_SHIFT_DECLTYPE(b, T) \
1371 BITPACK_FIELD_SHIFT(BITPACK_MEMBER_DECLTYPE(b, T))
1372
1373/// A fusion of BITPACK_MEMBER_DEPENDENT and BITPACK_FIELD_SHIFT
1374#define BITPACK_FIELD_SHIFT_DEPENDENT(b, T) \
1375 BITPACK_FIELD_SHIFT(BITPACK_MEMBER_DEPENDENT(b, T))
1376
1377/// @}
1378
1379/**
1380 * \defgroup bitpacks_macros_vararg Multi-field utility macros
1381 *
1382 * @{
1383 */
1384
1385/**
1386 * \defgroup bitpacks_macros_vararg_internal Internal helpers
1387 *
1388 * @{
1389 */
1390
1391/// Map function for BITPACK_MAP_DECLTYPE
1392#define BITPACK_MAP_DECLTYPE_HELPER(x, b) \
1393 std::remove_reference_t<decltype(b)>::x
1394
1395/**
1396 * Given bitpack `b`, qualify each additional argument with `decltype(b)`. That
1397 * is, `BITPACK_MAP_DECLTYPE(b, X, Y)` evalutes to
1398 * `decltype(b)::X, decltype(b)::Y`.
1399 */
1400#define BITPACK_MAP_DECLTYPE(b, ...) \
1401 CHERIOT_MAP_LIST_UD(BITPACK_MAP_DECLTYPE_HELPER, b, __VA_ARGS__)
1402
1403/// Map function for BITPACK_MAP_DEPENDENT
1404#define BITPACK_MAP_DEPENDENT_HELPER(x, b) \
1405 typename std::remove_reference_t<decltype(b)>::x
1406
1407/**
1408 * Given bitpack `b`, qualify each additional argument with `typename
1409 * decltype(b)`. That is, `BITPACK_DEPENDENT(b, X, Y)` evalutes to
1410 * `typename decltype(b)::X, typename decltype(b)::Y`.
1411 */
1412#define BITPACK_MAP_DEPENDENT(b, ...) \
1413 CHERIOT_MAP_LIST_UD(BITPACK_MAP_DEPENDENT_HELPER, b, __VA_ARGS__)
1414
1415/// Map function for BITPACK_WITHS
1416#define BITPACK_MAP_WITHS_HELPER(x) .with(x)
1417
1418/// @}
1419
1420/**
1421 * Construct a chain of .with() whose arguments are all qualified with the type
1422 * of the bitpack.
1423 */
1424#define BITPACK_WITHS(b, ...) \
1425 (b) CHERIOT_MAP(BITPACK_MAP_WITHS_HELPER, __VA_ARGS__)
1426
1427/**
1428 * A version of BITPACK_WITHS where the arguments to `.with()` are qualified
1429 * with the type of the bitpack.
1430 */
1431#define BITPACK_WITHS_DECLTYPE(b, ...) \
1432 BITPACK_WITHS(b, BITPACK_MAP_DECLTYPE(b, __VA_ARGS__))
1433
1434/**
1435 * A version of BITPACK_WITHS where the arguments to `.with()` are dependently
1436 * qualified with the type of the bitpack.
1437 */
1438#define BITPACK_WITHS_DEPENDENT(b, ...) \
1439 BITPACK_WITHS(b, BITPACK_MAP_DEPENDENT(b, __VA_ARGS__))
1440
1441/**
1442 * @addtogroup macros_vararg_internal
1443 * @{
1444 */
1445
1446/// Helper for BITPACK_RELATE_MASKED, for computing individual field's masks
1447#define BITPACK_RELATE_MASKED_HELPER(x, b) \
1448 | ({ \
1449 using BT = decltype(b); \
1450 using FT = decltype(x); \
1451 BT::Field<FT, BT::field_info_for_type<FT>()>::FieldMask; \
1452 })
1453
1454/// @}
1455
1456/**
1457 * Given a list of field values (which must be fully qualified names), compute
1458 * the mask of these fields and the bitpack value holding these field values,
1459 * then mask `b` with the computed mask, and then use `operator` to relate the
1460 * result with the computed bitpack value. The last value for each field is
1461 * used.
1462 */
1463#define BITPACK_RELATE_MASKED(b, operator, ...) \
1464 ({ \
1465 constexpr decltype(b.raw()) __bitpack_mask = \
1466 (0)CHERIOT_MAP_UD(BITPACK_RELATE_MASKED_HELPER, b, __VA_ARGS__); \
1467 constexpr auto __bitpack_query = \
1468 BITPACK_WITHS((decltype(b))(0), __VA_ARGS__).raw(); \
1469 (b.raw() & __bitpack_mask) operator(__bitpack_query); \
1470 })
1471
1472/**
1473 * A version of BITPACK_RELATE_MASKED where the field values are qualified with
1474 * the type of the bitpack.
1475 */
1476#define BITPACK_RELATE_MASKED_DECLTYPE(b, operator, ...) \
1477 BITPACK_RELATE_MASKED(b, operator, BITPACK_MAP_DECLTYPE(b, __VA_ARGS__))
1478
1479/**
1480 * A version of BITPACK_RELATE_MASKED where the field values are dependently
1481 * qualified with the type of the bitpack.
1482 */
1483#define BITPACK_RELATE_MASKED_DEPENDENT(b, operator, ...) \
1484 BITPACK_RELATE_MASKED(b, operator, BITPACK_MAP_DEPENDENT(b, __VA_ARGS__))
1485
1486/// @}
1487/// @}
1488/// @}
A proxy for this Field within the Bitpack's Storage.
Definition bitpack.hh:584
constexpr auto rawer() const
Get the value of this Numeric-typed field as its underlying type.
Definition bitpack.hh:692
constexpr DerivedBitpack with(Field::Type rhs) const
Construct a new Bitpack value with an updated value of this field.
Definition bitpack.hh:709
constexpr Field::Type raw() const
A shorter way of spelling static_cast<Field::Type>()...
Definition bitpack.hh:677
constexpr Proxy(R &r)
Construct a proxy given a reference to the storage word.
Definition bitpack.hh:605
constexpr DerivedBitpack with(auto &&f) const
Construct a new Bitpack value with an updated value of this field as a function of its current value.
Definition bitpack.hh:722
constexpr void alter(this Self &&self, auto &&f)
Compute and store an updated Bitpack value with a new value for this field as a function of its curre...
Definition bitpack.hh:741
constexpr Self && operator=(this Self &&self, Field::Type rhs)
Compute and store an updated Bitpack value with a new value for this field (of the field type itself)...
Definition bitpack.hh:619
constexpr auto rawer() const
Get the value of this enum-typed field as its underlying type.
Definition bitpack.hh:683
constexpr operator Field::Type(this Self &&self)
Explicit conversion of a Field::Proxy to the field value.
Definition bitpack.hh:671
RefTypeParam RefType
Expose the qualified reference type we hold to the Storage.
Definition bitpack.hh:593
DerivedBitpack ContainingBitpack
Expose the type of the bitpack into which we are a Field::Proxy.
Definition bitpack.hh:596
constexpr void assign_from(auto &&f)
Convenience function for unconditionally assigning a field when it helps to have a zero value of that...
Definition bitpack.hh:761
Field Field
Name the Field of which we are a proxy.
Definition bitpack.hh:590
constexpr void set(this Self &&self, FieldType v)
Compute a new Bitpack value with an updated field of a given type.
Definition bitpack.hh:885
constexpr void assign_from(this Self &&self, auto &&f)
Convenience function for unconditionally assigning an entire Bitpack from a computed value.
Definition bitpack.hh:912
constexpr auto operator<=>(this Self &&self, const std::remove_cvref_t< Self > &b)
Spaceship operator on Bitpack-s (reflecing that of Storage).
Definition bitpack.hh:382
constexpr std::remove_cvref_t< Self > with(this Self &&self, FieldType v)
Compute a new Bitpack value with an updated field of a given type.
Definition bitpack.hh:874
constexpr Self && operator=(this Self &&self, const std::remove_cvref_t< Self > &b)
Assign a whole Bitpack at once given a non-volatile Bitpack of the same (or convertible,...
Definition bitpack.hh:355
constexpr Storage raw() const
A shorter way of spelling static_cast<Storage>(...).
Definition bitpack.hh:403
constexpr void alter(this Self &&self, auto &&f)
Convenience function for unconditionally changing several sub-fields at once.
Definition bitpack.hh:897
constexpr Bitpack()
Construct a Bitpack value with an underlying Storage of all zero bits.
Definition bitpack.hh:329
constexpr Bitpack(Storage v)
Construct a Bitpack value from a value of its underlying Storage type.
Definition bitpack.hh:338
constexpr auto member(this Self &&self)
Build a Field::Proxy proxy by asking the derived Self class for a FieldInfo structure computed from t...
Definition bitpack.hh:845
const auto read(this Self &&self)
Return a snapshot of the underlying Storage.
Definition bitpack.hh:415
constexpr FieldType get(this Self &&self)
Fetch the value of a field in this Bitpack based on the field type.
Definition bitpack.hh:863
constexpr auto member(this Self &&self)
Build a Field::Proxy of an explicitly given type and info.
Definition bitpack.hh:829
StorageParam Storage
Expose the underlying storage type.
Definition bitpack.hh:322
consteval std::strong_ordering operator<=>(const DebugLevel Level, const DebugLevel Threshold)
Comparison operator to determine whether a provided debug level is above the threshold at which it sh...
Definition debug.hh:740
It is occasionally useful to derive one bitpack from another.
Definition bitpack.hh:931
Information about a field within a Bitpack.
Definition bitpack.hh:462
constexpr Storage maximum_as_unsigned() const
Return the maximum value this field can hold, if it is interpreted as holding an unsigned number.
Definition bitpack.hh:485
size_t maxIndex
Maximum 0-indexed bit position occupied by this field.
Definition bitpack.hh:466
constexpr size_t width() const
Return the field width, in bits.
Definition bitpack.hh:476
bool isConst
Should this field be proxied as constant (and so mutation be slightly less ergonomic)?
Definition bitpack.hh:471
size_t minIndex
Minimum 0-indexed bit position occupied by this field.
Definition bitpack.hh:464
A particular Field within a Bitpack.
Definition bitpack.hh:499
static constexpr Storage raw_with(Storage lhs, Storage rhs)
Compute the underlying Storage transform for a Field update.
Definition bitpack.hh:543
static constexpr Storage FieldMask
The mask of bits occupied by this value (occupied bits are set).
Definition bitpack.hh:534
static constexpr Storage raw_with(Storage lhs, Bitpack< OtherStorage > rhs)
Update for fields that are themselves Bitpacks at smaller types.
Definition bitpack.hh:558
TypeParam Type
Expose the type parameter.
Definition bitpack.hh:501
static constexpr Storage ValueMask
A span of set bits, starting at index 0, and of the field's width.
Definition bitpack.hh:530
static constexpr Storage raw_with(Storage lhs, Type rhs)
Update for fields whose type can be static_cast to Storage.
Definition bitpack.hh:549
static constexpr Type raw_view(Storage storage)
Extract a Field from a raw Storage value.
Definition bitpack.hh:537
static constexpr FieldInfo Info
Expose the FieldInfo parameter.
Definition bitpack.hh:503
Convenience wrapper for the types of numeric fields.
Definition bitpack.hh:424
T NumericType
Export the underlying numeric type.
Definition bitpack.hh:426