Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 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 <lib/core/CHIPCore.h>
22 :
23 : #include <algorithm>
24 : #include <new>
25 : #include <tuple>
26 : #include <type_traits>
27 : #include <typeinfo>
28 : #include <utility>
29 :
30 : #include <lib/core/InPlace.h>
31 :
32 : namespace chip {
33 :
34 : namespace VariantInternal {
35 :
36 : template <std::size_t Index, typename... Ts>
37 : struct VariantCurry;
38 :
39 : template <std::size_t Index, typename T, typename... Ts>
40 : struct VariantCurry<Index, T, Ts...>
41 : {
42 20826 : inline static void Destroy(std::size_t id, void * data)
43 : {
44 20826 : if (id == Index)
45 769 : reinterpret_cast<T *>(data)->~T();
46 : else
47 20057 : VariantCurry<Index + 1, Ts...>::Destroy(id, data);
48 20826 : }
49 :
50 4697 : inline static void Move(std::size_t that_t, void * that_v, void * this_v)
51 : {
52 4697 : if (that_t == Index)
53 235 : new (this_v) T(std::move(*reinterpret_cast<T *>(that_v)));
54 : else
55 4462 : VariantCurry<Index + 1, Ts...>::Move(that_t, that_v, this_v);
56 4697 : }
57 :
58 203 : inline static void Copy(std::size_t that_t, const void * that_v, void * this_v)
59 : {
60 203 : if (that_t == Index)
61 : {
62 106 : new (this_v) T(*reinterpret_cast<const T *>(that_v));
63 : }
64 : else
65 : {
66 97 : VariantCurry<Index + 1, Ts...>::Copy(that_t, that_v, this_v);
67 : }
68 203 : }
69 :
70 19 : inline static bool Equal(std::size_t type_t, const void * that_v, const void * this_v)
71 : {
72 19 : if (type_t == Index)
73 : {
74 10 : return *reinterpret_cast<const T *>(this_v) == *reinterpret_cast<const T *>(that_v);
75 : }
76 :
77 9 : return VariantCurry<Index + 1, Ts...>::Equal(type_t, that_v, this_v);
78 : }
79 : };
80 :
81 : template <std::size_t Index>
82 : struct VariantCurry<Index>
83 : {
84 8104 : inline static void Destroy(std::size_t id, void * data) {}
85 1772 : inline static void Move(std::size_t that_t, void * that_v, void * this_v) {}
86 0 : inline static void Copy(std::size_t that_t, const void * that_v, void * this_v) {}
87 0 : inline static bool Equal(std::size_t type_t, const void * that_v, const void * this_v)
88 : {
89 0 : VerifyOrDie(false);
90 : return false;
91 : }
92 : };
93 :
94 : template <typename T, typename TupleType>
95 : class TupleIndexOfType
96 : {
97 : private:
98 : template <std::size_t Index>
99 : static constexpr
100 : typename std::enable_if<std::is_same<T, typename std::tuple_element<Index, TupleType>::type>::value, std::size_t>::type
101 : calculate()
102 : {
103 : return Index;
104 : }
105 :
106 : template <std::size_t Index>
107 : static constexpr
108 : typename std::enable_if<!std::is_same<T, typename std::tuple_element<Index, TupleType>::type>::value, std::size_t>::type
109 : calculate()
110 : {
111 : return calculate<Index + 1>();
112 : }
113 :
114 : public:
115 : static constexpr std::size_t value = calculate<0>();
116 : };
117 :
118 : } // namespace VariantInternal
119 :
120 : /**
121 : * @brief
122 : * Represents a type-safe union. An instance of Variant at any given time either holds a value of one of its
123 : * alternative types, or no value.
124 : *
125 : * Example:
126 : * struct Type1 {};
127 : *
128 : * struct Type2 {};
129 : *
130 : * Variant<Type1, Type2> v;
131 : * v.Set<Type1>(); // v contains Type1
132 : * Type1 o1 = v.Get<Type1>();
133 : */
134 : template <typename... Ts>
135 : struct Variant
136 : {
137 : private:
138 : static constexpr std::size_t kDataSize = std::max({ sizeof(Ts)... });
139 : static constexpr std::size_t kDataAlign = std::max({ alignof(Ts)... });
140 : static constexpr std::size_t kInvalidType = SIZE_MAX;
141 :
142 : struct Data
143 : {
144 : alignas(kDataAlign) unsigned char data[kDataSize];
145 : };
146 : using Curry = VariantInternal::VariantCurry<0, Ts...>;
147 :
148 : std::size_t mTypeId;
149 : Data mData;
150 :
151 : public:
152 4325 : Variant() : mTypeId(kInvalidType) {}
153 :
154 : template <typename T, class... Args>
155 72 : constexpr explicit Variant(InPlaceTemplateType<T>, Args &&... args) :
156 72 : mTypeId(VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value)
157 : {
158 72 : new (&mData) T(std::forward<Args>(args)...);
159 72 : }
160 :
161 69 : Variant(const Variant<Ts...> & that) : mTypeId(that.mTypeId) { Curry::Copy(that.mTypeId, &that.mData, &mData); }
162 :
163 71 : Variant(Variant<Ts...> && that) : mTypeId(that.mTypeId)
164 : {
165 71 : Curry::Move(that.mTypeId, &that.mData, &mData);
166 71 : Curry::Destroy(that.mTypeId, &that.mData);
167 71 : that.mTypeId = kInvalidType;
168 71 : }
169 :
170 37 : Variant<Ts...> & operator=(const Variant<Ts...> & that)
171 : {
172 37 : Curry::Destroy(mTypeId, &mData);
173 37 : mTypeId = that.mTypeId;
174 37 : Curry::Copy(that.mTypeId, &that.mData, &mData);
175 37 : return *this;
176 : }
177 :
178 1936 : Variant<Ts...> & operator=(Variant<Ts...> && that)
179 : {
180 1936 : Curry::Destroy(mTypeId, &mData);
181 1936 : mTypeId = that.mTypeId;
182 1936 : Curry::Move(that.mTypeId, &that.mData, &mData);
183 1936 : Curry::Destroy(that.mTypeId, &that.mData);
184 1936 : that.mTypeId = kInvalidType;
185 1936 : return *this;
186 : }
187 :
188 25 : bool operator==(const Variant & other) const
189 : {
190 25 : return GetType() == other.GetType() && (!Valid() || Curry::Equal(mTypeId, &other.mData, &mData));
191 : }
192 :
193 : template <typename T>
194 8748 : bool Is() const
195 : {
196 8748 : return (mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value);
197 : }
198 :
199 50 : std::size_t GetType() const { return mTypeId; }
200 :
201 1076 : bool Valid() const { return (mTypeId != kInvalidType); }
202 :
203 : template <typename T, typename... Args>
204 28 : static Variant<Ts...> Create(Args &&... args)
205 : {
206 28 : return Variant<Ts...>(InPlaceTemplate<T>, std::forward<Args>(args)...);
207 : }
208 :
209 : template <typename T, typename... Args>
210 356 : void Set(Args &&... args)
211 : {
212 356 : Curry::Destroy(mTypeId, &mData);
213 356 : new (&mData) T(std::forward<Args>(args)...);
214 356 : mTypeId = VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value;
215 356 : }
216 :
217 : template <typename T>
218 685 : T & Get()
219 : {
220 685 : VerifyOrDie((mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value));
221 685 : return *reinterpret_cast<T *>(&mData);
222 : }
223 :
224 : template <typename T>
225 1751 : const T & Get() const
226 : {
227 1751 : VerifyOrDie((mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value));
228 1751 : return *reinterpret_cast<const T *>(&mData);
229 : }
230 :
231 : // Ideally we would suppress this from within Optional.h, where this false positive is coming from. That said suppressing
232 : // here is okay since mTypeId would seemingly only be uninitialized when Variant is in a union.
233 : //
234 : // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage): Only in a false positive is mTypeId uninitialized.
235 4537 : ~Variant() { Curry::Destroy(mTypeId, &mData); }
236 : };
237 :
238 : } // namespace chip
|