Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #pragma once
20 :
21 : #include <app/util/attribute-storage-null-handling.h>
22 : #include <optional>
23 : #include <type_traits>
24 : #include <utility>
25 :
26 : namespace chip {
27 : namespace app {
28 : namespace DataModel {
29 :
30 : /**
31 : * NullNullable is an alias for NullOptional, for better readability.
32 : */
33 : inline constexpr auto NullNullable = NullOptional;
34 :
35 : /*
36 : * Dedicated type for nullable things, to differentiate them from optional
37 : * things.
38 : */
39 : template <typename T>
40 : struct Nullable : protected std::optional<T>
41 : {
42 :
43 : //
44 : // The following 'using' statement is needed to make visible
45 : // all constructors of the base class within this derived class.
46 : //
47 : using std::optional<T>::optional;
48 :
49 : // Do NOT pull in optional::operator* or optional::operator->, because that
50 : // leads people to write code that looks like it should work, and compiles,
51 : // but does not do the right things with TLV encoding and decoding, when
52 : // nullable data model objects are involved.
53 :
54 5542 : Nullable(NullOptionalType) : std::optional<T>(std::nullopt) {}
55 :
56 : // Some consumers need an easy way to determine our underlying type.
57 : using UnderlyingType = T;
58 :
59 121 : constexpr void SetNull() { std::optional<T>::reset(); }
60 326 : constexpr bool IsNull() const { return !std::optional<T>::has_value(); }
61 :
62 : template <class... Args>
63 0 : constexpr T & SetNonNull(Args &&... args)
64 : {
65 0 : return std::optional<T>::emplace(std::forward<Args>(args)...);
66 : }
67 :
68 : template <typename... Args>
69 : constexpr auto ValueOr(Args &&... args) const
70 : {
71 : return std::optional<T>::value_or(std::forward<Args>(args)...);
72 : }
73 :
74 31 : inline constexpr const T & Value() const { return std::optional<T>::value(); }
75 0 : inline T & Value() { return std::optional<T>::value(); }
76 :
77 : // For integer types, being nullable involves a range restriction.
78 : template <
79 : typename U = std::decay_t<T>,
80 : typename std::enable_if_t<(std::is_integral<U>::value && !std::is_same<U, bool>::value) || std::is_enum<U>::value, int> = 0>
81 : constexpr bool ExistingValueInEncodableRange() const
82 : {
83 : return NumericAttributeTraits<T>::CanRepresentValue(/* isNullable = */ true, Value());
84 : }
85 :
86 : // For all other types, all values are valid.
87 : template <typename U = std::decay_t<T>,
88 : typename std::enable_if_t<(!std::is_integral<U>::value || std::is_same<U, bool>::value) && !std::is_enum<U>::value,
89 : int> = 0>
90 0 : constexpr bool ExistingValueInEncodableRange() const
91 : {
92 0 : return true;
93 : }
94 :
95 : // Set the nullable to the `other` nullable, returning true if something actually changed.
96 : // This can be used to determine if changes occurred on assignment, so that reporting can be triggered
97 : // only on actual changes.
98 : constexpr bool Update(const Nullable<T> & other)
99 : {
100 : bool changed = *this != other;
101 : if (changed)
102 : {
103 : *this = other;
104 : }
105 : return changed;
106 : }
107 :
108 : // The only fabric-scoped objects in the spec are commands, events and structs inside lists, and none of those can be nullable.
109 : static constexpr bool kIsFabricScoped = false;
110 :
111 : inline bool operator==(const T & other) const { return static_cast<const std::optional<T> &>(*this) == other; }
112 : inline bool operator!=(const T & other) const { return !(*this == other); }
113 :
114 19 : inline bool operator==(const Nullable<T> & other) const
115 : {
116 19 : return static_cast<const std::optional<T> &>(*this) == static_cast<const std::optional<T> &>(other);
117 : }
118 14 : inline bool operator!=(const Nullable<T> & other) const { return !(*this == other); }
119 : };
120 :
121 : template <class T>
122 4 : constexpr Nullable<std::decay_t<T>> MakeNullable(T && value)
123 : {
124 4 : return Nullable<std::decay_t<T>>(std::in_place, std::forward<T>(value));
125 : }
126 :
127 : template <class T, class... Args>
128 : constexpr Nullable<T> MakeNullable(Args &&... args)
129 : {
130 : return Nullable<T>(std::in_place, std::forward<Args>(args)...);
131 : }
132 :
133 : } // namespace DataModel
134 : } // namespace app
135 : } // namespace chip
|