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 <__macro_map.h>
291#include <cdefs.h>
292#include <concepts>
293#include <limits.h>
294#include <stddef.h>
295#include <type_traits>
296
297/*
298 * Mark the Bitpack class declaration with cdef.h's `CHERIOT_EXPERIMENTAL()` --
299 * that is, as eliciting a warning if CHERIOT_EXPERIMENTAL_APIS_WARN is defined
300 * -- unless CHERIOT_EXPERIMENTAL_NOWARN_BITPACKS is defined.
301 */
302#if defined(CHERIOT_EXPERIMENTAL) && \
303 !defined(CHERIOT_EXPERIMENTAL_NOWARN_BITPACKS)
304# define BITPACK_DECL_ANNOTATION \
305 CHERIOT_EXPERIMENTAL( \
306 "Bitpacks are an experimental CHERIoT RTOS feature")
307#else
308# define BITPACK_DECL_ANNOTATION
309#endif
310
311/**
312 * The `Bitpack` structure itself, templated on the underlying Storage type.
313 *
314 * This should be the sole (non-empty) superclass of a type representing a
315 * bitfield-esque composite structure.
316 */
317template<typename StorageParam>
318 requires std::is_unsigned_v<StorageParam>
319class BITPACK_DECL_ANNOTATION Bitpack
320{
321 public:
322 /// Expose the underlying storage type
323 using Storage = StorageParam;
324
325 private:
326 Storage value;
327
328 public:
329 /// Construct a `Bitpack` value with an underlying Storage of all zero bits
330 constexpr Bitpack() : value(0) {}
331
332 /**
333 * Construct a `Bitpack` value from a value of its underlying Storage type.
334 *
335 * This is marked `explicit` to make things like "device->register = 0x7;"
336 * slightly harder to spell, with the intent of encouraging use of named
337 * constants.
338 */
339 constexpr explicit Bitpack(Storage v) : value(v) {}
340
341 /**
342 * Assign a whole `Bitpack` at once given a non-volatile `Bitpack` of the
343 * same (or convertible, including derived) type.
344 *
345 * To assign from a `volatile` `Bitpack`, perform an explicit `.read()`
346 * first.
347 *
348 * "Deducing this" gives us CRTP-esque behavior without, well, the CRTP, so
349 * this will not accept an unrelated derived `Bitpack` class. (But will
350 * accept types that can be converted, so assigning to a `Bitpack`, as
351 * opposed to a derived class, will accept any derived class, for example.
352 * Probably best avoid that.)
353 */
354 template<typename Self>
355 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
356 constexpr Self &&operator=(this Self &&self,
357 const std::remove_cvref_t<Self> &b)
358 {
359 self.value = b.value;
360 return self;
361 }
362
363 /**
364 * Compare `Bitpack`-s for equality (reflecting that of `Storage`).
365 *
366 * Neither side may be volatile; use explicit `.read()`-s first.
367 */
368 template<typename Self>
369 requires(!std::is_volatile_v<Self>)
370 constexpr bool operator==(this Self &&self,
371 const std::remove_cvref_t<Self> &b)
372 {
373 return self.value == b.value;
374 }
375
376 /**
377 * Spaceship operator on `Bitpack`-s (reflecing that of `Storage`).
378 *
379 * Neither side may be volatile; use explicit `.read()`-s first.
380 */
381 template<typename Self>
382 requires(!std::is_volatile_v<Self>)
383 constexpr auto operator<=>(this Self &&self,
384 const std::remove_cvref_t<Self> &b)
385 {
386 return self.value <=> b.value;
387 }
388
389 /**
390 * Extract the underlying `Storage` value.
391 *
392 * The `Bitpack` must not be `volatile`. Use `.read()` first.
393 */
394 constexpr explicit operator Storage() const
395 {
396 return this->value;
397 }
398
399 /**
400 * A shorter way of spelling `static_cast<Storage>(...)`.
401 *
402 * The `Bitpack` must not be `volatile`. Use `.read()` first.
403 */
404 [[nodiscard]] constexpr Storage raw() const
405 {
406 return static_cast<Storage>(*this);
407 }
408
409 /**
410 * Return a snapshot of the underlying `Storage`.
411 *
412 * Notably, this can be used to get a `const` `Bitpack` from a `volatile`
413 * `Bitpack` (`const` or not).
414 */
415 template<typename Self>
416 const auto read(this Self &&self)
417 {
418 return std::remove_cvref_t<Self>{self.value};
419 }
420
421 /// Convenience wrapper for the types of numeric fields.
422 template<typename T>
423 requires std::is_unsigned_v<T>
424 struct Numeric
425 {
426 /// Export the underlying numeric type
427 using NumericType = T;
428
429 /*
430 * The field type must be smaller than the `Bitpack`'s underlying
431 * `Storage`.
432 */
433 static_assert(sizeof(T) <= sizeof(Storage));
434
435 T value;
436
437 constexpr Numeric(T v) : value(v) {}
438
439 constexpr operator T() const
440 {
441 return this->value;
442 }
443
444 [[nodiscard]] constexpr T raw() const
445 {
446 return static_cast<T>(this->value);
447 }
448 };
449
450 /**
451 * Information about a field within a `Bitpack`.
452 *
453 * Fields are contiguous spans of bits and so have a lowest and highest bit
454 * position within the underlying Storage word.
455 *
456 * Fields may be marked as constant to dissuade software from attempting to
457 * change their value. Do note, though, that any store of an underlying
458 * Storage word will also, necessarily, include the bits for constant
459 * Fields. It is likely generally more advisable to not mix constant and
460 * non-constant fields within the same Storage word or `Bitpack`.
461 */
463 {
464 /// Minimum 0-indexed bit position occupied by this field
465 size_t minIndex;
466 /// Maximum 0-indexed bit position occupied by this field
467 size_t maxIndex;
468 /**
469 * Should this field be proxied as constant (and so mutation be slightly
470 * less ergonomic)?
471 */
472 bool isConst = false;
473
474 bool operator==(const FieldInfo &) const = default;
475 };
476
477 /**
478 * A particular Field within a `Bitpack`.
479 *
480 * Fields are templated on the type as seen by external code and the
481 * FieldInfo data giving the field's location and other properties.
482 */
483 template<typename TypeParam, FieldInfo InfoParam>
484 struct Field
485 {
486 /// Expose the type parameter
487 using Type = TypeParam;
488 /// Expose the FieldInfo parameter
489 static constexpr FieldInfo Info = InfoParam;
490
491 // The field's span of bits must start before it ends
492 static_assert(Info.maxIndex >= Info.minIndex,
493 "Field span ends before it begins");
494
495 static_assert(!std::is_base_of_v<Numeric<bool>, Type> ||
496 (Info.maxIndex == Info.minIndex),
497 "Numeric<bool> fields should be exactly one bit wide");
498
499 static_assert(
500 !std::is_enum_v<Type> ||
501 !requires {
502 { std::underlying_type_t<Type>() } -> std::same_as<bool>;
503 } || (Info.maxIndex == Info.minIndex),
504 "Enum fields with underlying type bool should be exactly "
505 "one bit wide");
506
507 static_assert((Info.maxIndex - Info.minIndex + 1) <=
508 CHAR_BIT * sizeof(Type),
509 "Field type is narrower than specified field bit width");
510
511 static_assert((Info.maxIndex - Info.minIndex + 1) <
512 CHAR_BIT * sizeof(Storage),
513 "Field width is not smaller than Bitpack's Storage type");
514
515 /// A span of set bits, starting at index 0, and of the field's width.
516 static constexpr Storage ValueMask =
517 (1U << (Info.maxIndex - Info.minIndex + 1)) - 1;
518
519 /// The mask of bits occupied by this value (occupied bits are set).
520 static constexpr Storage FieldMask = ValueMask << Info.minIndex;
521
522 /// Extract a Field from a raw Storage value
523 constexpr static Type raw_view(Storage storage)
524 {
525 return static_cast<Type>((storage >> Info.minIndex) & ValueMask);
526 }
527
528 /// Compute the underlying Storage transform for a Field update
529 constexpr static Storage raw_with(Storage lhs, Storage rhs)
530 {
531 return (lhs & ~FieldMask) | ((rhs & ValueMask) << Info.minIndex);
532 }
533
534 /// Update for fields whose type can be static_cast to Storage
535 constexpr static Storage raw_with(Storage lhs, Type rhs)
536 requires requires { static_cast<Storage>(std::declval<Type>()); }
537 {
538 return raw_with(lhs, static_cast<Storage>(rhs));
539 }
540
541 /// Update for fields that are themselves `Bitpacks` at smaller types
542 template<typename OtherStorage>
543 requires requires { sizeof(OtherStorage) < sizeof(Storage); }
544 constexpr static Storage raw_with(Storage lhs,
546 {
547 return raw_with(lhs, static_cast<OtherStorage>(rhs));
548 }
549
550 /**
551 * A proxy for this Field within the `Bitpack`'s `Storage`.
552 *
553 * This provides getters, setters, and mutators phrased in terms of the
554 * `Field`'s exposed `Type` rather than the raw bits within the
555 * `Bitpack`'s underlying `Storage` word.
556 *
557 * Proxies are templated on their containing derived type (so that they
558 * can act "in situ" in the class hierarchy, consuming and returning the
559 * same type, which must have `Bitpack` as its base type) and the type
560 * of the reference they hold to their containing `Bitpack`'s underlying
561 * `Storage` word (so that they can properly propagate `volatile` and
562 * `const` qualifications from the `Bitpack` value and the `Field`'s
563 * properties to perceived type).
564 */
565 template<typename DerivedBitpack, typename RefTypeParam>
566 requires std::is_base_of_v<Bitpack, DerivedBitpack> &&
567 std::is_lvalue_reference_v<RefTypeParam> &&
568 std::is_same_v<Storage, std::remove_cvref_t<RefTypeParam>>
569 class Proxy
570 {
571 /// A qualified reference to the containing `Bitpack`'s `Storage`.
572 RefTypeParam ref;
573
574 public:
575 /// Name the Field of which we are a proxy
576 using Field = Field;
577
578 /// Expose the qualified reference type we hold to the Storage
579 using RefType = RefTypeParam;
580
581 /**
582 * Construct a proxy given a reference to the storage word.
583 *
584 * May take any reference type implicitly convertible to RefType,
585 * not just RefType itself.
586 */
587 template<typename R>
588 constexpr Proxy(R &r) : ref(r.value)
589 {
590 }
591
592 /**
593 * Compute and store an updated `Bitpack` value with a new value for
594 * this field (of the field type itself).
595 *
596 * For `volatile` `Bitpack`s, this will perform a load and store.
597 */
598 template<typename Self>
599 requires(
600 !std::is_const_v<std::remove_reference_t<RefTypeParam>>)
601 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
602 constexpr Self &&operator=(this Self &&self, Field::Type rhs)
603 {
604 self.ref = raw_with(self.ref, rhs);
605 return self;
606 }
607
608 /**
609 * Compute and store an updated `Bitpack` value with a new value for
610 * this field, when this field's type is a Numeric wrapper, and the
611 * RHS is the underlying type inside the Numeric wrapper.
612 *
613 * For `volatile` `Bitpack`s, this will perform a load and store.
614 */
615 template<typename Self, typename RHS>
616 requires(
617 !std::is_const_v<std::remove_reference_t<RefTypeParam>> &&
618 std::is_same_v<Field::Type, Numeric<RHS>>)
619 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
620 constexpr Self &&operator=(this Self &&self, RHS rhs)
621 {
622 self.ref = raw_with(self.ref, Field::Type{rhs});
623 return self;
624 }
625
626 /**
627 * Compute and store an updated `Bitpack` value with a new value for
628 * this field, when this field's type is an enum class and the
629 * RHS is the underlying type of that enum.
630 *
631 * For `volatile` `Bitpack`s, this will perform a load and store.
632 */
633 template<typename Self, typename RHS>
634 requires(
635 !std::is_const_v<std::remove_reference_t<RefTypeParam>> &&
636 std::is_scoped_enum_v<Field::Type> &&
637 std::is_same_v<std::underlying_type_t<Field::Type>, RHS>)
638 // NOLINTNEXTLINE(misc-unconventional-assign-operator)
639 constexpr Self &&operator=(this Self &&self, RHS rhs)
640 {
641 self.ref = raw_with(self.ref, Field::Type{rhs});
642 return self;
643 }
644
645 /**
646 * Explicit conversion of a Field::Proxy to the field value.
647 *
648 * The Proxy must not have a `volatile` reference to Storage.
649 * Use `.read()` on the containing `Bitpack`, first.
650 */
651 template<typename Self>
652 requires(
653 !std::is_volatile_v<std::remove_reference_t<RefTypeParam>>)
654 constexpr operator Field::Type(this Self &&self)
655 {
656 return raw_view(self.ref);
657 }
658
659 /// A shorter way of spelling static_cast<Field::Type>()...
660 [[nodiscard]] constexpr Field::Type raw() const
661 {
662 return static_cast<Field::Type>(*this);
663 }
664
665 /// Get the value of this enum-typed field as its underlying type.
666 [[nodiscard]] constexpr auto rawer() const
667 requires(std::is_enum_v<typename Field::Type>)
668 {
669 return std::to_underlying(this->raw());
670 }
671
672 /**
673 * Get the value of this Numeric-typed field as its underlying type.
674 */
675 [[nodiscard]] constexpr auto rawer() const
676 requires(std::is_base_of_v<
677 Numeric<typename std::remove_cvref_t<
678 typename Field::Type>::NumericType>,
679 typename std::remove_cvref_t<typename Field::Type>>)
680 {
681 return this->raw().raw();
682 }
683
684 /**
685 * Construct a new `Bitpack` value with an updated value of this
686 * field.
687 *
688 * This is emphatically not `volatile`-qualified, and has no
689 * `volatile`-qualified overload. `.read()` the containing
690 * `Bitpack`, first.
691 */
692 [[nodiscard]] constexpr DerivedBitpack with(Field::Type rhs) const
693 {
694 return DerivedBitpack{raw_with(this->ref, rhs)};
695 }
696
697 /**
698 * Construct a new `Bitpack` value with an updated value of this
699 * field as a function of its current value.
700 *
701 * This is emphatically not `volatile`-qualified, and has no
702 * `volatile`-qualified overload. `.read()` the containing
703 * `Bitpack`, first.
704 */
705 constexpr DerivedBitpack with(auto &&f) const
706 requires std::
707 is_invocable_r_v<Field::Type, decltype(f), Field::Type>
708 {
709 /*
710 * Perform one read and use it twice to avoid double-tapping
711 * volatile storage!
712 */
713 Storage storage = this->ref;
714 return DerivedBitpack{raw_with(storage, f(raw_view(storage)))};
715 }
716
717 /**
718 * Compute and store an updated `Bitpack` value with a new value for
719 * this field as a function of its current value.
720 *
721 * For `volatile` `Bitpack`s, this will perform a load and store.
722 */
723 template<typename Self>
724 constexpr void alter(this Self &&self, auto &&f)
725 requires(
726 std::
727 is_invocable_r_v<Field::Type, decltype(f), Field::Type> &&
728 !std::is_const_v<std::remove_reference_t<RefTypeParam>>)
729 {
730 Storage storage = self.ref;
731 self.ref = raw_with(storage, f(raw_view(storage)));
732 }
733
734 /**
735 * Convenience function for unconditionally assigning a field when
736 * it helps to have a zero value of that field's type.
737 *
738 * Use `.alter()` if the current value of the field is required.
739 *
740 * Because this requires a read of the whole `Bitpack` to update,
741 * we refuse to operate on `volatile` values, and because it's an
742 * assignment, we don't operate on `const` values either.
743 */
744 constexpr void assign_from(auto &&f)
745 requires(
746 std::is_invocable_r_v<Field::Type, decltype(f), Field::Type>)
747 {
748 this->ref = raw_with(this->ref, f(raw_view(0)));
749 }
750
751 constexpr auto operator<=>(Proxy rhs) const
752 requires(requires(Field::Type v) {
753 { v <=> v };
754 })
755 {
756 return raw() <=> rhs.raw();
757 }
758
759 constexpr auto operator<=>(Field::Type rhs) const
760 requires(requires(Field::Type v) {
761 { v <=> v };
762 })
763 {
764 return raw() <=> rhs;
765 }
766
767 constexpr bool operator==(Proxy rhs) const
768 requires(requires(Field::Type v) {
769 { v == v } -> std::same_as<bool>;
770 })
771 {
772 return raw() == rhs.raw();
773 }
774
775 constexpr bool operator==(Field::Type rhs) const
776 requires(requires(Field::Type v) {
777 { v == v } -> std::same_as<bool>;
778 })
779 {
780 return raw() == rhs;
781 }
782 };
783
784 template<bool C, typename T>
785 requires std::is_lvalue_reference_v<T>
786 using ConditionalConstRef = std::
787 conditional_t<C, std::add_const_t<std::remove_reference_t<T>> &, T>;
788
789 /*
790 * Guide template deduction to conclude that a Field has a RefType
791 * that is as CV-qualified as the `Bitpack` of which it is a part, with
792 * the further possibility of const-qualification for fields annotated
793 * as constant.
794 *
795 * Yes, the `decltype(())` is deliberate: we want the lvalue expression
796 * rather than the prvalue expression.
797 */
798 template<typename BitpackType>
799 Proxy(BitpackType &r)
800 -> Proxy<std::remove_cvref_t<BitpackType>,
801 ConditionalConstRef<Info.isConst, decltype((r.value))>>;
802 };
803
804 protected:
805 /**
806 * Build a Field::Proxy of an explicitly given type and info.
807 *
808 * This is protected so that it is available to subclasses but not more
809 * generally.
810 */
811 template<typename FieldType, FieldInfo Info, typename Self>
812 constexpr auto member(this Self &&self)
813 {
814 return typename Field<FieldType, Info>::Proxy(self);
815 }
816
817 public:
818 /**
819 * Build a Field::Proxy proxy by asking the derived Self class for a
820 * FieldInfo structure computed from template expansion of
821 * Self::field_info_for_type<FieldType>().
822 */
823 template<typename FieldType, typename Self>
824 requires std::is_convertible_v<
825 decltype(std::remove_cvref_t<Self>::template field_info_for_type<
826 FieldType>()),
827 const FieldInfo>
828 constexpr auto member(this Self &&self)
829 {
830 /*
831 * Find the information for the field type; "deducing this" gives us
832 * access to the derived class's type without need for the CRTP.
833 */
834 static constexpr FieldInfo Info =
835 std::remove_cvref_t<Self>::template field_info_for_type<FieldType>();
836
837 return self.template member<FieldType, Info>();
838 }
839
840 /**
841 * Fetch the value of a field in this `Bitpack` based on the field type.
842 *
843 * For `volatile` `Bitpack`s, this will perform a load.
844 */
845 template<typename FieldType, typename Self>
846 constexpr FieldType get(this Self &&self)
847 {
848 return self.template member<FieldType>();
849 }
850
851 /**
852 * Compute a new `Bitpack` value with an updated field of a given type.
853 *
854 * For `volatile` `Bitpack`s, this will perform a load.
855 */
856 template<typename FieldType, typename Self>
857 constexpr std::remove_cvref_t<Self> with(this Self &&self, FieldType v)
858 {
859 return self.template member<FieldType>().with(v);
860 }
861
862 /**
863 * Compute a new `Bitpack` value with an updated field of a given type.
864 *
865 * For `volatile` `Bitpack`s, this will perform a load and store.
866 */
867 template<typename FieldType, typename Self>
868 constexpr void set(this Self &&self, FieldType v)
869 {
870 self.template member<FieldType>() = v;
871 }
872
873 /**
874 * Convenience function for unconditionally changing several sub-fields at
875 * once. Intended particularly for use with `volatile` `Bitpack`-s, to help
876 * ensure only one read and one write takes place, as the callback operates
877 * on a non-`volatile` `Bitpack`.
878 */
879 template<typename Self>
880 constexpr void alter(this Self &&self, auto &&f)
881 requires std::is_invocable_r_v<std::remove_cvref_t<Self>,
882 decltype(f),
883 std::remove_cvref_t<Self>>
884 {
885 std::remove_cvref_t<Self> value{self.value};
886 self = f(value);
887 }
888
889 /**
890 * Convenience function for unconditionally assigning an entire `Bitpack`
891 * from a computed value. The function receives a zero-valued instance of
892 * the `Bitpack` type.
893 */
894 template<typename Self>
895 constexpr void assign_from(this Self &&self, auto &&f)
896 requires std::is_invocable_r_v<std::remove_cvref_t<Self>,
897 decltype(f),
898 std::remove_cvref_t<Self>>
899 {
900 self = f(std::remove_cvref_t<Self>(0));
901 }
902};
903
904/**
905 * It is occasionally useful to derive one bitpack from another. Using
906 * BitpackDerived<B> as the sole (non-empty) base class of an aggregate type
907 * reduces the syntactic clutter of deriving from the bitpack B.
908 *
909 * See `BITPACK_DERIVED_PREFIX` and `BITPACK_DERIVED_FIELD_INFO_FOR_TYPE`.
910 */
911template<typename B>
912 requires std::derived_from<B, Bitpack<typename B::Storage>>
914{
915 using B::B;
916 using B::operator=;
917
918 protected:
919 using ParentBitpack = B;
920};
921
922/**
923 * \defgroup bitpacks_macros Convenience macros
924 * @{
925 */
926
927/**
928 * \defgroup bitpacks_macros_defn Field definition macros
929 * @{
930 */
931
932/**
933 * Capture the incantations often at the top of a `Bitpack` structure,
934 * especially one with type-directed field proxies.
935 */
936#define BITPACK_USUAL_PREFIX \
937 using Bitpack::Bitpack; \
938 using Bitpack::operator=; \
939 template<typename FieldType> \
940 static constexpr FieldInfo field_info_for_type() = delete;
941
942/**
943 * Define a named accessor for a field.
944 *
945 * Takes the name of the accessor, the type to use for the proxy, and the fields
946 * of the FieldInfo (as varargs). If the latter is omitted, this defines an
947 * alias for a field whose type is sufficient to resolve the FieldInfo through
948 * field_info_for_type.
949 */
950#define BITPACK_MEMBER_ADD(name, Type, ...) \
951 template<typename Self> \
952 constexpr auto name(this Self &&self) \
953 { \
954 return self.template member<Type __VA_OPT__(, {__VA_ARGS__})>(); \
955 }
956
957/**
958 * Encapsulate the gyrations required to define an `enum class`-typed field and
959 * its associated `field_info_for_type` within a `Bitpack`. Takes the name of
960 * the `enum` class to define, its underlying type, and then the fields of a
961 * `FieldInfo` as varargs; the enumerators should follow the macro invocation,
962 * surrounded in curly braces and terminated with a semicolon.
963 */
964#define BITPACK_MEMBER_ADD_ENUM(Type, Base, ...) \
965 enum class Type : Base; \
966 template<> \
967 constexpr FieldInfo field_info_for_type<Type>() \
968 { \
969 constexpr auto info = FieldInfo{__VA_ARGS__}; \
970 static_assert(requires { Field<Type, info>(); }); \
971 return info; \
972 } \
973 enum class Type : Base
974
975/**
976 * Define a new scoped enumeration type whose underlying type is bool, with the
977 * given false and true value names, at the given bit position.
978 *
979 * FieldInfo fields other than minIndex and maxIndex may be provided as
980 * additional arguments.
981 *
982 * Contrast `BITPACK_MEMBER_ADD_BOOL`, which does not introduce the custom
983 * enumerator values like our `FalseVal` and `TrueVal`.
984 *
985 * See `BITPACK_MEMBER_ADD_ENUM`, which this uses, for more details.
986 */
987#define BITPACK_MEMBER_ADD_ENUM_BOOL(Type, FalseVal, TrueVal, BitIndex, ...) \
988 BITPACK_MEMBER_ADD_ENUM(Type, bool, BitIndex, BitIndex, __VA_ARGS__) \
989 { \
990 FalseVal = false, TrueVal = true, \
991 }
992
993/**
994 * Define a new boolean scoped enumeration with values named "Cleared" (0)
995 * and "Asserted" (1) at the given bit index.
996 */
997#define BITPACK_MEMBER_ADD_ENUM_BOOL_CLEARED_ASSERTED(Type, BitIndex, ...) \
998 BITPACK_MEMBER_ADD_ENUM_BOOL(Type, Cleared, Asserted, BitIndex, __VA_ARGS__)
999
1000/**
1001 * Define a new boolean scoped enumeration with values named "Disabled" (0)
1002 * and "Enabled" (1) at the given bit index.
1003 */
1004#define BITPACK_MEMBER_ADD_ENUM_BOOL_DISABLED_ENABLED(Type, BitIndex, ...) \
1005 BITPACK_MEMBER_ADD_ENUM_BOOL(Type, Disabled, Enabled, BitIndex, __VA_ARGS__)
1006
1007/**
1008 * Encapsulate the gyrations required to define a `Numeric`-typed field and its
1009 * associated `field_info_for_type` within a `Bitpack`. Takes the name of the
1010 * struct to define, the `Numeric`'s underlying type, and then the fields of a
1011 * `FieldInfo` as varargs.
1012 */
1013#define BITPACK_MEMBER_ADD_NUMERIC(Type, Base, ...) \
1014 struct Type : Numeric<Base> \
1015 { \
1016 using Numeric::Numeric; \
1017 }; \
1018 template<> \
1019 constexpr FieldInfo field_info_for_type<Type>() \
1020 { \
1021 constexpr auto info = FieldInfo{__VA_ARGS__}; \
1022 static_assert(requires { Field<Type, info>(); }); \
1023 return info; \
1024 }
1025
1026/**
1027 * Define a new type wrapper around bool for a 1-bit field at a given BitIndex.
1028 *
1029 * FieldInfo fields other than minIndex and maxIndex may be provided as
1030 * additional arguments.
1031 *
1032 * By contrast to `BITPACK_MEMBER_ADD_ENUM_BOOL`, the values introduced here are
1033 * `Type{false}` and `Type{true}` rather than custom enumerators.
1034 *
1035 * See `BITPACK_MEMBER_ADD_NUMERIC`, which this uses, for more details.
1036 */
1037#define BITPACK_MEMBER_ADD_BOOL(Type, BitIndex, ...) \
1038 BITPACK_MEMBER_ADD_NUMERIC(Type, bool, BitIndex, BitIndex, __VA_ARGS__)
1039
1040/// @}
1041
1042/**
1043 * \defgroup bitpacks_macros_member Type-qualifying member accessor macros
1044 * @{
1045 */
1046
1047/**
1048 * A convenience macro that presumes the type `T` is defined within the
1049 * `Bitpack` `b` and finds such a field's proxy.
1050 *
1051 * There is no `BITPACK_MEMBER_TYPE(b, T)` analogue, because that's just
1052 * `b.member<T>()`.
1053 */
1054#define BITPACK_MEMBER_DECLTYPE(b, T) \
1055 (b).template member<std::remove_reference_t<decltype(b)>::T>()
1056
1057/**
1058 * Like BITPACK_MEMBER_DECLTYPE, but with additional an "typename" keyword so we
1059 * can use a dependently-typed bitpack `b` (say, the type of `b` is `auto` or,
1060 * more generally, involves a template argument).
1061 */
1062#define BITPACK_MEMBER_DEPENDENT(b, T) \
1063 (b).template member<typename std::remove_reference_t<decltype(b)>::T>()
1064
1065/// @}
1066
1067/**
1068 * \defgroup bitpacks_macros_derived Derived bitpack definition macros
1069 * @{
1070 */
1071
1072/**
1073 * Capture the incantations often at the top of a `BitpackDerived` structure.
1074 * especially one with type-directed field proxys.
1075 */
1076#define BITPACK_DERIVED_PREFIX \
1077 using BitpackDerived<ParentBitpack>::BitpackDerived; \
1078 using BitpackDerived<ParentBitpack>::operator=; \
1079 /* Inherit field info for most types from parent bitpack */ \
1080 template<typename FieldType> \
1081 static constexpr FieldInfo field_info_for_type() \
1082 { \
1083 return ParentBitpack::field_info_for_type<FieldType>(); \
1084 }
1085
1086/// Modify the field information for a type in a derived bitpack.
1087#define BITPACK_DERIVED_FIELD_INFO_FOR_TYPE(Type, lambda) \
1088 template<> \
1089 constexpr FieldInfo field_info_for_type<Type>() \
1090 { \
1091 static_assert( \
1092 std::is_invocable_r_v<void, decltype(lambda), FieldInfo &>); \
1093 auto fi = ParentBitpack::field_info_for_type<Type>(); \
1094 lambda(fi); \
1095 return fi; \
1096 }
1097
1098/**
1099 * Modify the constness field information for a type in a derived bitpack.
1100 * This is a thin wrapper around BITPACK_DERIVED_FIELD_INFO_FOR_TYPE.
1101 */
1102#define BITPACK_DERIVED_FIELD_CONST_FOR_TYPE(Type, c) \
1103 BITPACK_DERIVED_FIELD_INFO_FOR_TYPE(Type, [](auto &fi) { fi.isConst = c; })
1104
1105/// @}
1106
1107/**
1108 * \defgroup bitpacks_macros_proxyop_qualify Type-qualifying proxy operator
1109 * macros
1110 * @{
1111 */
1112
1113/**
1114 * Operate between a `Field::Proxy` `proxy` and a given `value`, which will be
1115 * qualifed with `proxy`'s `Field::Type` name.
1116 *
1117 * This will not work on `volatile` bitpacks; use `.read` first.
1118 */
1119#define BITPACK_OPERATE_QUALIFY(proxy, operator, value) \
1120 ({ \
1121 using F = decltype(proxy)::Field::Type; \
1122 (proxy) operator(F::value); \
1123 })
1124
1125/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_QUALIFY
1126#define BITPACK_OPERATE_QUALIFY_DECLTYPE(b, T, operator, v) \
1127 BITPACK_OPERATE_QUALIFY(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1128
1129/// A fusion of BITPACK_MEMBER_DEPENDENT and BITPACK_OPERATE_QUALIFY
1130#define BITPACK_OPERATE_QUALIFY_DEPENDENT(b, T, operator, v) \
1131 BITPACK_OPERATE_QUALIFY(BITPACK_MEMBER_DEPENDENT(b, T), operator, v)
1132
1133/// A fusion of .member<>() and BITPACK_OPERATE_QUALIFY
1134#define BITPACK_OPERATE_QUALIFY_TYPE(b, T, operator, v) \
1135 BITPACK_OPERATE_QUALIFY((b).template member<T>(), operator, v)
1136
1137/// A specialization of BITPACK_OPERATE_QUALIFY using `.with` as the operator
1138#define BITPACK_WITH_QUALIFY(proxy, value) \
1139 BITPACK_OPERATE_QUALIFY(proxy, .with, value)
1140
1141/**
1142 * A specialization of BITPACK_OPERATE_QUALIFY_DECLTYPE using `.with` as the
1143 * operator
1144 */
1145#define BITPACK_WITH_QUALIFY_DECLTYPE(b, T, v) \
1146 BITPACK_OPERATE_QUALIFY_DECLTYPE(b, T, .with, v)
1147
1148/**
1149 * A specialization of BITPACK_OPERATE_QUALIFY_DEPENDENT using `.with` as the
1150 * operator
1151 */
1152#define BITPACK_WITH_QUALIFY_DEPENDENT(b, T, v) \
1153 BITPACK_OPERATE_QUALIFY_DEPENDENT(b, T, .with, v)
1154
1155/**
1156 * A specialization of BITPACK_OPERATE_QUALIFY_TYPE using `.with` as the
1157 * operator
1158 */
1159#define BITPACK_WITH_QUALIFY_TYPE(b, T, v) \
1160 BITPACK_OPERATE_QUALIFY_TYPE(b, T, .with, v)
1161
1162/// @}
1163
1164/**
1165 * \defgroup bitpacks_macros_proxyop_construct Type-constructing proxy operator
1166 * macros
1167 *
1168 * @{
1169 */
1170
1171/**
1172 * Operate between a `Field::Proxy` `proxy` and a given `value`, which will be
1173 * wrapped with `proxy`'s `Field::Type`'s constructor.
1174 *
1175 * This will not work on `volatile` bitpacks; use `.read` first.
1176 */
1177#define BITPACK_OPERATE_WRAP(proxy, operator, value) \
1178 ({ \
1179 using F = decltype(proxy)::Field::Type; \
1180 (proxy) operator(F{value}); \
1181 })
1182
1183/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_WRAP
1184#define BITPACK_OPERATE_WRAP_DECLTYPE(b, T, operator, v) \
1185 BITPACK_OPERATE_WRAP(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1186
1187/// A fusion of BITPACK_MEMBER_DEPENDENT and BITPACK_OPERATE_WRAP
1188#define BITPACK_OPERATE_WRAP_DEPENDENT(b, T, operator, v) \
1189 BITPACK_OPERATE_WRAP(BITPACK_MEMBER_DEPENDENT(b, T), operator, v)
1190
1191/// A fusion of .member<>() and BITPACK_OPERATE_WRAP
1192#define BITPACK_OPERATE_WRAP_TYPE(b, T, operator, v) \
1193 BITPACK_OPERATE_WRAP((b).template member<T>(), operator, v)
1194
1195/// A specialization of BITPACK_OPERATE_WRAP using `.with` as the operator
1196#define BITPACK_WRAP_WITH(proxy, value) \
1197 BITPACK_OPERATE_WRAP(proxy, .with, value)
1198
1199/**
1200 * A specialization of BITPACK_OPERATE_WRAP_DECLTYPE using `.with` as the
1201 * operator.
1202 */
1203#define BITPACK_WITH_WRAP_DECLTYPE(b, T, v) \
1204 BITPACK_OPERATE_WRAP_DECLTYPE(b, T, .with, v)
1205
1206/**
1207 * A specialization of BITPACK_OPERATE_WRAP_DEPENDENT using `.with` as the
1208 * operator
1209 */
1210#define BITPACK_WITH_WRAP_DEPENDENT(b, T, v) \
1211 BITPACK_OPERATE_WRAP_DEPENDENT(b, T, .with, v)
1212
1213/// A specialization of BITPACK_OPERATE_WRAP_TYPE using `.with` as the operator
1214#define BITPACK_WITH_WRAP_TYPE(b, T, v) \
1215 BITPACK_OPERATE_WRAP_TYPE(b, T, .with, v)
1216
1217/// @}
1218
1219/**
1220 * \defgroup bitpacks_macros_proxyop_directed Type-directed proxy operator
1221 * macros
1222 *
1223 * @{
1224 */
1225
1226/**
1227 * Given a bitpack `b` -- not a Proxy of a Field therein -- and a value, operate
1228 * between the bitpack's view of that value's type and the value.
1229 */
1230#define BITPACK_OPERATE_VALUE(b, operator, value) \
1231 ({ (b).template member<decltype(value)>() operator(value); })
1232
1233/**
1234 * Given a bitpack `b` -- not a Proxy of a Field therein -- qualify the given
1235 * value with the bitpack's type and then operate between the bitpack's Proxy of
1236 * that qualified value's type and said value.
1237 */
1238#define BITPACK_OPERATE_VALUE_DECLTYPE(b, operator, value) \
1239 BITPACK_OPERATE_VALUE( \
1240 b, operator, std::remove_reference_t<decltype(b)>::value)
1241
1242/**
1243 * BITPACK_OPERATE_VALUE with dependent qualification for the type of the
1244 * bitpack.
1245 */
1246#define BITPACK_OPERATE_VALUE_DEPENDENT(b, operator, value) \
1247 BITPACK_OPERATE_VALUE( \
1248 b, operator, typename std::remove_reference_t<decltype(b)>::value)
1249
1250/// A specialization of BITPACK_OPERATE_VALUE using `.with` as the operator
1251#define BITPACK_WITH_VALUE(b, value) BITPACK_OPERATE_VALUE(b, .with, value)
1252
1253/**
1254 * A specialization of BITPACK_OPERATE_VALUE_DECLTYPE using `.with` as the
1255 * operator
1256 */
1257#define BITPACK_WITH_VALUE_DECLTYPE(b, value) \
1258 BITPACK_OPERATE_VALUE_DECLTYPE(b, .with, value)
1259
1260/**
1261 * A specialization of BITPACK_OPERATE_VALUE_DEPENDENT using `.with` as the
1262 * operator.
1263 */
1264#define BITPACK_WITH_VALUE_DEPENDENT(b, value) \
1265 BITPACK_OPERATE_VALUE_DEPENDENT(b, .with, value)
1266
1267/// @}
1268
1269/**
1270 * \defgroup bitpacks_macros_proxyop_enum "using enum" proxy operator macros
1271 *
1272 * @{
1273 */
1274
1275/**
1276 * Convenience for scoped enum fields, bringing the enumerators of the field
1277 * type into scope while evaluating the value.
1278 *
1279 * Note that this uses `using enum` internally, and so cannot work with
1280 * `Proxy`-s whose Field::Type-s are dependent. Alas, this precludes the
1281 * existence of a `BITPACK_*_ENUM_DEPENDENT` family of helpers.
1282 *
1283 * Probably prefer BITPACK_OPERATE_QUALIFY if you do not need repeated use of
1284 * the enumeration's values within the passed `value`.
1285 */
1286#define BITPACK_OPERATE_ENUM(proxy, operator, value) \
1287 ({ \
1288 using E = decltype(proxy)::Field::Type; \
1289 static_assert(std::is_enum_v<E>); \
1290 using enum E; \
1291 (proxy) operator(value); \
1292 })
1293
1294/// A fusion of BITPACK_MEMBER_DECLTYPE and BITPACK_OPERATE_ENUM
1295#define BITPACK_OPERATE_ENUM_DECLTYPE(b, T, operator, v) \
1296 BITPACK_OPERATE_ENUM(BITPACK_MEMBER_DECLTYPE(b, T), operator, v)
1297
1298/// A fusion of .member<>() and BITPACK_OPERATE_ENUM
1299#define BITPACK_OPERATE_ENUM_TYPE(b, T, operator, v) \
1300 BITPACK_OPERATE_ENUM((b).template member<T>(), operator, v)
1301
1302/// A specialization of BITPACK_OPERATE_ENUM using `.with` as the operator
1303#define BITPACK_WITH_ENUM(proxy, value) \
1304 BITPACK_OPERATE_ENUM(proxy, .with, value)
1305
1306/**
1307 * A specialization of BITPACK_OPERATE_ENUM_DECLTYPE using `.with` as the
1308 * operator.
1309 */
1310#define BITPACK_WITH_ENUM_DECLTYPE(b, T, v) \
1311 BITPACK_OPERATE_ENUM_DECLTYPE(b, T, .with, v)
1312
1313/// A specialization of BITPACK_OPERATE_ENUM_TYPE using `.with` as the operator
1314#define BITPACK_WITH_ENUM_TYPE(b, T, v) \
1315 BITPACK_OPERATE_ENUM_TYPE(b, T, .with, v)
1316
1317/// @}
1318
1319/**
1320 * \defgroup bitpacks_macros_vararg Multi-field utility macros
1321 *
1322 * @{
1323 */
1324
1325/**
1326 * \defgroup bitpacks_macros_vararg_internal Internal helpers
1327 *
1328 * @{
1329 */
1330
1331/// Map function for BITPACK_MAP_DECLTYPE
1332#define BITPACK_MAP_DECLTYPE_HELPER(x, b) \
1333 std::remove_reference_t<decltype(b)>::x
1334
1335/**
1336 * Given bitpack `b`, qualify each additional argument with `decltype(b)`. That
1337 * is, `BITPACK_MAP_DECLTYPE(b, X, Y)` evalutes to
1338 * `decltype(b)::X, decltype(b)::Y`.
1339 */
1340#define BITPACK_MAP_DECLTYPE(b, ...) \
1341 CHERIOT_MAP_LIST_UD(BITPACK_MAP_DECLTYPE_HELPER, b, __VA_ARGS__)
1342
1343/// Map function for BITPACK_MAP_DEPENDENT
1344#define BITPACK_MAP_DEPENDENT_HELPER(x, b) \
1345 typename std::remove_reference_t<decltype(b)>::x
1346
1347/**
1348 * Given bitpack `b`, qualify each additional argument with `typename
1349 * decltype(b)`. That is, `BITPACK_DEPENDENT(b, X, Y)` evalutes to
1350 * `typename decltype(b)::X, typename decltype(b)::Y`.
1351 */
1352#define BITPACK_MAP_DEPENDENT(b, ...) \
1353 CHERIOT_MAP_LIST_UD(BITPACK_MAP_DEPENDENT_HELPER, b, __VA_ARGS__)
1354
1355/// Map function for BITPACK_WITHS
1356#define BITPACK_MAP_WITHS_HELPER(x) .with(x)
1357
1358/// @}
1359
1360/**
1361 * Construct a chain of .with() whose arguments are all qualified with the type
1362 * of the bitpack.
1363 */
1364#define BITPACK_WITHS(b, ...) \
1365 (b) CHERIOT_MAP(BITPACK_MAP_WITHS_HELPER, __VA_ARGS__)
1366
1367/**
1368 * A version of BITPACK_WITHS where the arguments to `.with()` are qualified
1369 * with the type of the bitpack.
1370 */
1371#define BITPACK_WITHS_DECLTYPE(b, ...) \
1372 BITPACK_WITHS(b, BITPACK_MAP_DECLTYPE(b, __VA_ARGS__))
1373
1374/**
1375 * A version of BITPACK_WITHS where the arguments to `.with()` are dependently
1376 * qualified with the type of the bitpack.
1377 */
1378#define BITPACK_WITHS_DEPENDENT(b, ...) \
1379 BITPACK_WITHS(b, BITPACK_MAP_DEPENDENT(b, __VA_ARGS__))
1380
1381/**
1382 * @addtogroup macros_vararg_internal
1383 * @{
1384 */
1385
1386/// Helper for BITPACK_RELATE_MASKED, for computing individual field's masks
1387#define BITPACK_RELATE_MASKED_HELPER(x, b) \
1388 | ({ \
1389 using BT = decltype(b); \
1390 using FT = decltype(x); \
1391 BT::Field<FT, BT::field_info_for_type<FT>()>::FieldMask; \
1392 })
1393
1394/// @}
1395
1396/**
1397 * Given a list of field values (which must be fully qualified names), compute
1398 * the mask of these fields and the bitpack value holding these field values,
1399 * then mask `b` with the computed mask, and then use `operator` to relate the
1400 * result with the computed bitpack value. The last value for each field is
1401 * used.
1402 */
1403#define BITPACK_RELATE_MASKED(b, operator, ...) \
1404 ({ \
1405 constexpr decltype(b.raw()) __bitpack_mask = \
1406 (0)CHERIOT_MAP_UD(BITPACK_RELATE_MASKED_HELPER, b, __VA_ARGS__); \
1407 constexpr auto __bitpack_query = \
1408 BITPACK_WITHS((decltype(b))(0), __VA_ARGS__).raw(); \
1409 (b.raw() & __bitpack_mask) operator(__bitpack_query); \
1410 })
1411
1412/**
1413 * A version of BITPACK_RELATE_MASKED where the field values are qualified with
1414 * the type of the bitpack.
1415 */
1416#define BITPACK_RELATE_MASKED_DECLTYPE(b, operator, ...) \
1417 BITPACK_RELATE_MASKED(b, operator, BITPACK_MAP_DECLTYPE(b, __VA_ARGS__))
1418
1419/**
1420 * A version of BITPACK_RELATE_MASKED where the field values are dependently
1421 * qualified with the type of the bitpack.
1422 */
1423#define BITPACK_RELATE_MASKED_DEPENDENT(b, operator, ...) \
1424 BITPACK_RELATE_MASKED(b, operator, BITPACK_MAP_DEPENDENT(b, __VA_ARGS__))
1425
1426/// @}
1427/// @}
1428/// @}
A proxy for this Field within the Bitpack's Storage.
Definition bitpack.hh:570
constexpr auto rawer() const
Get the value of this Numeric-typed field as its underlying type.
Definition bitpack.hh:675
constexpr DerivedBitpack with(Field::Type rhs) const
Construct a new Bitpack value with an updated value of this field.
Definition bitpack.hh:692
constexpr Field::Type raw() const
A shorter way of spelling static_cast<Field::Type>()...
Definition bitpack.hh:660
constexpr Proxy(R &r)
Construct a proxy given a reference to the storage word.
Definition bitpack.hh:588
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:705
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:724
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:602
constexpr auto rawer() const
Get the value of this enum-typed field as its underlying type.
Definition bitpack.hh:666
constexpr operator Field::Type(this Self &&self)
Explicit conversion of a Field::Proxy to the field value.
Definition bitpack.hh:654
RefTypeParam RefType
Expose the qualified reference type we hold to the Storage.
Definition bitpack.hh:579
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:744
Field Field
Name the Field of which we are a proxy.
Definition bitpack.hh:576
constexpr void set(this Self &&self, FieldType v)
Compute a new Bitpack value with an updated field of a given type.
Definition bitpack.hh:868
constexpr void assign_from(this Self &&self, auto &&f)
Convenience function for unconditionally assigning an entire Bitpack from a computed value.
Definition bitpack.hh:895
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:383
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:857
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:356
constexpr Storage raw() const
A shorter way of spelling static_cast<Storage>(...).
Definition bitpack.hh:404
constexpr void alter(this Self &&self, auto &&f)
Convenience function for unconditionally changing several sub-fields at once.
Definition bitpack.hh:880
constexpr Bitpack()
Construct a Bitpack value with an underlying Storage of all zero bits.
Definition bitpack.hh:330
constexpr Bitpack(Storage v)
Construct a Bitpack value from a value of its underlying Storage type.
Definition bitpack.hh:339
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:828
const auto read(this Self &&self)
Return a snapshot of the underlying Storage.
Definition bitpack.hh:416
constexpr FieldType get(this Self &&self)
Fetch the value of a field in this Bitpack based on the field type.
Definition bitpack.hh:846
constexpr auto member(this Self &&self)
Build a Field::Proxy of an explicitly given type and info.
Definition bitpack.hh:812
StorageParam Storage
Expose the underlying storage type.
Definition bitpack.hh:323
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:914
Information about a field within a Bitpack.
Definition bitpack.hh:463
size_t maxIndex
Maximum 0-indexed bit position occupied by this field.
Definition bitpack.hh:467
bool isConst
Should this field be proxied as constant (and so mutation be slightly less ergonomic)?
Definition bitpack.hh:472
size_t minIndex
Minimum 0-indexed bit position occupied by this field.
Definition bitpack.hh:465
A particular Field within a Bitpack.
Definition bitpack.hh:485
static constexpr Storage raw_with(Storage lhs, Storage rhs)
Compute the underlying Storage transform for a Field update.
Definition bitpack.hh:529
static constexpr Storage FieldMask
The mask of bits occupied by this value (occupied bits are set).
Definition bitpack.hh:520
static constexpr Storage raw_with(Storage lhs, Bitpack< OtherStorage > rhs)
Update for fields that are themselves Bitpacks at smaller types.
Definition bitpack.hh:544
TypeParam Type
Expose the type parameter.
Definition bitpack.hh:487
static constexpr Storage ValueMask
A span of set bits, starting at index 0, and of the field's width.
Definition bitpack.hh:516
static constexpr Storage raw_with(Storage lhs, Type rhs)
Update for fields whose type can be static_cast to Storage.
Definition bitpack.hh:535
static constexpr Type raw_view(Storage storage)
Extract a Field from a raw Storage value.
Definition bitpack.hh:523
static constexpr FieldInfo Info
Expose the FieldInfo parameter.
Definition bitpack.hh:489
Convenience wrapper for the types of numeric fields.
Definition bitpack.hh:425
T NumericType
Export the underlying numeric type.
Definition bitpack.hh:427