Skip to content

Commit 448930e

Browse files
Make chip::Optional be trivially destructible if the underlying type is. (project-chip#37017)
* Make chip::Optional be trivially destructible if the underlying type is. Previous implementation always had a destructor, so it was never trivially destructible. * Remove extra char * Remove unused variable * Remove one more unused variable --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com>
1 parent a96f6ca commit 448930e

File tree

4 files changed

+105
-72
lines changed

4 files changed

+105
-72
lines changed

examples/energy-management-app/energy-management-common/device-energy-management/src/DEMTestEventTriggers.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ constexpr uint16_t MAX_POWER_ADJUSTMENTS = 5;
3636

3737
chip::app::Clusters::DeviceEnergyManagement::Structs::SlotStruct::Type sSlots[MAX_SLOTS];
3838
chip::app::Clusters::DeviceEnergyManagement::Structs::ForecastStruct::Type sForecastStruct;
39-
chip::app::DataModel::Nullable<chip::app::Clusters::DeviceEnergyManagement::Structs::ForecastStruct::Type> sForecast;
4039

4140
chip::app::Clusters::DeviceEnergyManagement::Structs::PowerAdjustStruct::Type sPowerAdjustments[MAX_POWER_ADJUSTMENTS];
4241
chip::app::Clusters::DeviceEnergyManagement::Structs::PowerAdjustCapabilityStruct::Type sPowerAdjustCapabilityStruct;

src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,6 @@ CHIP_ERROR CloseValve(EndpointId ep)
309309
CHIP_ERROR SetValveLevel(EndpointId ep, DataModel::Nullable<Percent> level, DataModel::Nullable<uint32_t> openDuration)
310310
{
311311
Delegate * delegate = GetDelegate(ep);
312-
Optional<Status> status = Optional<Status>::Missing();
313312
CHIP_ERROR attribute_error = CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute);
314313

315314
if (HasFeature(ep, ValveConfigurationAndControl::Feature::kTimeSync))

src/lib/core/Optional.h

+99-64
Original file line numberDiff line numberDiff line change
@@ -48,92 +48,92 @@ template <class T>
4848
class Optional
4949
{
5050
public:
51-
constexpr Optional() : mHasValue(false) {}
52-
constexpr Optional(NullOptionalType) : mHasValue(false) {}
51+
constexpr Optional() {}
52+
constexpr Optional(NullOptionalType) {}
5353

54-
~Optional()
54+
explicit Optional(const T & value)
5555
{
56-
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch): mData is set when mHasValue
57-
if (mHasValue)
58-
{
59-
mValue.mData.~T();
60-
}
56+
mValueHolder.mHasValue = true;
57+
new (&mValueHolder.mValue.mData) T(value);
6158
}
6259

63-
explicit Optional(const T & value) : mHasValue(true) { new (&mValue.mData) T(value); }
64-
6560
template <class... Args>
66-
constexpr explicit Optional(InPlaceType, Args &&... args) : mHasValue(true)
61+
constexpr explicit Optional(InPlaceType, Args &&... args)
6762
{
68-
new (&mValue.mData) T(std::forward<Args>(args)...);
63+
mValueHolder.mHasValue = true;
64+
new (&mValueHolder.mValue.mData) T(std::forward<Args>(args)...);
6965
}
7066

71-
constexpr Optional(const Optional & other) : mHasValue(other.mHasValue)
67+
constexpr Optional(const Optional & other)
7268
{
73-
if (mHasValue)
69+
mValueHolder.mHasValue = other.mValueHolder.mHasValue;
70+
if (mValueHolder.mHasValue)
7471
{
75-
new (&mValue.mData) T(other.mValue.mData);
72+
new (&mValueHolder.mValue.mData) T(other.mValueHolder.mValue.mData);
7673
}
7774
}
7875

7976
// Converts an Optional of an implicitly convertible type
8077
template <class U, std::enable_if_t<!std::is_same_v<T, U> && std::is_convertible_v<const U, T>, bool> = true>
81-
constexpr Optional(const Optional<U> & other) : mHasValue(other.HasValue())
78+
constexpr Optional(const Optional<U> & other)
8279
{
83-
if (mHasValue)
80+
mValueHolder.mHasValue = other.HasValue();
81+
if (mValueHolder.mHasValue)
8482
{
85-
new (&mValue.mData) T(other.Value());
83+
new (&mValueHolder.mValue.mData) T(other.Value());
8684
}
8785
}
8886

8987
// Converts an Optional of a type that requires explicit conversion
9088
template <class U,
9189
std::enable_if_t<!std::is_same_v<T, U> && !std::is_convertible_v<const U, T> && std::is_constructible_v<T, const U &>,
9290
bool> = true>
93-
constexpr explicit Optional(const Optional<U> & other) : mHasValue(other.HasValue())
91+
constexpr explicit Optional(const Optional<U> & other)
9492
{
95-
if (mHasValue)
93+
mValueHolder.mHasValue = other.HasValue();
94+
if (mValueHolder.mHasValue)
9695
{
97-
new (&mValue.mData) T(other.Value());
96+
new (&mValueHolder.mValue.mData) T(other.Value());
9897
}
9998
}
10099

101-
constexpr Optional(Optional && other) : mHasValue(other.mHasValue)
100+
constexpr Optional(Optional && other)
102101
{
103-
if (mHasValue)
102+
mValueHolder.mHasValue = other.mValueHolder.mHasValue;
103+
if (mValueHolder.mHasValue)
104104
{
105-
new (&mValue.mData) T(std::move(other.mValue.mData));
106-
other.mValue.mData.~T();
107-
other.mHasValue = false;
105+
new (&mValueHolder.mValue.mData) T(std::move(other.mValueHolder.mValue.mData));
106+
other.mValueHolder.mValue.mData.~T();
107+
other.mValueHolder.mHasValue = false;
108108
}
109109
}
110110

111111
constexpr Optional & operator=(const Optional & other)
112112
{
113-
if (mHasValue)
113+
if (mValueHolder.mHasValue)
114114
{
115-
mValue.mData.~T();
115+
mValueHolder.mValue.mData.~T();
116116
}
117-
mHasValue = other.mHasValue;
118-
if (mHasValue)
117+
mValueHolder.mHasValue = other.mValueHolder.mHasValue;
118+
if (mValueHolder.mHasValue)
119119
{
120-
new (&mValue.mData) T(other.mValue.mData);
120+
new (&mValueHolder.mValue.mData) T(other.mValueHolder.mValue.mData);
121121
}
122122
return *this;
123123
}
124124

125125
constexpr Optional & operator=(Optional && other)
126126
{
127-
if (mHasValue)
127+
if (mValueHolder.mHasValue)
128128
{
129-
mValue.mData.~T();
129+
mValueHolder.mValue.mData.~T();
130130
}
131-
mHasValue = other.mHasValue;
132-
if (mHasValue)
131+
mValueHolder.mHasValue = other.mValueHolder.mHasValue;
132+
if (mValueHolder.mHasValue)
133133
{
134-
new (&mValue.mData) T(std::move(other.mValue.mData));
135-
other.mValue.mData.~T();
136-
other.mHasValue = false;
134+
new (&mValueHolder.mValue.mData) T(std::move(other.mValueHolder.mValue.mData));
135+
other.mValueHolder.mValue.mData.~T();
136+
other.mValueHolder.mHasValue = false;
137137
}
138138
return *this;
139139
}
@@ -142,24 +142,24 @@ class Optional
142142
template <class... Args>
143143
constexpr T & Emplace(Args &&... args)
144144
{
145-
if (mHasValue)
145+
if (mValueHolder.mHasValue)
146146
{
147-
mValue.mData.~T();
147+
mValueHolder.mValue.mData.~T();
148148
}
149-
mHasValue = true;
150-
new (&mValue.mData) T(std::forward<Args>(args)...);
151-
return mValue.mData;
149+
mValueHolder.mHasValue = true;
150+
new (&mValueHolder.mValue.mData) T(std::forward<Args>(args)...);
151+
return mValueHolder.mValue.mData;
152152
}
153153

154154
/** Make the optional contain a specific value */
155155
constexpr void SetValue(const T & value)
156156
{
157-
if (mHasValue)
157+
if (mValueHolder.mHasValue)
158158
{
159-
mValue.mData.~T();
159+
mValueHolder.mValue.mData.~T();
160160
}
161-
mHasValue = true;
162-
new (&mValue.mData) T(value);
161+
mValueHolder.mHasValue = true;
162+
new (&mValueHolder.mValue.mData) T(value);
163163
}
164164

165165
constexpr void SetValue(std::optional<T> & value)
@@ -177,48 +177,49 @@ class Optional
177177
/** Make the optional contain a specific value */
178178
constexpr void SetValue(T && value)
179179
{
180-
if (mHasValue)
180+
if (mValueHolder.mHasValue)
181181
{
182-
mValue.mData.~T();
182+
mValueHolder.mValue.mData.~T();
183183
}
184-
mHasValue = true;
185-
new (&mValue.mData) T(std::move(value));
184+
mValueHolder.mHasValue = true;
185+
new (&mValueHolder.mValue.mData) T(std::move(value));
186186
}
187187

188188
/** Invalidate the value inside the optional. Optional now has no value */
189189
constexpr void ClearValue()
190190
{
191-
if (mHasValue)
191+
if (mValueHolder.mHasValue)
192192
{
193-
mValue.mData.~T();
193+
mValueHolder.mValue.mData.~T();
194194
}
195-
mHasValue = false;
195+
mValueHolder.mHasValue = false;
196196
}
197197

198198
/** Gets the current value of the optional. Valid IFF `HasValue`. */
199199
T & Value() &
200200
{
201201
VerifyOrDie(HasValue());
202-
return mValue.mData;
202+
return mValueHolder.mValue.mData;
203203
}
204204

205205
/** Gets the current value of the optional. Valid IFF `HasValue`. */
206206
const T & Value() const &
207207
{
208208
VerifyOrDie(HasValue());
209-
return mValue.mData;
209+
return mValueHolder.mValue.mData;
210210
}
211211

212212
/** Gets the current value of the optional if the optional has a value;
213213
otherwise returns the provided default value. */
214214
const T & ValueOr(const T & defaultValue) const { return HasValue() ? Value() : defaultValue; }
215215

216216
/** Checks if the optional contains a value or not */
217-
constexpr bool HasValue() const { return mHasValue; }
217+
constexpr bool HasValue() const { return mValueHolder.mHasValue; }
218218

219219
bool operator==(const Optional & other) const
220220
{
221-
return (mHasValue == other.mHasValue) && (!other.mHasValue || (mValue.mData == other.mValue.mData));
221+
return (mValueHolder.mHasValue == other.mValueHolder.mHasValue) &&
222+
(!other.mValueHolder.mHasValue || (mValueHolder.mValue.mData == other.mValueHolder.mValue.mData));
222223
}
223224
bool operator!=(const Optional & other) const { return !(*this == other); }
224225
bool operator==(const T & other) const { return HasValue() && Value() == other; }
@@ -241,13 +242,47 @@ class Optional
241242
}
242243

