[common] enforce correct CRTP usage for mix-in classes (#11880)

The mix-in helper classes like `Clearable<T>`, `Equatable<T>`, and
`Unequatable<T>` are intended for CRTP style inheritance, where `T`
is the derived class itself. A mistaken inheritance, such as `class
Foo : public Clearable<Bar>`, can compile successfully but lead to
subtle bugs.

This change enforces the correct CRTP usage at compile time. By making
the constructors of these helper classes `private` and declaring the
derived template class `T` as a `friend`, any incorrect inheritance
will now result in a build failure. This approach correctly detects
such a mistake, even if `Foo` and `Bar` happen to be `friend`s of
each other.

Additionally, `Equatable<T>` is updated to provide both `operator==`
and `operator!=`, removing its dependency on `Unequatable<T>`. This
change allows us to apply the `private` constructor enforcement to
`Equatable<T>` as well.
This commit is contained in:
Abtin Keshavarzian
2025-08-29 12:58:45 -07:00
committed by GitHub
parent 33d4f08385
commit 853bbd1f43
2 changed files with 26 additions and 1 deletions
+5
View File
@@ -66,8 +66,13 @@ template <typename ObjectType> void ClearAllBytes(ObjectType &aObject)
*/
template <typename Type> class Clearable
{
friend Type;
public:
void Clear(void) { ClearAllBytes<Type>(*static_cast<Type *>(this)); }
private:
Clearable(void) = default;
};
} // namespace ot
+21 -1
View File
@@ -50,6 +50,8 @@ namespace ot {
*/
template <typename Type> class Unequatable
{
friend Type;
public:
/**
* Overloads operator `!=` to evaluate whether or not two instances of `Type` are equal.
@@ -62,6 +64,9 @@ public:
* @retval FALSE If the two `Type` instances are equal.
*/
bool operator!=(const Type &aOther) const { return !(*static_cast<const Type *>(this) == aOther); }
private:
Unequatable(void) = default;
};
/**
@@ -72,8 +77,10 @@ public:
* Users of this class should follow CRTP-style inheritance, i.e., the `Type` class itself should publicly inherit
* from `Equatable<Type>`.
*/
template <typename Type> class Equatable : public Unequatable<Type>
template <typename Type> class Equatable
{
friend Type;
public:
/**
* Overloads operator `==` to evaluate whether or not two instances of `Type` are equal.
@@ -87,6 +94,19 @@ public:
{
return memcmp(static_cast<const Type *>(this), &aOther, sizeof(Type)) == 0;
}
/**
* Overloads operator `!=` to evaluate whether or not two instances of `Type` are equal.
*
* @param[in] aOther The other `Type` instance to compare with.
*
* @retval TRUE If the two `Type` instances are not equal.
* @retval FALSE If the two `Type` instances are equal.
*/
bool operator!=(const Type &aOther) const { return !(*static_cast<const Type *>(this) == aOther); }
private:
Equatable(void) = default;
};
} // namespace ot