/* * * Copyright (c) 2020-2021 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include namespace chip { namespace app { namespace DataModel { /** * NullNullable is an alias for NullOptional, for better readability. */ inline constexpr auto NullNullable = NullOptional; /* * Dedicated type for nullable things, to differentiate them from optional * things. */ template struct Nullable : protected std::optional { // // The following 'using' statement is needed to make visible // all constructors of the base class within this derived class. // using std::optional::optional; // Do NOT pull in optional::operator* or optional::operator->, because that // leads people to write code that looks like it should work, and compiles, // but does not do the right things with TLV encoding and decoding, when // nullable data model objects are involved. Nullable(NullOptionalType) : std::optional(std::nullopt) {} // Some consumers need an easy way to determine our underlying type. using UnderlyingType = T; constexpr void SetNull() { std::optional::reset(); } constexpr bool IsNull() const { return !std::optional::has_value(); } template constexpr T & SetNonNull(Args &&... args) { return std::optional::emplace(std::forward(args)...); } template constexpr auto ValueOr(Args &&... args) const { return std::optional::value_or(std::forward(args)...); } inline constexpr const T & Value() const { return std::optional::value(); } inline T & Value() { return std::optional::value(); } // For integer types, being nullable involves a range restriction. template < typename U = std::decay_t, typename std::enable_if_t<(std::is_integral::value && !std::is_same::value) || std::is_enum::value, int> = 0> constexpr bool ExistingValueInEncodableRange() const { return NumericAttributeTraits::CanRepresentValue(/* isNullable = */ true, Value()); } // For all other types, all values are valid. template , typename std::enable_if_t<(!std::is_integral::value || std::is_same::value) && !std::is_enum::value, int> = 0> constexpr bool ExistingValueInEncodableRange() const { return true; } // Set the nullable to the `other` nullable, returning true if something actually changed. // This can be used to determine if changes occurred on assignment, so that reporting can be triggered // only on actual changes. constexpr bool Update(const Nullable & other) { bool changed = *this != other; if (changed) { *this = other; } return changed; } // The only fabric-scoped objects in the spec are commands, events and structs inside lists, and none of those can be nullable. static constexpr bool kIsFabricScoped = false; inline bool operator==(const T & other) const { return static_cast &>(*this) == other; } inline bool operator!=(const T & other) const { return !(*this == other); } inline bool operator==(const Nullable & other) const { return static_cast &>(*this) == static_cast &>(other); } inline bool operator!=(const Nullable & other) const { return !(*this == other); } }; template constexpr Nullable> MakeNullable(T && value) { return Nullable>(std::in_place, std::forward(value)); } template constexpr Nullable MakeNullable(Args &&... args) { return Nullable(std::in_place, std::forward(args)...); } } // namespace DataModel } // namespace app } // namespace chip