Line data Source code
1 : /*
2 : * Copyright (c) 2025 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 : #pragma once
18 :
19 : #include <cstdint>
20 : #include <lib/core/DataModelTypes.h>
21 :
22 : namespace chip {
23 : namespace app {
24 : namespace Internal {
25 :
26 : // Compile-time helper to check if an ID is in a list of IDs.
27 : template <AttributeId T, AttributeId... Ts>
28 : struct IsOneOf;
29 :
30 : template <AttributeId T, AttributeId Head, AttributeId... Tail>
31 : struct IsOneOf<T, Head, Tail...>
32 : {
33 : static constexpr bool value = (T == Head) || IsOneOf<T, Tail...>::value;
34 : };
35 :
36 : template <AttributeId T>
37 : struct IsOneOf<T>
38 : {
39 : static constexpr bool value = false;
40 : };
41 :
42 : } // namespace Internal
43 :
44 : /// It is very common that a class has optional attributes. Such optional attributes
45 : /// need checking for and also affect what attributes are being returned by
46 : /// server cluster implementations
47 : ///
48 : /// This class implements the common case where attribute IDs can fit
49 : /// within a 32-bit bitset as most attributes start with low IDs and are incremented
50 : /// by one.
51 : ///
52 : /// NOTE: this will NOT work for all possible attributes/clusters, only for clusters
53 : /// whose optionnal attributes all have IDs under 32.
54 : ///
55 : /// The implementation of the class generally is a wrapper over a bitset with a
56 : /// `IsSet()` method. Configurations should use the OptionalAttributeSet<...> class.
57 : class AttributeSet
58 : {
59 : public:
60 4 : constexpr explicit AttributeSet(uint32_t initialValue) : mSetBits(initialValue) {}
61 :
62 298 : constexpr AttributeSet() = default;
63 : constexpr AttributeSet(const AttributeSet & other) = default;
64 : constexpr AttributeSet(AttributeSet && other) = default;
65 : constexpr AttributeSet & operator=(const AttributeSet & other) = default;
66 : constexpr AttributeSet & operator=(AttributeSet && other) = default;
67 :
68 : // Checks if an attribute ID is set.
69 : //
70 : // NOTE: this does NOT validate that the ID is < 32 because all the Set functions
71 : // generally are asserted on this (forceset as well as subclasses) and the
72 : // initial value contructor uses a uint32_t bitmask as well.
73 : //
74 : // This MUST be called with id < 32.
75 5262 : constexpr bool IsSet(AttributeId id) const { return (mSetBits & (1u << id)) != 0; }
76 :
77 : /// Exposes a "force attribute bit set" without extra validation,
78 : /// so that clusters can enforce specific bits to be set.
79 : ///
80 : /// This is NOT intended as a generic set, use `OptionalAttributeSet` to configure values.
81 : template <AttributeId id>
82 224 : constexpr AttributeSet & ForceSet()
83 : {
84 : static_assert(id < 32, "Attribute ID must be settable");
85 224 : return Set(id, true);
86 : }
87 :
88 : constexpr uint32_t Raw() const { return mSetBits; }
89 :
90 : protected:
91 945 : constexpr AttributeSet & Set(AttributeId id, bool value = true)
92 : {
93 945 : if (value)
94 : {
95 703 : mSetBits |= static_cast<uint32_t>((static_cast<uint32_t>(1) << id));
96 : }
97 : else
98 : {
99 242 : mSetBits &= ~static_cast<uint32_t>((static_cast<uint32_t>(1) << id));
100 : }
101 945 : return *this;
102 : }
103 :
104 : private:
105 : uint32_t mSetBits = 0;
106 : };
107 :
108 : /// A specialization of AttributeSet that provides checked calls to `Set`.
109 : ///
110 : /// Specifically it requires that attributes are declared as part of the template
111 : /// parameter pack.
112 : ///
113 : /// NOTE: this will NOT work for all possible attributes/clusters, only for clusters
114 : /// whose optional attributes all have IDs under 32. Static asserts are in place
115 : /// to ensure that arguments to the template are valid.
116 : ///
117 : /// Example usage:
118 : ///
119 : /// namespace chip::app::Clusters::GeneralDiagnostics {
120 : ///
121 : /// using OptionalAttributeSet = chip::app::OptionalAttributeSet<
122 : /// Attributes::TotalOperationalHours::Id,
123 : /// Attributes::BootReason::Id,
124 : /// Attributes::ActiveHardwareFaults::Id
125 : /// >;
126 : ///
127 : /// } // namespace chip::app::Clusters::GeneralDiagnostics
128 : ///
129 : /// After this, one can:
130 : ///
131 : /// GeneralDiagnostics::OptionalAttributeSet()
132 : /// .Set<GeneralDiagnostics::Attributes::TotalOperationalHours::Id>()
133 : /// .Set<GeneralDiagnostics::Attributes::BootReason::Id>();
134 : ///
135 : /// Cluster implementations can then store a
136 : /// Constructor(const GeneralDiagnostics::OptionalAttributeSet& optionalAttributeSet) :
137 : /// mOptionalAttributeSet(optionalAttributeSet) {...}
138 : ///
139 : /// where:
140 : /// const AttributeSet mOptionalAttributeSet;
141 : template <AttributeId... OptionalAttributeIds>
142 : class OptionalAttributeSet : public AttributeSet
143 : {
144 : public:
145 4 : constexpr explicit OptionalAttributeSet(uint32_t initialValue) : AttributeSet(initialValue & All()) {}
146 116 : constexpr OptionalAttributeSet(const AttributeSet & initialValue) : AttributeSet(initialValue) {}
147 276 : constexpr OptionalAttributeSet() = default;
148 :
149 : template <uint32_t ATTRIBUTE_ID>
150 721 : constexpr OptionalAttributeSet & Set(bool value = true)
151 : {
152 : static_assert(ATTRIBUTE_ID < 32, "Cluster attribute bits supports attributes up to 31");
153 : static_assert(Internal::IsOneOf<ATTRIBUTE_ID, OptionalAttributeIds...>::value, "attribute MUST be optional");
154 721 : (void) AttributeSet::Set(ATTRIBUTE_ID, value);
155 721 : return *this;
156 : }
157 :
158 4 : static constexpr uint32_t All()
159 : {
160 : if constexpr (sizeof...(OptionalAttributeIds) == 0)
161 : {
162 1 : return 0;
163 : }
164 : else
165 : {
166 3 : return ((1U << OptionalAttributeIds) | ...);
167 : }
168 : }
169 : };
170 :
171 : } // namespace app
172 : } // namespace chip
|