243244
private:
244-
bool mHasValue;
245-
union Value
245+
// A container of bool + value (without constructor/destructor) when the underlying
246+
// type has a trivial destructor
247+
class TrivialDestructor
248+
{
249+
public:
250+
bool mHasValue = false;
251+
union Value
252+
{
253+
Value() {}
254+
T mData;
255+
} mValue;
256+
};
257+
258+
// A container of bool + value that destroys the underlying type when mHasValue is true.
259+
// To be used for non-trivial destructor classes.
260+
class NonTrivialDestructor
261+
{
262+
public:
263+
~NonTrivialDestructor()
264+
{
265+
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch): mData is set when mHasValue
266+
if (mHasValue)
267+
{
268+
mValue.mData.~T();
269+
}
270+
}
271+
272+
bool mHasValue = false;
273+
union Value
274+
{
275+
Value() {}
276+
~Value() {}
277+
T mData;
278+
} mValue;
279+
};
280+
281+
class ValueHolder : public std::conditional_t<std::is_trivially_destructible_v<T>, TrivialDestructor, NonTrivialDestructor>
246282
{
247-
Value() {}
248-
~Value() {}
249-
T mData;
250-
} mValue;
283+
};
284+
285+
ValueHolder mValueHolder;
251286
};
252287

253288
template <class T>

src/lib/core/tests/TestOptional.cpp

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,18 @@
1717
* limitations under the License.
1818
*/
1919

20-
/**
21-
* @file
22-
* This file implements a test for CHIP core library reference counted object.
23-
*
24-
*/
25-
2620
#include <array>
2721
#include <cinttypes>
2822
#include <cstdint>
2923
#include <cstring>
24+
#include <string>
3025

3126
#include <pw_unit_test/framework.h>
3227

3328
#include <lib/core/Optional.h>
3429
#include <lib/core/StringBuilderAdapters.h>
3530
#include <lib/support/Span.h>
31+
#include <type_traits>
3632

3733
using namespace chip;
3834

@@ -76,6 +72,10 @@ struct CountMovable : public Count
7672
int Count::created;
7773
int Count::destroyed;
7874

75+
// Optional is trivially destructible if the underlying type is trivially destructible
76+
static_assert(std::is_trivially_destructible_v<chip::Optional<unsigned>>);
77+
static_assert(!std::is_trivially_destructible_v<chip::Optional<std::string>>);
78+
7979
TEST(TestOptional, TestBasic)
8080
{
8181
// Set up our test Count objects, which will mess with counts, before we reset the

0 commit comments

Comments
 (0)