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 7211 : inline static void Destroy(std::size_t id, void * data)
43 : {
44 7211 : if (id == Index)
45 53 : reinterpret_cast<T *>(data)->~T();
46 : else
47 7158 : VariantCurry<Index + 1, Ts...>::Destroy(id, data);
48 7211 : }
49 :
50 1977 : inline static void Move(std::size_t that_t, void * that_v, void * this_v)
51 : {
52 1977 : if (that_t == Index)
53 115 : new (this_v) T(std::move(*reinterpret_cast<T *>(that_v)));
54 : else
55 1862 : VariantCurry<Index + 1, Ts...>::Move(that_t, that_v, this_v);
56 1977 : }
57 :
58 116 : inline static void Copy(std::size_t that_t, const void * that_v, void * this_v)
59 : {
60 116 : if (that_t == Index)
61 : {
62 67 : new (this_v) T(*reinterpret_cast<const T *>(that_v));
63 : }
64 : else
65 : {
66 49 : VariantCurry<Index + 1, Ts...>::Copy(that_t, that_v, this_v);
67 : }
68 116 : }
69 :
70 : inline static bool Equal(std::size_t type_t, const void * that_v, const void * this_v)
71 : {
72 : if (type_t == Index)
73 : {
74 : return *reinterpret_cast<const T *>(this_v) == *reinterpret_cast<const T *>(that_v);
75 : }
76 :
77 : 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 3235 : inline static void Destroy(std::size_t id, void * data) {}
85 827 : 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 : inline static bool Equal(std::size_t type_t, const void * that_v, const void * this_v)
88 : {
89 : 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 : using Data = typename std::aligned_storage<kDataSize, kDataAlign>::type;
143 : using Curry = VariantInternal::VariantCurry<0, Ts...>;
144 :
145 : std::size_t mTypeId;
146 : Data mData;
147 :
148 : public:
149 1773 : Variant() : mTypeId(kInvalidType) {}
150 :
151 : template <typename T, class... Args>
152 0 : constexpr explicit Variant(InPlaceTemplateType<T>, Args &&... args) :
153 0 : mTypeId(VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value)
154 : {
155 0 : new (&mData) T(std::forward<Args>(args)...);
156 0 : }
157 :
158 43 : Variant(const Variant<Ts...> & that) : mTypeId(that.mTypeId) { Curry::Copy(that.mTypeId, &that.mData, &mData); }
159 :
160 0 : Variant(Variant<Ts...> && that) : mTypeId(that.mTypeId)
161 : {
162 0 : Curry::Move(that.mTypeId, &that.mData, &mData);
163 0 : Curry::Destroy(that.mTypeId, &that.mData);
164 0 : that.mTypeId = kInvalidType;
165 0 : }
166 :
167 24 : Variant<Ts...> & operator=(const Variant<Ts...> & that)
168 : {
169 24 : Curry::Destroy(mTypeId, &mData);
170 24 : mTypeId = that.mTypeId;
171 24 : Curry::Copy(that.mTypeId, &that.mData, &mData);
172 24 : return *this;
173 : }
174 :
175 980 : Variant<Ts...> & operator=(Variant<Ts...> && that)
176 : {
177 980 : Curry::Destroy(mTypeId, &mData);
178 980 : mTypeId = that.mTypeId;
179 980 : Curry::Move(that.mTypeId, &that.mData, &mData);
180 980 : Curry::Destroy(that.mTypeId, &that.mData);
181 980 : that.mTypeId = kInvalidType;
182 980 : return *this;
183 : }
184 :
185 : bool operator==(const Variant & other) const
186 : {
187 : return GetType() == other.GetType() && (!Valid() || Curry::Equal(mTypeId, &other.mData, &mData));
188 : }
189 :
190 : template <typename T>
191 6877 : bool Is() const
192 : {
193 6877 : return (mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value);
194 : }
195 :
196 : std::size_t GetType() const { return mTypeId; }
197 :
198 1184 : bool Valid() const { return (mTypeId != kInvalidType); }
199 :
200 : template <typename T, typename... Args>
201 : static Variant<Ts...> Create(Args &&... args)
202 : {
203 : return Variant<Ts...>(InPlaceTemplate<T>, std::forward<Args>(args)...);
204 : }
205 :
206 : template <typename T, typename... Args>
207 166 : void Set(Args &&... args)
208 : {
209 166 : Curry::Destroy(mTypeId, &mData);
210 166 : new (&mData) T(std::forward<Args>(args)...);
211 166 : mTypeId = VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value;
212 166 : }
213 :
214 : template <typename T>
215 641 : T & Get()
216 : {
217 641 : VerifyOrDie((mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value));
218 641 : return *reinterpret_cast<T *>(&mData);
219 : }
220 :
221 : template <typename T>
222 1496 : const T & Get() const
223 : {
224 1496 : VerifyOrDie((mTypeId == VariantInternal::TupleIndexOfType<T, std::tuple<Ts...>>::value));
225 1496 : return *reinterpret_cast<const T *>(&mData);
226 : }
227 :
228 : // Ideally we would suppress this from within Optional.h, where this false positive is coming from. That said suppressing
229 : // here is okay since mTypeId would seemingly only be uninitialized when Variant is in a union.
230 : //
231 : // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage): Only in a false positive is mTypeId uninitialized.
232 1683 : ~Variant() { Curry::Destroy(mTypeId, &mData); }
233 : };
234 :
235 : } // namespace chip
|