|
21 | 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | 22 | * SOFTWARE. |
23 | 23 | */ |
24 | | -// HAPI 0.7.0, single header: include/hapi/*.h inlined by scripts/amalgamate.py. Do not edit; |
| 24 | +// HAPI 0.8.0, single header: include/hapi/*.h inlined by scripts/amalgamate.py. Do not edit; |
25 | 25 | // regenerate with `python3 scripts/amalgamate.py` (tests/single_header/run.sh checks it is current). |
26 | | -// Headers, in order: hapi/hapi.h, hapi/rules.h, hapi/chain.h, hapi/meta.h, hapi/base.h, hapi/platform/avr/avr_std.h |
| 26 | +// Headers, in order: hapi/hapi.h, hapi/rules.h, hapi/chain.h, hapi/meta.h, hapi/base.h, hapi/platform/avr/avr_std.h, hapi/slots.h |
27 | 27 | #pragma once |
28 | 28 |
|
29 | 29 | // ---- begin hapi/hapi.h ---- |
@@ -1039,6 +1039,81 @@ namespace hapi { |
1039 | 1039 | }; |
1040 | 1040 | // ---- end hapi/rules.h ---- |
1041 | 1041 | // #include "hapi/meta.h" -- inlined above |
| 1042 | +// ---- begin hapi/slots.h ---- |
| 1043 | +/** |
| 1044 | + * @file slots.h |
| 1045 | + * @brief Slots: state composed along a chain and addressed by tag. |
| 1046 | + * |
| 1047 | + * hapi::Slot<Tag, S> is a component that adds one slot, a value of type S, to the state the components after it built. |
| 1048 | + * The state is one object of static size with no heap; a slot whose S is empty adds nothing (on MSVC through __declspec(empty_bases), which the |
| 1049 | + * header applies; HAPI_EMPTY_BASES is the macro). |
| 1050 | + * |
| 1051 | + * using State = hapi::APIOf<hapi::SlotApi, hapi::Slot<A, SlotA>, hapi::Slot<B, SlotB>>::Res; |
| 1052 | + * hapi::slot<A>(state).x = 1; // by tag; a tag that is not in the state is a compile error |
| 1053 | + * state.each(visitor); // visitor.layer(Tag::name()), then S::each(slot, visitor), for every slot, the last-listed first |
| 1054 | + * |
| 1055 | + * The same tag twice in one state is a compile error, also across nested chains and nested APIOf. |
| 1056 | + * |
| 1057 | + * What the slots mean, what their fields are called, how the state evolves or travels is not decided here. Slot's third parameter, |
| 1058 | + * a Contract, is how a user adds members to the state's type; R is the state's type and R::me() is the slot of this Part: |
| 1059 | + * |
| 1060 | + * template<class Below, class R> struct Counted : Below { unsigned steps = 0; void step() { ++steps; } }; |
| 1061 | + * hapi::Slot<A, SlotA, Counted> |
| 1062 | + * |
| 1063 | + * each() calls Tag::name() and S::each() only when it is used, and does not look at what Tag::name() returns. |
| 1064 | + */ |
| 1065 | +// #include "hapi/base.h" -- inlined above |
| 1066 | + |
| 1067 | +#ifndef HAPI_EMPTY_BASES |
| 1068 | + #if defined(_MSC_VER) && !defined(__clang__) |
| 1069 | + #define HAPI_EMPTY_BASES __declspec(empty_bases) // MSVC removes an empty base from one base only, unless asked |
| 1070 | + #else |
| 1071 | + #define HAPI_EMPTY_BASES |
| 1072 | + #endif |
| 1073 | +#endif |
| 1074 | +#ifndef HAPI_SLOT_EACH_INLINE |
| 1075 | + #define HAPI_SLOT_EACH_INLINE // e.g. [[gnu::always_inline]]: a size policy for walks over the state |
| 1076 | +#endif |
| 1077 | + |
| 1078 | +namespace hapi { |
| 1079 | + /// @brief the slot as a base class, addressed by its tag |
| 1080 | + template<class Tag, class S> struct SlotOf : S {}; |
| 1081 | + template<class Tag, class S> constexpr S& slot_(SlotOf<Tag,S>& f) { return f; } |
| 1082 | + template<class Tag, class S> constexpr const S& slot_(const SlotOf<Tag,S>& f) { return f; } |
| 1083 | + |
| 1084 | + template<class T> T& slot_decl(); |
| 1085 | + template<class Tag, class R, class = void> struct HasSlot : std::false_type {}; |
| 1086 | + template<class Tag, class R> struct HasSlot<Tag, R, std::void_t<decltype(slot_<Tag>(slot_decl<R>()))>> : std::true_type {}; |
| 1087 | + |
| 1088 | + /// @brief the slot of Tag in a state |
| 1089 | + template<class Tag, class R> constexpr decltype(auto) slot(R& r) { |
| 1090 | + static_assert(HasSlot<Tag,R>::value, "hapi::slot<Tag>: no slot with that tag in this state (a Part sees its own slot and the slots of the Parts after it)"); |
| 1091 | + return slot_<Tag>(r); |
| 1092 | + } |
| 1093 | + |
| 1094 | + /// @brief the end of the chain: no slot, nothing to walk. SlotApi is the terminal API of a state that needs nothing more. |
| 1095 | + struct SlotRoot { template<class V> constexpr void each(V&) const {} }; |
| 1096 | + struct SlotApi { using Res = SlotRoot; }; |
| 1097 | + |
| 1098 | + /// @brief what a state adds when no Contract asks for more: nothing |
| 1099 | + template<class Below, class R> struct SlotBase : Below {}; |
| 1100 | + |
| 1101 | + /// @brief a component that adds a slot. Contract<Below, R> is the base the state derives from, carrying the state below (Below) and the state's |
| 1102 | + /// own type (R): a Contract adds members to the state without adding an inheritance level, so the state keeps the same aggregate shape. |
| 1103 | + template<class Tag, class S, template<class, class> class Contract = SlotBase> |
| 1104 | + struct Slot { |
| 1105 | + template<class O> struct Part : O { |
| 1106 | + static_assert(!HasSlot<Tag, typename O::Res>::value, "hapi::Slot: two Parts claim the same tag"); |
| 1107 | + struct HAPI_EMPTY_BASES Res : Contract<typename O::Res, Res>, SlotOf<Tag,S> { |
| 1108 | + constexpr S& me() { return slot_<Tag>(*this); } |
| 1109 | + constexpr const S& me() const { return slot_<Tag>(*this); } |
| 1110 | + template<class V> HAPI_SLOT_EACH_INLINE constexpr void each(V& v) { O::Res::each(v); v.layer(Tag::name()); S::each(me(), v); } |
| 1111 | + template<class V> HAPI_SLOT_EACH_INLINE constexpr void each(V& v) const { O::Res::each(v); v.layer(Tag::name()); S::each(me(), v); } |
| 1112 | + }; |
| 1113 | + }; |
| 1114 | + }; |
| 1115 | +} |
| 1116 | +// ---- end hapi/slots.h ---- |
1042 | 1117 |
|
1043 | 1118 | namespace hapi { |
1044 | 1119 | // ====================== APIOf ======================-- |
|
0 commit comments