Skip to content

Commit

Permalink
Stop importing operator* and operator-> into Nullable. (project-chip#…
Browse files Browse the repository at this point in the history
…34353)

These are serious footguns because:

1.  They don't do checks that there is actually a value, and will silently
    produce garbage.
2.  They are really easy to accidentally use without a IsNull() check
    (especially operator->).

I've now reviewed several PRs where people were misusing these and helped a few
other people who were misusing them and then not getting expected behavior, so
it would be better if we just didn't have these at all.  Nullable is not a
pointer and people shouldn't treat it like one.
  • Loading branch information
bzbarsky-apple authored Jul 16, 2024
1 parent 1da085d commit bcb4179
Show file tree
Hide file tree
Showing 4 changed files with 13 additions and 10 deletions.
6 changes: 3 additions & 3 deletions src/app/cluster-building-blocks/QuieterReporting.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,9 @@ class QuieterReportingAttribute
bool isChangeOfNull = newValue.IsNull() ^ mValue.IsNull();
bool areBothValuesNonNull = !newValue.IsNull() && !mValue.IsNull();

bool changeToFromZero = areBothValuesNonNull && (*newValue == 0 || *mValue == 0);
bool isIncrement = areBothValuesNonNull && (*newValue > *mValue);
bool isDecrement = areBothValuesNonNull && (*newValue < *mValue);
bool changeToFromZero = areBothValuesNonNull && (newValue.Value() == 0 || mValue.Value() == 0);
bool isIncrement = areBothValuesNonNull && (newValue.Value() > mValue.Value());
bool isDecrement = areBothValuesNonNull && (newValue.Value() < mValue.Value());

bool isNewlyDirty = isChangeOfNull;
isNewlyDirty =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ TEST(TestQuieterReporting, ChangeOnIncrementPolicyWorks)
QuieterReportingAttribute<int> attribute{ MakeNullable<int>(10) };

// Always start not dirty (because first sub priming always just read value anyway).
ASSERT_EQ(*attribute.value(), 10);
ASSERT_EQ(attribute.value().Value(), 10);

auto now = fakeClock.now();

Expand Down Expand Up @@ -149,7 +149,7 @@ TEST(TestQuieterReporting, ChangeOnDecrementPolicyWorks)
QuieterReportingAttribute<int> attribute{ MakeNullable<int>(9) };

// Always start not dirty (because first sub priming always just read value anyway).
ASSERT_EQ(*attribute.value(), 9);
ASSERT_EQ(attribute.value().Value(), 9);

auto now = fakeClock.now();

Expand Down Expand Up @@ -202,7 +202,7 @@ TEST(TestQuieterReporting, SufficientChangePredicateWorks)
QuieterReportingAttribute<int> attribute{ MakeNullable<int>(9) };

// Always start not dirty (because first sub priming always just read value anyway).
ASSERT_EQ(*attribute.value(), 9);
ASSERT_EQ(attribute.value().Value(), 9);

auto now = fakeClock.now();

Expand Down
4 changes: 2 additions & 2 deletions src/app/codegen-data-model/CodegenDataModel_Write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ CHIP_ERROR DecodeIntoEmberBuffer(AttributeValueDecoder & decoder, bool isNullabl
// Nullable<uint8_t>(0xFF) is not representable because 0xFF is the encoding of NULL in ember
// as well as odd-sized integers (e.g. full 32-bit value like 0x11223344 cannot be written
// to a 3-byte odd-sized integger).
VerifyOrReturnError(Traits::CanRepresentValue(isNullable, *workingValue), CHIP_ERROR_INVALID_ARGUMENT);
Traits::WorkingToStorage(*workingValue, storageValue);
VerifyOrReturnError(Traits::CanRepresentValue(isNullable, workingValue.Value()), CHIP_ERROR_INVALID_ARGUMENT);
Traits::WorkingToStorage(workingValue.Value(), storageValue);
}

VerifyOrReturnError(out.size() >= sizeof(storageValue), CHIP_ERROR_INVALID_ARGUMENT);
Expand Down
7 changes: 5 additions & 2 deletions src/app/data-model/Nullable.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ struct Nullable : protected std::optional<T>
// all constructors of the base class within this derived class.
//
using std::optional<T>::optional;
using std::optional<T>::operator*;
using std::optional<T>::operator->;

// 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<T>(std::nullopt) {}

Expand Down

0 comments on commit bcb4179

Please sign in to comment.