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 3 : explicit AttributeSet(uint32_t initialValue) : mSetBits(initialValue) {}
61 :
62 18 : AttributeSet() = default;
63 : AttributeSet(const AttributeSet & other) = default;
64 : AttributeSet(AttributeSet && other) = default;
65 : AttributeSet & operator=(const AttributeSet & other) = default;
66 : 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 191 : 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 9 : constexpr AttributeSet & ForceSet()
83 : {
84 : static_assert(id < 32, "Attribute ID must be settable");
85 9 : return Set(id, true);
86 : }
87 :
88 : protected:
89 37 : constexpr AttributeSet & Set(AttributeId id, bool value = true)
90 : {
91 37 : if (value)
92 : {
93 37 : mSetBits |= static_cast<uint32_t>((static_cast<uint32_t>(1) << id));
94 : }
95 : else
96 : {
97 0 : mSetBits &= ~static_cast<uint32_t>((static_cast<uint32_t>(1) << id));
98 : }
99 37 : return *this;
100 : }
101 :
102 : private:
103 : uint32_t mSetBits = 0;
104 : };
105 :
106 : /// A specialization of AttributeSet that provides checked calls to `Set`.
107 : ///
108 : /// Specifically it requires that attributes are declared as part of the template
109 : /// parameter pack.
110 : ///
111 : /// NOTE: this will NOT work for all possible attributes/clusters, only for clusters
112 : /// whose optional attributes all have IDs under 32. Static asserts are in place
113 : /// to ensure that arguments to the template are valid.
114 : ///
115 : /// Example usage:
116 : ///
117 : /// namespace chip::app::Clusters::GeneralDiagnostics {
118 : ///
119 : /// using OptionalAttributeSet = chip::app::OptionalAttributeSet<
120 : /// Attributes::TotalOperationalHours::Id,
121 : /// Attributes::BootReason::Id,
122 : /// Attributes::ActiveHardwareFaults::Id
123 : /// >;
124 : ///
125 : /// } // namespace chip::app::Clusters::GeneralDiagnostics
126 : ///
127 : /// After this, one can:
128 : ///
129 : /// GeneralDiagnostics::OptionalAttributeSet()
130 : /// .Set<GeneralDiagnostics::Attributes::TotalOperationalHours::Id>()
131 : /// .Set<GeneralDiagnostics::Attributes::BootReason::Id>();
132 : ///
133 : /// Cluster implementations can then store a
134 : /// Constructor(const GeneralDiagnostics::OptionalAttributeSet& optionalAttributeSet) :
135 : /// mOptionalAttributeSet(optionalAttributeSet) {...}
136 : ///
137 : /// where:
138 : /// const AttributeSet mOptionalAttributeSet;
139 : template <AttributeId... OptionalAttributeIds>
140 : class OptionalAttributeSet : public AttributeSet
141 : {
142 : public:
143 3 : explicit OptionalAttributeSet(uint32_t initialValue) : AttributeSet(initialValue & All()) {}
144 7 : OptionalAttributeSet(const AttributeSet & initialValue) : AttributeSet(initialValue) {}
145 13 : OptionalAttributeSet() = default;
146 :
147 : template <uint32_t ATTRIBUTE_ID>
148 28 : constexpr OptionalAttributeSet & Set(bool value = true)
149 : {
150 : static_assert(ATTRIBUTE_ID < 32, "Cluster attribute bits supports attributes up to 31");
151 : static_assert(Internal::IsOneOf<ATTRIBUTE_ID, OptionalAttributeIds...>::value, "attribute MUST be optional");
152 28 : (void) AttributeSet::Set(ATTRIBUTE_ID, value);
153 28 : return *this;
154 : }
155 :
156 3 : static constexpr uint32_t All()
157 : {
158 : if constexpr (sizeof...(OptionalAttributeIds) == 0)
159 : {
160 1 : return 0;
161 : }
162 : else
163 : {
164 2 : return ((1U << OptionalAttributeIds) | ...);
165 : }
166 : }
167 : };
168 :
169 : } // namespace app
170 : } // namespace chip
|