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
208 * `BITPACK_MEMBER_` macros above). The first two families of macros atop
209 * proxies help with these cases.
210 *
211 * * The family of \ref bitpacks_macros_proxyop_qualify is built around the
212 * `BITPACK_OPERATE_QUALIFY(proxy, operator, value)` macro, which qualifies
213 * `value` with the field type of the proxy `proxy` and relates the proxy and
214 * qualified value with `operator`, which is often `=` for assignment or `==`
215 * for equality testing. For example,
216 * `BITPACK_OPERATE_QUALIFY(p, =, UpOnly)`, with `p` a `Proxy` of a
217 * `Drivers`-typed field (recall above), would assign the field to `UpOnly`
218 * without needing to use a fully qualified form like
219 * `MyBitpack::Drivers::UpOnly`.
220 *
221 * * The family of \ref bitpacks_macros_proxyop_construct is built around the
222 * `BITPACK_OPERATE_WRAP(proxy, operator, value)` macro. Here, the `value` is
223 * passed to a single-argument constructor of the proxy's field type. This
224 * works well for `Numeric` types: `BITPACK_OPERATE_WRAP(p, ==, 7)` will
225 * compare the value of the field being proxied by `p` with the value `7`
226 * converted to that field's type (by said type's constructor).
227 *
228 * Each of these families have further wrappers around their center macros:
229 *
230 * * `BITPACK_OPERATE_{QUALIFY,WRAP}_{DECLTYPE,DEPENDENT}(b, T, operator, v)`
231 * fuse `BITPACK_OPERATE_{QUALIFY,WRAP}` and
232 * `BITPACK_MEMBER_{DECLTYPE,DEPENDENT}`, building the `Proxy`
233 * of the `T`-typed field in the bitpack value `b`, rather than requiring it
234 * to be explicitly built first.
235 *
236 * * For the case where the type `T` does not need qualification when finding
237 * its associated field in `b`, use the
238 * `BITPACK_OPERATE_{QUALIFY,WRAP}_TYPE(b, T, operator, v)` macro (which can
239 * use `b.member<T>()`, rather than a `BITPACK_MEMBER_` macro, internally).
240 *
241 * * `BITPACK_WITH_{QUALIFY_WRAP}(proxy, value)` and
242 * `BITPACK_WITH_{QUALIFY,WRAP}_{DECLTYPE,DEPENDENT,TYPE}(b, T, v)` are
243 * specializations of their `BITPACK_OPERATE_...` siblings with `.with` as the
244 * operator, as this is a common enough occurrence.
245 *
246 * The family of \ref bitpacks_macros_proxyop_directed is somewhat dual: rather
247 * than using the field type to in some way influence the meaning of a `value`,
248 * this family uses the type of the value to find the appropriate field and a
249 * proxy thereof within a bitpack value. This can be convenient when a field
250 * value is already available. The centerpiece of this family is the
251 * `BITPACK_OPERATE_VALUE(b, operator, value)` macro, which relates with
252 * `operator` a proxy of the field in the bitpack value `b` whose type is that
253 * of `value` and `value` itself. The
254 * `BITPACK_OPERATE_{DECLTYPE,DEPENDENT}(b, operator, value)` wrappers qualify
255 * `value` with the type of the bitpack value `b`. As above, there are also
256 * `_WITH` specializations of the `_OPERATE` forms.
257 *
258 * There are other macros likely of more niche interest; see...
259 *
260 * * \ref bitpacks_macros_proxyop_enum and
261 * * \ref bitpacks_macros_vararg .
262 *
263 * \section bitpacks_formal A More Formal Perspective
264 *
265 * More formally, `Bitpack`-s are a kind of aggregate structure within a numeric
266 * word and offer lens-like, typed access to contiguous spans of bits therein,
267 * offering a view of bits as a product of typed fields, occuping a disjoint,
268 * contiguous span of bits within that word.
269 *
270 * The static information about a field is represented by a specialization of
271 * the `Bitpack::Field<Type, Info>` template. `Type` is the exposed C++ type of
272 * the field in question, and `Info` is a (necessarily constexpr) value of the
273 * `FieldInfo` type, which holds the bit indicies of the field's span within the
274 * `Storage` type. `FieldInfo` also has an `isConst` flag, used to specify that
275 * the field is not mutable from software even if the larger bitpack is
276 * non-constant.
277 *
278 * Our lenses, the centerpiece of the whole thing, are expressed as "proxy"
279 * objects, instances of specializations of the [Bitpack::Field<Type,
280 * Info>::Proxy<DerivedBitpack, RefType>](#Bitpack::Field::Proxy) class, which
281 * wrap references to the underlying `Storage`-typed word in a `Bitpack` class
282 * (or a derived class). The type of such references, `RefType`, inherits any
283 * `const` and/or `volatile` qualification of the user's handle to the bitpack,
284 * and are additionally `const` qualified if the associated `FieldInfo` has
285 * `isConst` asserted.
286 *
287 * @{
288 */
289
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
476 /**
477 * A particular Field within a `Bitpack`.
478 *
479 * Fields are templated on the type as seen by external code and the
480 * FieldInfo data giving the field's location and other properties.
481 */
482 template<typename TypeParam, FieldInfo InfoParam>
483 struct Field
484 {
485 /// Expose the type parameter
486 using Type = TypeParam;
487 /// Expose the FieldInfo parameter
488 static constexpr FieldInfo Info = InfoParam;
489
490 // The field's span of bits must start before it ends
491 static_assert(Info.maxIndex >= Info.minIndex,
492 "Field span ends before it begins");
493
494 static_assert(!std::is_base_of_v<Numeric<bool>, Type> ||
495 (Info.maxIndex == Info.minIndex),
496 "Numeric<bool> fields should be exactly one bit wide");
497
498 static_assert(
499 !std::is_enum_v<Type> ||
500 !requires {
501 { std::underlying_type_t<Type>() } -> std::same_as<bool>;
502 } || (Info.maxIndex == Info.minIndex),
503 "Enum fields with underlying type bool should be exactly "
504 "one bit wide");
505
506 static_assert((Info.maxIndex - Info.minIndex + 1) <=
507 CHAR_BIT * sizeof(Type),
508 "Field type is narrower than specified field bit width");
509
510 static_assert((Info.maxIndex - Info.minIndex + 1) <
511 CHAR_BIT * sizeof(Storage),
512 "Field width is not smaller than Bitpack's Storage type");
513
514 /// A span of set bits, starting at index 0, and of the field's width.
515 static constexpr Storage ValueMask =
516 (1U << (Info.maxIndex - Info.minIndex + 1)) - 1;
517
518 /// The mask of bits occupied by this value (occupied bits are set).
519 static constexpr Storage FieldMask = ValueMask << Info.minIndex;
520
521 /// Extract a Field from a raw Storage value
522 constexpr static Type raw_view(Storage storage)
523 {
524 return static_cast<Type>((storage >> Info.minIndex) & ValueMask);
525 }
526
527 /// Compute the underlying Storage transform for a Field update
528 constexpr static Storage raw_with(Storage lhs, Storage rhs)
529 {
530 return (lhs & ~FieldMask) | ((rhs & ValueMask) << Info.minIndex);
531 }
532
533 /// Update for fields whose type can be static_cast to Storage
534 constexpr static Storage raw_with(Storage lhs, Type rhs)
535 requires requires { static_cast<Storage>(std::declval<Type>()); }
536 {
537 return raw_with(lhs, static_cast<Storage>(rhs));
538 }
539
540 /// Update for fields that are themselves `Bitpacks` at smaller types
541 template<typename OtherStorage>
542 requires requires { sizeof(OtherStorage) < sizeof(Storage); }
543 constexpr static Storage raw_with(Storage lhs,
545 {
546 return raw_with(lhs, static_cast<OtherStorage>(rhs));
547 }
548
549 /**
550 * A proxy for this Field within the `Bitpack`'s `Storage`.
551 *
552 * This provides getters, setters, and mutators phrased in terms of the
553 * `Field`'s exposed `Type` rather than the raw bits within the
554 * `Bitpack`'s underlying `Storage` word.
555 *
556 * Proxies are templated on their containing derived type (so that they
557 * can act "in situ" in the class hierarchy, consuming and returning the
558 * same type, which must have `Bitpack` as its base type) and the type
559 * of the reference they hold to their containing `Bitpack`'s underlying
560 * `Storage` word (so that they can properly propagate `volatile` and
561 * `const` qualifications from the `Bitpack` value and the `Field`'s
562 * properties to perceived type).
563 */
564 template<typename DerivedBitpack, typename RefTypeParam>
565 requires std::is_base_of_v<Bitpack, DerivedBitpack> &&
566 std::is_lvalue_reference_v<RefTypeParam> &&
567 std::is_same_v<Storage, std::remove_cvref_t<RefTypeParam>>
568 class Proxy
569 {
570 /// A qualified reference to the containing `Bitpack`'s `Storage`.
571 RefTypeParam ref;
572
573 public:
574 /// Name the Field of which we are a proxy
575 using Field = Field;
576
577 /// Expose the qualified reference type we hold to the Storage
578 using RefType = RefTypeParam;
579
580 /**
581 * Construct a proxy given a reference to the storage word.
582 *
583 * May take any reference type implicitly convertible to RefType,
584 * not just RefType itself.
585 */
586 template<typename R>
587 constexpr Proxy(R &r) : ref(r.value)
588 {
589 }
590
591 /**
592 * Compute and store an updated `Bitpack` value with a new value for
593 * this field (of the field type itself).
594 *
595 * For `volatile` `Bitpack`s, this will perform a load and store.
596 */
597 template<typename Self>
598 requires(
599 !std::is_const_v<std::remove_reference_t<RefTypeParam>>)
600 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
601 constexpr Self &&operator=(this Self &&self, Field::Type rhs)
602 {
603 self.ref = raw_with(self.ref, rhs);
604 return self;
605 }
606
607 /**
608 * Compute and store an updated `Bitpack` value with a new value for
609 * this field, when this field's type is a Numeric wrapper, and the
610 * RHS is the underlying type inside the Numeric wrapper.
611 *
612 * For `volatile` `Bitpack`s, this will perform a load and store.
613 */
614 template<typename Self, typename RHS>
615 requires(
616 !std::is_const_v<std::remove_reference_t<RefTypeParam>> &&
617 std::is_same_v<Field::Type, Numeric<RHS>>)
618 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
619 constexpr Self &&operator=(this Self &&self, RHS rhs)
620 {
621 self.ref = raw_with(self.ref, Field::Type{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 an enum class and the
628 * RHS is the underlying type of that enum.
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_scoped_enum_v<Field::Type> &&
636 std::is_same_v<std::underlying_type_t<Field::Type>, RHS>)
637 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
638 constexpr Self &&operator=(this Self &&self, RHS rhs)
639 {
640 self.ref = raw_with(self.ref, Field::Type{rhs});
641 return self;
642 }
643
644 /**
645 * Explicit conversion of a Field::Proxy to the field value.
646 *
647 * The Proxy must not have a `volatile` reference to Storage.
648 * Use `.read()` on the containing `Bitpack`, first.
649 */
650 template<typename Self>
651 requires(
652 !std::is_volatile_v<std::remove_reference_t<RefTypeParam>>)
653 constexpr operator Field::Type(this Self &&self)
654 {
655 return raw_view(self.ref);
656 }
657
658 /// A shorter way of spelling static_cast<Field::Type>()...
659 [[nodiscard]] constexpr Field::Type raw() const
660 {
661 return static_cast<Field::Type>(*this);
662 }
663
664 /// Get the value of this enum-typed field as its underlying type.
665 [[nodiscard]] constexpr auto rawer() const
666 requires(std::is_enum_v<typename Field::Type>)
667 {
668 return std::to_underlying(this->raw());
669 }
670
671 /**
672 * Get the value of this Numeric-typed field as its underlying type.
673 */
674 [[nodiscard]] constexpr auto rawer() const
675 requires(std::is_base_of_v<
676 Numeric<typename std::remove_cvref_t<
677 typename Field::Type>::NumericType>,
678 typename std::remove_cvref_t<typename Field::Type>>)
679 {
680 return this->raw().raw();
681 }
682
683 /**
684 * Construct a new `Bitpack` value with an updated value of this
685 * field.
686 *
687 * This is emphatically not `volatile`-qualified, and has no
688 * `volatile`-qualified overload. `.read()` the containing
689 * `Bitpack`, first.
690 */
691 [[nodiscard]] constexpr DerivedBitpack with(Field::Type rhs) const
692 {
693 return DerivedBitpack{raw_with(this->ref, rhs)};
694 }
695
696 /**
697 * Construct a new `Bitpack` value with an updated value of this
698 * field as a function of its current value.
699 *
700 * This is emphatically not `volatile`-qualified, and has no
701 * `volatile`-qualified overload. `.read()` the containing
702 * `Bitpack`, first.
703 */
704 constexpr DerivedBitpack with(auto &&f) const
705 requires std::
706 is_invocable_r_v<Field::Type, decltype(f), Field::Type>
707 {
708 /*
709 * Perform one read and use it twice to avoid double-tapping
710 * volatile storage!
711 */
712 Storage storage = this->ref;
713 return DerivedBitpack{raw_with(storage, f(raw_view(storage)))};
714 }
715
716 /**
717 * Compute and store an updated `Bitpack` value with a new value for
718 * this field as a function of its current value.
719 *
720 * For `volatile` `Bitpack`s, this will perform a load and store.
721 */
722 template<typename Self>
723 constexpr void alter(this Self &&self, auto &&f)
724 requires(
725 std::
726 is_invocable_r_v<Field::Type, decltype(f), Field::Type> &&
727 !std::is_const_v<std::remove_reference_t<RefTypeParam>>)
728 {
729 Storage storage = self.ref;
730 self.ref = raw_with(storage, f(raw_view(storage)));
731 }
732
733 /**
734 * Convenience function for unconditionally assigning a field when
735 * it helps to have a zero value of that field's type.
736 *
737 * Use `.alter()` if the current value of the field is required.
738 *
739 * Because this requires a read of the whole `Bitpack` to update,
740 * we refuse to operate on `volatile` values, and because it's an
741 * assignment, we don't operate on `const` values either.
742 */
743 constexpr void assign_from(auto &&f)
744 requires(
745 std::is_invocable_r_v<Field::Type, decltype(f), Field::Type>)
746 {
747 this->ref = raw_with(this->ref, f(raw_view(0)));
748 }
749
750 constexpr auto operator<=>(Proxy rhs) const
751 requires(requires(Field::Type v) {
752 { v <=> v };
753 })
754 {
755 return raw() <=> rhs.raw();
756 }
757
758 constexpr auto operator<=>(Field::Type rhs) const
759 requires(requires(Field::Type v) {
760 { v <=> v };
761 })
762 {
763 return raw() <=> rhs;
764 }
765
766 constexpr bool operator==(Proxy rhs) const
767 requires(requires(Field::Type v) {
768 { v == v } -> std::same_as<bool>;
769 })
770 {
771 return raw() == rhs.raw();
772 }
773
774 constexpr bool operator==(Field::Type rhs) const
775 requires(requires(Field::Type v) {
776 { v == v } -> std::same_as<bool>;
777 })
778 {
779 return raw() == rhs;
780 }
781 };
782
783 template<bool C, typename T>
784 requires std::is_lvalue_reference_v<T>
785 using ConditionalConstRef = std::
786 conditional_t<C, std::add_const_t<std::remove_reference_t<T>> &, T>;
787
788 /*
789 * Guide template deduction to conclude that a Field has a RefType
790 * that is as CV-qualified as the `Bitpack` of which it is a part, with
791 * the further possibility of const-qualification for fields annotated
792 * as constant.
793 *
794 * Yes, the `decltype(())` is deliberate: we want the lvalue expression
795 * rather than the prvalue expression.
796 */
797 template<typename BitpackType>
798 Proxy(BitpackType &r)
799 -> Proxy<std::remove_cvref_t<BitpackType>,
800 ConditionalConstRef<Info.isConst, decltype((r.value))>>;
801 };
802
803 protected:
804 /**
805 * Build a Field::Proxy of an explicitly given type and info.
806 *
807 * This is protected so that it is available to subclasses but not more
808 * generally.
809 */
810 template<typename FieldType, FieldInfo Info, typename Self>
811 constexpr auto member(this Self &&self)
812 {
813 return typename Field<FieldType, Info>::Proxy(self);
814 }
815
816 public:
817 /**
818 * Build a Field::Proxy proxy by asking the derived Self class for a
819 * FieldInfo structure computed from template expansion of
820 * Self::field_info_for_type<FieldType>().
821 */
822 template<typename FieldType, typename Self>
823 requires std::is_convertible_v<
824 decltype(std::remove_cvref_t<Self>::template field_info_for_type<
825 FieldType>()),
826 const FieldInfo>
827 constexpr auto member(this Self &&self)
828 {
829 /*
830 * Find the information for the field type; "deducing this" gives us
831 * access to the derived class's type without need for the CRTP.
832 */
833 static constexpr FieldInfo Info =
834 std::remove_cvref_t<Self>::template field_info_for_type<FieldType>();
835
836 return self.template member<FieldType, Info>();
837 }
838
839 /**
840 * Fetch the value of a field in this `Bitpack` based on the field type.
841 *
842 * For `volatile` `Bitpack`s, this will perform a load.
843 */
844 template<typename FieldType, typename Self>
845 constexpr FieldType get(this Self &&self)
846 {
847 return self.template member<FieldType>();
848 }
849
850 /**
851 * Compute a new `Bitpack` value with an updated field of a given type.
852 *
853 * For `volatile` `Bitpack`s, this will perform a load.
854 */
855 template<typename FieldType, typename Self>
856 constexpr std::remove_cvref_t<Self> with(this Self &&self, FieldType v)
857 {
858 return self.template member<FieldType>().with(v);
859 }
860
861 /**
862 * Compute a new `Bitpack` value with an updated field of a given type.
863 *
864 * For `volatile` `Bitpack`s, this will perform a load and store.
865 */
866 template<typename FieldType, typename Self>
867 constexpr void set(this Self &&self, FieldType v)
868 {
869 self.template member<FieldType>() = v;
870 }
871
872 /**
873 * Convenience function for unconditionally changing several sub-fields at
874 * once. Intended particularly for use with `volatile` `Bitpack`-s, to help
875 * ensure only one read and one write takes place, as the callback operates
876 * on a non-`volatile` `Bitpack`.
877 */
878 template<typename Self>
879 constexpr void alter(this Self &&self, auto &&f)
880 requires std::is_invocable_r_v<std::remove_cvref_t<Self>,
881 decltype(f),
882 std::remove_cvref_t<Self>>
883 {
884 std::remove_cvref_t<Self> value{self.value};
885 self = f(value);
886 }
887
888 /**
889 * Convenience function for unconditionally assigning an entire `Bitpack`
890 * from a computed value. The function receives a zero-valued instance of
891 * the `Bitpack` type.
892 */
893 template<typename Self>
894 constexpr void assign_from(this Self &&self, auto &&f)
895 requires std::is_invocable_r_v<std::remove_cvref_t<Self>,
896 decltype(f),
897 std::remove_cvref_t<Self>>
898 {
899 self = f(std::remove_cvref_t<Self>(0));
900 }
901};
902
903/**
904 * It is occasionally useful to derive one bitpack from another. Using
905 * BitpackDerived<B> as the sole (non-empty) base class of an aggregate type
906 * reduces the syntactic clutter of deriving from the bitpack B.
907 *
908 * See `BITPACK_DERIVED_PREFIX` and `BITPACK_DERIVED_FIELD_INFO_FOR_TYPE`.
909 */
910template<typename B>
911 requires std::derived_from<B, Bitpack<typename B::Storage>>
913{
914 using B::B;
915 using B::operator=;
916
917 protected:
918 using ParentBitpack = B;
919};
920
921/**
922 * \defgroup bitpacks_macros Convenience macros
923 * @{
924 */
925
926/**
927 * \defgroup bitpacks_macros_defn Field definition macros
928 * @{
929 */
930
931/**
932 * Capture the incantations often at the top of a `Bitpack` structure,
933 * especially one with type-directed field proxies.
934 */
935#define BITPACK_USUAL_PREFIX \
936 using Bitpack::Bitpack; \
937 using Bitpack::operator=; \
938 template<typename FieldType> \
939 static constexpr FieldInfo field_info_for_type() = delete;
940
941/**
942 * Define a named accessor for a field.
943 *
944 * Takes the name of the accessor, the type to use for the proxy, and the fields
945 * of the FieldInfo (as varargs). If the latter is omitted, this defines an
946 * alias for a field whose type is sufficient to resolve the FieldInfo through
947 * field_info_for_type.
948 */
949#define BITPACK_MEMBER_ADD(name, Type, ...) \
950 template<typename Self> \
951 constexpr auto name(this Self &&self) \
952 { \
953 return self.template member<Type __VA_OPT__(, {__VA_ARGS__})>(); \
954 }
955
956/**
957 * Encapsulate the gyrations required to define an `enum class`-typed field and
958 * its associated `field_info_for_type` within a `Bitpack`. Takes the name of
959 * the `enum` class to define, its underlying type, and then the fields of a
960 * `FieldInfo` as varargs; the enumerators should follow the macro invocation,
961 * surrounded in curly braces and terminated with a semicolon.
962 */
963#define BITPACK_MEMBER_ADD_ENUM(Type, Base, ...) \
964 enum class Type : Base; \
965 template<> \
966 constexpr FieldInfo field_info_for_type<Type>() \
967 { \
968 constexpr auto info = FieldInfo{__VA_ARGS__}; \
969 static_assert(requires { Field<Type, info>(); }); \
970 return info; \
971 } \
972 enum class Type : Base
973
974/**
975 * Define a new scoped enumeration type whose underlying type is bool, with the
976 * given false and true value names, at the given bit position.
977 *
978 * FieldInfo fields other than minIndex and maxIndex may be provided as
979 * additional arguments.
980 *
981 * Contrast `BITPACK_MEMBER_ADD_BOOL`, which does not introduce the custom
982 * enumerator values like our `FalseVal` and `TrueVal`.
983 *
984 * See `BITPACK_MEMBER_ADD_ENUM`, which this uses, for more details.
985 */
986#define BITPACK_MEMBER_ADD_ENUM_BOOL(Type, FalseVal, TrueVal, BitIndex, ...) \
987 BITPACK_MEMBER_ADD_ENUM(Type, bool, BitIndex, BitIndex, __VA_ARGS__) \
988 { \
989 FalseVal = false, TrueVal = true, \
990 }
991
992/**
993 * Define a new boolean scoped enumeration with values named "Cleared" (0)
994 * and "Asserted" (1) at the given bit index.
995 */
996#define BITPACK_MEMBER_ADD_ENUM_BOOL_CLEARED_ASSERTED(Type, BitIndex, ...) \
997 BITPACK_MEMBER_ADD_ENUM_BOOL(Type, Cleared, Asserted, BitIndex, __VA_ARGS__)
998
999/**
1000 * Define a new boolean scoped enumeration with values named "Disabled" (0)
1001 * and "Enabled" (1) at the given bit index.
1002 */
1003#define BITPACK_MEMBER_ADD_ENUM_BOOL_DISABLED_ENABLED(Type, BitIndex, ...) \
1004 BITPACK_MEMBER_ADD_ENUM_BOOL(Type, Disabled, Enabled, BitIndex, __VA_ARGS__)
1005
1006/**
1007 * Encapsulate the gyrations required to define a `Numeric`-typed field and its
1008 * associated `field_info_for_type` within a `Bitpack`. Takes the name of the
1009 * struct to define, the `Numeric`'s underlying type, and then the fields of a
1010 * `FieldInfo` as varargs.
1011 */
1012#define BITPACK_MEMBER_ADD_NUMERIC(Type, Base, ...) \
1013 struct Type : Numeric<Base> \
1014 { \
1015 using Numeric::Numeric; \
1016 }; \
1017 template<> \
1018 constexpr FieldInfo field_info_for_type<Type>() \
1019 { \
1020 constexpr auto info = FieldInfo{__VA_ARGS__}; \
1021 static_assert(requires { Field<Type, info>(); }); \
1022 return info; \
1023 }
1024
1025/**
1026 * Define a new type wrapper around bool for a 1-bit field at a given BitIndex.
1027 *
1028 * FieldInfo fields other than minIndex and maxIndex may be provided as
1029 * additional arguments.
1030 *
1031 * By contrast to `BITPACK_MEMBER_ADD_ENUM_BOOL`, the values introduced here are
1032 * `Type{false}` and `Type{true}` rather than custom enumerators.
1033 *
1034 * See `BITPACK_MEMBER_ADD_NUMERIC`, which this uses, for more details.
1035 */
1036#define BITPACK_MEMBER_ADD_BOOL(Type, BitIndex, ...) \
1037 BITPACK_MEMBER_ADD_NUMERIC(Type, bool, BitIndex, BitIndex, __VA_ARGS__)
1038
1039/// @}
1040
1041/**
1042 * \defgroup bitpacks_macros_member Type-qualifying member accessor macros
1043 * @{
1044 */
1045
1046/**
1047 * A convenience macro that presumes the type `T` is defined within the
1048 * `Bitpack` `b` and finds such a field's proxy.
1049 *
1050 * There is no `BITPACK_MEMBER_TYPE(b, T)` analogue, because that's just
1051 * `b.member<T>()`.
1052 */
1053#define BITPACK_MEMBER_DECLTYPE(b, T) \
1054 (b).template member<std::remove_reference_t<decltype(b)>::T>()
1055
1056/**
1057 * Like BITPACK_MEMBER_DECLTYPE, but with additional an "typename" keyword so we
1058 * can use a dependently-typed bitpack `b` (say, the type of `b` is `auto` or,
1059 * more generally, involves a template argument).
1060 */
1061#define BITPACK_MEMBER_DEPENDENT(b, T) \
1062 (b).template member<typename std::remove_reference_t<decltype(b)>::T>()
1063
1064/// @}
1065
1066/**
1067 * \defgroup bitpacks_macros_derived Derived bitpack definition macros
1068 * @{
1069 */
1070
1071/**
1072 * Capture the incantations often at the top of a `BitpackDerived` structure.
1073 * especially one with type-directed field proxys.
1074 */
1075#define BITPACK_DERIVED_PREFIX \
1076 using BitpackDerived<ParentBitpack>::BitpackDerived; \
1077 using BitpackDerived<ParentBitpack>::operator=; \
1078 /* Inherit field info for most types from parent bitpack */ \
1079 template<typename FieldType> \
1080 static constexpr FieldInfo field_info_for_type() \
1081 { \
1082 return ParentBitpack::field_info_for_type<FieldType>(); \
1083 }
1084
1085/// Modify the field information for a type in a derived bitpack.
1086#define BITPACK_DERIVED_FIELD_INFO_FOR_TYPE(Type, lambda) \
1087 template<> \
1088 constexpr FieldInfo field_info_for_type<Type>() \
1089 { \
1090 static_assert( \
1091 std::is_invocable_r_v<void, decltype(lambda), FieldInfo &>); \
1092 auto fi = ParentBitpack::field_info_for_type<Type>(); \
1093 lambda(fi); \
1094 return fi; \
1095 }
1096
1097/**
1098 * Modify the constness field information for a type in a derived bitpack.
1099 * This is a thin wrapper around BITPACK_DERIVED_FIELD_INFO_FOR_TYPE.
1100 */
1101#define BITPACK_DERIVED_FIELD_CONST_FOR_TYPE(Type, c) \
1102 BITPACK_DERIVED_FIELD_INFO_FOR_TYPE(Type, [](auto &fi) { fi.isConst = c; })
1103
1104/// @}
1105
1106/**
1107 * \defgroup bitpacks_macros_proxyop_qualify Type-qualifying proxy operator
1108 * macros
1109 * @{
1110 */
1111
1112/**
1113 * Operate between a `Field::Proxy` `proxy` and a given `value`, which will be
1114 * qualifed with `proxy`'s `Field::Type` name.
1115 *
1116 * This will not work on `volatile` bitpacks; use `.read` first.
1117 */
1118#define BITPACK_OPERATE_QUALIFY(proxy, operator, value) \
1119 ({ \
1120 using F = decltype(proxy)::Field::Type; \
1121 (proxy) operator(F::value); \
1122 })
1123
1124/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_QUALIFY
1125#define BITPACK_OPERATE_QUALIFY_DECLTYPE(b, T, operator, v) \
1126 BITPACK_OPERATE_QUALIFY(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1127
1128/// A fusion of BITPACK_MEMBER_DEPENDENT and BITPACK_OPERATE_QUALIFY
1129#define BITPACK_OPERATE_QUALIFY_DEPENDENT(b, T, operator, v) \
1130 BITPACK_OPERATE_QUALIFY(BITPACK_MEMBER_DEPENDENT(b, T), operator, v)
1131
1132/// A fusion of .member<>() and BITPACK_OPERATE_QUALIFY
1133#define BITPACK_OPERATE_QUALIFY_TYPE(b, T, operator, v) \
1134 BITPACK_OPERATE_QUALIFY((b).template member<T>(), operator, v)
1135
1136/// A specialization of BITPACK_OPERATE_QUALIFY using `.with` as the operator
1137#define BITPACK_WITH_QUALIFY(proxy, value) \
1138 BITPACK_OPERATE_QUALIFY(proxy, .with, value)
1139
1140/**
1141 * A specialization of BITPACK_OPERATE_QUALIFY_DECLTYPE using `.with` as the
1142 * operator
1143 */
1144#define BITPACK_WITH_QUALIFY_DECLTYPE(b, T, v) \
1145 BITPACK_OPERATE_QUALIFY_DECLTYPE(b, T, .with, v)
1146
1147/**
1148 * A specialization of BITPACK_OPERATE_QUALIFY_DEPENDENT using `.with` as the
1149 * operator
1150 */
1151#define BITPACK_WITH_QUALIFY_DEPENDENT(b, T, v) \
1152 BITPACK_OPERATE_QUALIFY_DEPENDENT(b, T, .with, v)
1153
1154/**
1155 * A specialization of BITPACK_OPERATE_QUALIFY_TYPE using `.with` as the
1156 * operator
1157 */
1158#define BITPACK_WITH_QUALIFY_TYPE(b, T, v) \
1159 BITPACK_OPERATE_QUALIFY_TYPE(b, T, .with, v)
1160
1161/// @}
1162
1163/**
1164 * \defgroup bitpacks_macros_proxyop_construct Type-constructing proxy operator
1165 * macros
1166 *
1167 * @{
1168 */
1169
1170/**
1171 * Operate between a `Field::Proxy` `proxy` and a given `value`, which will be
1172 * wrapped with `proxy`'s `Field::Type`'s constructor.
1173 *
1174 * This will not work on `volatile` bitpacks; use `.read` first.
1175 */
1176#define BITPACK_OPERATE_WRAP(proxy, operator, value) \
1177 ({ \
1178 using F = decltype(proxy)::Field::Type; \
1179 (proxy) operator(F{value}); \
1180 })
1181
1182/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_WRAP
1183#define BITPACK_OPERATE_WRAP_DECLTYPE(b, T, operator, v) \
1184 BITPACK_OPERATE_WRAP(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1185
1186/// A fusion of BITPACK_MEMBER_DEPENDENT and BITPACK_OPERATE_WRAP
1187#define BITPACK_OPERATE_WRAP_DEPENDENT(b, T, operator, v) \
1188 BITPACK_OPERATE_WRAP(BITPACK_MEMBER_DEPENDENT(b, T), operator, v)
1189
1190/// A fusion of .member<>() and BITPACK_OPERATE_WRAP
1191#define BITPACK_OPERATE_WRAP_TYPE(b, T, operator, v) \
1192 BITPACK_OPERATE_WRAP((b).template member<T>(), operator, v)
1193
1194/// A specialization of BITPACK_OPERATE_WRAP using `.with` as the operator
1195#define BITPACK_WRAP_WITH(proxy, value) \
1196 BITPACK_OPERATE_WRAP(proxy, .with, value)
1197
1198/**
1199 * A specialization of BITPACK_OPERATE_WRAP_DECLTYPE using `.with` as the
1200 * operator.
1201 */
1202#define BITPACK_WITH_WRAP_DECLTYPE(b, T, v) \
1203 BITPACK_OPERATE_WRAP_DECLTYPE(b, T, .with, v)
1204
1205/**
1206 * A specialization of BITPACK_OPERATE_WRAP_DEPENDENT using `.with` as the
1207 * operator
1208 */
1209#define BITPACK_WITH_WRAP_DEPENDENT(b, T, v) \
1210 BITPACK_OPERATE_WRAP_DEPENDENT(b, T, .with, v)
1211
1212/// A specialization of BITPACK_OPERATE_WRAP_TYPE using `.with` as the operator
1213#define BITPACK_WITH_WRAP_TYPE(b, T, v) \
1214 BITPACK_OPERATE_WRAP_TYPE(b, T, .with, v)
1215
1216/// @}
1217
1218/**
1219 * \defgroup bitpacks_macros_proxyop_directed Type-directed proxy operator
1220 * macros
1221 *
1222 * @{
1223 */
1224
1225/**
1226 * Given a bitpack `b` -- not a Proxy of a Field therein -- and a value, operate
1227 * between the bitpack's view of that value's type and the value.
1228 */
1229#define BITPACK_OPERATE_VALUE(b, operator, value) \
1230 ({ (b).template member<decltype(value)>() operator(value); })
1231
1232/**
1233 * Given a bitpack `b` -- not a Proxy of a Field therein -- qualify the given
1234 * value with the bitpack's type and then operate between the bitpack's Proxy of
1235 * that qualified value's type and said value.
1236 */
1237#define BITPACK_OPERATE_VALUE_DECLTYPE(b, operator, value) \
1238 BITPACK_OPERATE_VALUE( \
1239 b, operator, std::remove_reference_t<decltype(b)>::value)
1240
1241/**
1242 * BITPACK_OPERATE_VALUE with dependent qualification for the type of the
1243 * bitpack.
1244 */
1245#define BITPACK_OPERATE_VALUE_DEPENDENT(b, operator, value) \
1246 BITPACK_OPERATE_VALUE( \
1247 b, operator, typename std::remove_reference_t<decltype(b)>::value)
1248
1249/// A specialization of BITPACK_OPERATE_VALUE using `.with` as the operator
1250#define BITPACK_WITH_VALUE(b, value) BITPACK_OPERATE_VALUE(b, .with, value)
1251
1252/**
1253 * A specialization of BITPACK_OPERATE_VALUE_DECLTYPE using `.with` as the
1254 * operator
1255 */
1256#define BITPACK_WITH_VALUE_DECLTYPE(b, value) \
1257 BITPACK_OPERATE_VALUE_DECLTYPE(b, .with, value)
1258
1259/**
1260 * A specialization of BITPACK_OPERATE_VALUE_DEPENDENT using `.with` as the
1261 * operator.
1262 */
1263#define BITPACK_WITH_VALUE_DEPENDENT(b, value) \
1264 BITPACK_OPERATE_VALUE_DEPENDENT(b, .with, value)
1265
1266/// @}
1267
1268/**
1269 * \defgroup bitpacks_macros_proxyop_enum "using enum" proxy operator macros
1270 *
1271 * @{
1272 */
1273
1274/**
1275 * Convenience for scoped enum fields, bringing the enumerators of the field
1276 * type into scope while evaluating the value.
1277 *
1278 * Note that this uses `using enum` internally, and so cannot work with
1279 * `Proxy`-s whose Field::Type-s are dependent. Alas, this precludes the
1280 * existence of a `BITPACK_*_ENUM_DEPENDENT` family of helpers.
1281 *
1282 * Probably prefer BITPACK_OPERATE_QUALIFY if you do not need repeated use of
1283 * the enumeration's values within the passed `value`.
1284 */
1285#define BITPACK_OPERATE_ENUM(proxy, operator, value) \
1286 ({ \
1287 using E = decltype(proxy)::Field::Type; \
1288 static_assert(std::is_enum_v<E>); \
1289 using enum E; \
1290 (proxy) operator(value); \
1291 })
1292
1293/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_ENUM
1294#define BITPACK_OPERATE_ENUM_DECLTYPE(b, T, operator, v) \
1295 BITPACK_OPERATE_ENUM(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1296
1297/// A fusion of .member<>() and BITPACK_OPERATE_ENUM
1298#define BITPACK_OPERATE_ENUM_TYPE(b, T, operator, v) \
1299 BITPACK_OPERATE_ENUM((b).template member<T>(), operator, v)
1300
1301/// A specialization of BITPACK_OPERATE_ENUM using `.with` as the operator
1302#define BITPACK_WITH_ENUM(proxy, value) \
1303 BITPACK_OPERATE_ENUM(proxy, .with, value)
1304
1305/**
1306 * A specialization of BITPACK_OPERATE_ENUM_DECLTYPE using `.with` as the
1307 * operator.
1308 */
1309#define BITPACK_WITH_ENUM_DECLTYPE(b, T, v) \
1310 BITPACK_OPERATE_ENUM_DECLTYPE(b, T, .with, v)
1311
1312/// A specialization of BITPACK_OPERATE_ENUM_TYPE using `.with` as the operator
1313#define BITPACK_WITH_ENUM_TYPE(b, T, v) \
1314 BITPACK_OPERATE_ENUM_TYPE(b, T, .with, v)
1315
1316/// @}
1317
1318/**
1319 * \defgroup bitpacks_macros_vararg Multi-field utility macros
1320 *
1321 * @{
1322 */
1323
1324/**
1325 * \defgroup bitpacks_macros_vararg_internal Internal helpers
1326 *
1327 * @{
1328 */
1329
1330/**
1331 * An in-situ probe for the #include-sion of <__macro_map.h>.
1332 *
1333 * While we could gate our behavior on `#if defined(CHERIOT_EVAL0)`, for
1334 * example, that would require that <__macro_map.h> be included before us, which
1335 * is slightly rude. Instead, we can take creative advantage of the syntactic
1336 * primitives __macro_map.h uses internally and some C++ quirks to (ab)usefully
1337 * test the behavior of `CHERIOT_EVAL0` at each expansion of this macro.
1338 *
1339 * In terms of operation, this relies on `U CHERIOT_EVAL0(T);` meaning one of
1340 * two very different things and so dramatically changing what the subsequent
1341 * `sizeof(T)` means.
1342 *
1343 * - If CHERIOT_EVAL0 hasn't been #defined, then this defines a function named
1344 * CHERIOT_EVAL0 which takes a `struct T` and returns a `U`. In this case,
1345 * the `T` in `sizeof(T)` means `struct T`, and that's 1 by definition,
1346 * since `sizeof(char)` is defined to be 1.
1347 *
1348 * - If CHERIOT_EVAL0 has been defined as per <__macro_map.h>, then this is
1349 * preprocessed into `U T;`, a variable declaration, and the `T` in
1350 * `sizeof(T)` now refers to that variable and evaluates to 2.
1351 */
1352#define BITPACK_HAS_MACRO_MAP \
1353 ([]() { \
1354 struct BITPACK_HAS_MACRO_MAP_T \
1355 { \
1356 char t; \
1357 }; \
1358 struct BITPACK_HAS_MACRO_MAP_U \
1359 { \
1360 char u[2]; \
1361 }; \
1362 BITPACK_HAS_MACRO_MAP_U CHERIOT_EVAL0(BITPACK_HAS_MACRO_MAP_T); \
1363 return sizeof(BITPACK_HAS_MACRO_MAP_T); \
1364 }() > 1)
1365
1366/// Map function for BITPACK_MAP_DECLTYPE
1367#define BITPACK_MAP_DECLTYPE_HELPER(x, b) \
1368 std::remove_reference_t<decltype(b)>::x
1369
1370/**
1371 * Given bitpack `b`, qualify each additional argument with `decltype(b)`. That
1372 * is, `BITPACK_MAP_DECLTYPE(b, X, Y)` evalutes to
1373 * `decltype(b)::X, decltype(b)::Y`.
1374 *
1375 * This requires #include <__macro_map.h> (and will give confusing error
1376 * messages if not included). (Because this is very much a C++ token level
1377 * hack, unlike the other __macro_map users, it's hard for us to statically
1378 * assert and give nice error messages.)
1379 */
1380#define BITPACK_MAP_DECLTYPE(b, ...) \
1381 CHERIOT_MAP_LIST_UD(BITPACK_MAP_DECLTYPE_HELPER, b, __VA_ARGS__)
1382
1383/// Map function for BITPACK_MAP_DEPENDENT
1384#define BITPACK_MAP_DEPENDENT_HELPER(x, b) \
1385 typename std::remove_reference_t<decltype(b)>::x
1386
1387/**
1388 * Given bitpack `b`, qualify each additional argument with `typename
1389 * decltype(b)`. That is, `BITPACK_DEPENDENT(b, X, Y)` evalutes to
1390 * `typename decltype(b)::X, typename decltype(b)::Y`.
1391 *
1392 * This requires #include <__macro_map.h>.
1393 */
1394#define BITPACK_MAP_DEPENDENT(b, ...) \
1395 CHERIOT_MAP_LIST_UD(BITPACK_MAP_DEPENDENT_HELPER, b, __VA_ARGS__)
1396
1397/// Map function for BITPACK_WITHS
1398#define BITPACK_MAP_WITHS_HELPER(x) .with(x)
1399
1400/// @}
1401
1402/**
1403 * Construct a chain of .with() whose arguments are all qualified with the type
1404 * of the bitpack. #include <__macro_map.h> if you want to use this.
1405 */
1406#define BITPACK_WITHS(b, ...) \
1407 ({ \
1408 static_assert(BITPACK_HAS_MACRO_MAP, \
1409 "BITPACK_WITHS requires __macro_map.h"); \
1410 (b) CHERIOT_MAP(BITPACK_MAP_WITHS_HELPER, __VA_ARGS__); \
1411 })
1412
1413/**
1414 * A version of BITPACK_WITHS where the arguments to `.with()` are qualified
1415 * with the type of the bitpack.
1416 */
1417#define BITPACK_WITHS_DECLTYPE(b, ...) \
1418 BITPACK_WITHS(b, BITPACK_MAP_DECLTYPE(b, __VA_ARGS__))
1419
1420/**
1421 * A version of BITPACK_WITHS where the arguments to `.with()` are dependently
1422 * qualified with the type of the bitpack.
1423 */
1424#define BITPACK_WITHS_DEPENDENT(b, ...) \
1425 BITPACK_WITHS(b, BITPACK_MAP_DEPENDENT(b, __VA_ARGS__))
1426
1427/**
1428 * @addtogroup macros_vararg_internal
1429 * @{
1430 */
1431
1432/// Helper for BITPACK_RELATE_MASKED, for computing individual field's masks
1433#define BITPACK_RELATE_MASKED_HELPER(x, b) \
1434 | ({ \
1435 using BT = decltype(b); \
1436 using FT = decltype(x); \
1437 BT::Field<FT, BT::field_info_for_type<FT>()>::FieldMask; \
1438 })
1439
1440/// @}
1441
1442/**
1443 * Given a list of field values (which must be fully qualified names), compute
1444 * the mask of these fields and the bitpack value holding these field values,
1445 * then mask `b` with the computed mask, and then use `operator` to relate the
1446 * result with the computed bitpack value. The last value for each field is
1447 * used. #include <__macro_map.h> if you want to use this.
1448 */
1449#define BITPACK_RELATE_MASKED(b, operator, ...) \
1450 ({ \
1451 static_assert(BITPACK_HAS_MACRO_MAP, \
1452 "BITPACK_MASKED_REL requires __macro_map.h"); \
1453 constexpr decltype(b.raw()) __bitpack_mask = \
1454 (0)CHERIOT_MAP_UD(BITPACK_RELATE_MASKED_HELPER, b, __VA_ARGS__); \
1455 constexpr auto __bitpack_query = \
1456 BITPACK_WITHS((decltype(b))(0), __VA_ARGS__).raw(); \
1457 (b.raw() & __bitpack_mask) operator(__bitpack_query); \
1458 })
1459
1460/**
1461 * A version of BITPACK_RELATE_MASKED where the field values are qualified with
1462 * the type of the bitpack.
1463 */
1464#define BITPACK_RELATE_MASKED_DECLTYPE(b, operator, ...) \
1465 BITPACK_RELATE_MASKED(b, operator, BITPACK_MAP_DECLTYPE(b, __VA_ARGS__))
1466
1467/**
1468 * A version of BITPACK_RELATE_MASKED where the field values are dependently
1469 * qualified with the type of the bitpack.
1470 */
1471#define BITPACK_RELATE_MASKED_DEPENDENT(b, operator, ...) \
1472 BITPACK_RELATE_MASKED(b, operator, BITPACK_MAP_DEPENDENT(b, __VA_ARGS__))
1473
1474/// @}
1475/// @}
1476/// @}
A proxy for this Field within the Bitpack's Storage.
Definition bitpack.hh:569
constexpr auto rawer() const
Get the value of this Numeric-typed field as its underlying type.
Definition bitpack.hh:674
constexpr DerivedBitpack with(Field::Type rhs) const
Construct a new Bitpack value with an updated value of this field.
Definition bitpack.hh:691
constexpr Field::Type raw() const
A shorter way of spelling static_cast<Field::Type>()...
Definition bitpack.hh:659
constexpr Proxy(R &r)
Construct a proxy given a reference to the storage word.
Definition bitpack.hh:587
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:704
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:723
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:601
constexpr auto rawer() const
Get the value of this enum-typed field as its underlying type.
Definition bitpack.hh:665
constexpr operator Field::Type(this Self &&self)
Explicit conversion of a Field::Proxy to the field value.
Definition bitpack.hh:653
RefTypeParam RefType
Expose the qualified reference type we hold to the Storage.
Definition bitpack.hh:578
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:743
Field Field
Name the Field of which we are a proxy.
Definition bitpack.hh:575
constexpr void set(this Self &&self, FieldType v)
Compute a new Bitpack value with an updated field of a given type.
Definition bitpack.hh:867
constexpr void assign_from(this Self &&self, auto &&f)
Convenience function for unconditionally assigning an entire Bitpack from a computed value.
Definition bitpack.hh:894
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:856
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:879
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:827
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:845
constexpr auto member(this Self &&self)
Build a Field::Proxy of an explicitly given type and info.
Definition bitpack.hh:811
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:913
Information about a field within a Bitpack.
Definition bitpack.hh:462
size_t maxIndex
Maximum 0-indexed bit position occupied by this field.
Definition bitpack.hh:466
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:484
static constexpr Storage raw_with(Storage lhs, Storage rhs)
Compute the underlying Storage transform for a Field update.
Definition bitpack.hh:528
static constexpr Storage FieldMask
The mask of bits occupied by this value (occupied bits are set).
Definition bitpack.hh:519
static constexpr Storage raw_with(Storage lhs, Bitpack< OtherStorage > rhs)
Update for fields that are themselves Bitpacks at smaller types.
Definition bitpack.hh:543
TypeParam Type
Expose the type parameter.
Definition bitpack.hh:486
static constexpr Storage ValueMask
A span of set bits, starting at index 0, and of the field's width.
Definition bitpack.hh:515
static constexpr Storage raw_with(Storage lhs, Type rhs)
Update for fields whose type can be static_cast to Storage.
Definition bitpack.hh:534
static constexpr Type raw_view(Storage storage)
Extract a Field from a raw Storage value.
Definition bitpack.hh:522
static constexpr FieldInfo Info
Expose the FieldInfo parameter.
Definition bitpack.hh:488
Convenience wrapper for the types of numeric fields.
Definition bitpack.hh:424
T NumericType
Export the underlying numeric type.
Definition bitpack.hh:426