1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #ifndef FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ |
6 | #define FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ |
7 | |
8 | #include <any> |
9 | #include <cassert> |
10 | #include <cstdint> |
11 | #include <map> |
12 | #include <string> |
13 | #include <utility> |
14 | #include <variant> |
15 | #include <vector> |
16 | |
17 | // Unless overridden, attempt to detect the RTTI state from the compiler. |
18 | #ifndef FLUTTER_ENABLE_RTTI |
19 | #if defined(_MSC_VER) |
20 | #ifdef _CPPRTTI |
21 | #define FLUTTER_ENABLE_RTTI 1 |
22 | #endif |
23 | #elif defined(__clang__) |
24 | #if __has_feature(cxx_rtti) |
25 | #define FLUTTER_ENABLE_RTTI 1 |
26 | #endif |
27 | #elif defined(__GNUC__) |
28 | #ifdef __GXX_RTTI |
29 | #define FLUTTER_ENABLE_RTTI 1 |
30 | #endif |
31 | #endif |
32 | #endif // #ifndef FLUTTER_ENABLE_RTTI |
33 | |
34 | namespace flutter { |
35 | |
36 | static_assert(sizeof(double) == 8, "EncodableValue requires a 64-bit double" ); |
37 | |
38 | // A container for arbitrary types in EncodableValue. |
39 | // |
40 | // This is used in conjunction with StandardCodecExtension to allow using other |
41 | // types with a StandardMethodCodec/StandardMessageCodec. It is implicitly |
42 | // convertible to EncodableValue, so constructing an EncodableValue from a |
43 | // custom type can generally be written as: |
44 | // CustomEncodableValue(MyType(...)) |
45 | // rather than: |
46 | // EncodableValue(CustomEncodableValue(MyType(...))) |
47 | // |
48 | // For extracting received custom types, it is implicitly convertible to |
49 | // std::any. For example: |
50 | // const MyType& my_type_value = |
51 | // std::any_cast<MyType>(std::get<CustomEncodableValue>(value)); |
52 | // |
53 | // If RTTI is enabled, different extension types can be checked with type(): |
54 | // if (custom_value->type() == typeid(SomeData)) { ... } |
55 | // Clients that wish to disable RTTI would need to decide on another approach |
56 | // for distinguishing types (e.g., in StandardCodecExtension::WriteValueOfType) |
57 | // if multiple custom types are needed. For instance, wrapping all of the |
58 | // extension types in an EncodableValue-style variant, and only ever storing |
59 | // that variant in CustomEncodableValue. |
60 | class CustomEncodableValue { |
61 | public: |
62 | explicit CustomEncodableValue(const std::any& value) : value_(value) {} |
63 | ~CustomEncodableValue() = default; |
64 | |
65 | // Allow implicit conversion to std::any to allow direct use of any_cast. |
66 | // NOLINTNEXTLINE(google-explicit-constructor) |
67 | operator std::any&() { return value_; } |
68 | // NOLINTNEXTLINE(google-explicit-constructor) |
69 | operator const std::any&() const { return value_; } |
70 | |
71 | #if defined(FLUTTER_ENABLE_RTTI) && FLUTTER_ENABLE_RTTI |
72 | // Passthrough to std::any's type(). |
73 | const std::type_info& type() const noexcept { return value_.type(); } |
74 | #endif |
75 | |
76 | // This operator exists only to provide a stable ordering for use as a |
77 | // std::map key, to satisfy the compiler requirements for EncodableValue. |
78 | // It does not attempt to provide useful ordering semantics, and using a |
79 | // custom value as a map key is not recommended. |
80 | bool operator<(const CustomEncodableValue& other) const { |
81 | return this < &other; |
82 | } |
83 | bool operator==(const CustomEncodableValue& other) const { |
84 | return this == &other; |
85 | } |
86 | |
87 | private: |
88 | std::any value_; |
89 | }; |
90 | |
91 | class EncodableValue; |
92 | |
93 | // Convenience type aliases. |
94 | using EncodableList = std::vector<EncodableValue>; |
95 | using EncodableMap = std::map<EncodableValue, EncodableValue>; |
96 | |
97 | namespace internal { |
98 | // The base class for EncodableValue. Do not use this directly; it exists only |
99 | // for EncodableValue to inherit from. |
100 | // |
101 | // Do not change the order or indexes of the items here; see the comment on |
102 | // EncodableValue |
103 | using EncodableValueVariant = std::variant<std::monostate, |
104 | bool, |
105 | int32_t, |
106 | int64_t, |
107 | double, |
108 | std::string, |
109 | std::vector<uint8_t>, |
110 | std::vector<int32_t>, |
111 | std::vector<int64_t>, |
112 | std::vector<double>, |
113 | EncodableList, |
114 | EncodableMap, |
115 | CustomEncodableValue, |
116 | std::vector<float>>; |
117 | } // namespace internal |
118 | |
119 | // An object that can contain any value or collection type supported by |
120 | // Flutter's standard method codec. |
121 | // |
122 | // For details, see: |
123 | // https://api.flutter.dev/flutter/services/StandardMessageCodec-class.html |
124 | // |
125 | // As an example, the following Dart structure: |
126 | // { |
127 | // 'flag': true, |
128 | // 'name': 'Thing', |
129 | // 'values': [1, 2.0, 4], |
130 | // } |
131 | // would correspond to: |
132 | // EncodableValue(EncodableMap{ |
133 | // {EncodableValue("flag"), EncodableValue(true)}, |
134 | // {EncodableValue("name"), EncodableValue("Thing")}, |
135 | // {EncodableValue("values"), EncodableValue(EncodableList{ |
136 | // EncodableValue(1), |
137 | // EncodableValue(2.0), |
138 | // EncodableValue(4), |
139 | // })}, |
140 | // }) |
141 | // |
142 | // The primary API surface for this object is std::variant. For instance, |
143 | // getting a string value from an EncodableValue, with type checking: |
144 | // if (std::holds_alternative<std::string>(value)) { |
145 | // std::string some_string = std::get<std::string>(value); |
146 | // } |
147 | // |
148 | // The order/indexes of the variant types is part of the API surface, and is |
149 | // guaranteed not to change. |
150 | // |
151 | // The variant types are mapped with Dart types in following ways: |
152 | // std::monostate -> null |
153 | // bool -> bool |
154 | // int32_t -> int |
155 | // int64_t -> int |
156 | // double -> double |
157 | // std::string -> String |
158 | // std::vector<uint8_t> -> Uint8List |
159 | // std::vector<int32_t> -> Int32List |
160 | // std::vector<int64_t> -> Int64List |
161 | // std::vector<float> -> Float32List |
162 | // std::vector<double> -> Float64List |
163 | // EncodableList -> List |
164 | // EncodableMap -> Map |
165 | class EncodableValue : public internal::EncodableValueVariant { |
166 | public: |
167 | // Rely on std::variant for most of the constructors/operators. |
168 | using super = internal::EncodableValueVariant; |
169 | using super::super; |
170 | using super::operator=; |
171 | |
172 | explicit EncodableValue() = default; |
173 | |
174 | // Avoid the C++17 pitfall of conversion from char* to bool. Should not be |
175 | // needed for C++20. |
176 | explicit EncodableValue(const char* string) : super(std::string(string)) {} |
177 | EncodableValue& operator=(const char* other) { |
178 | *this = std::string(other); |
179 | return *this; |
180 | } |
181 | |
182 | // Allow implicit conversion from CustomEncodableValue; the only reason to |
183 | // make a CustomEncodableValue (which can only be constructed explicitly) is |
184 | // to use it with EncodableValue, so the risk of unintended conversions is |
185 | // minimal, and it avoids the need for the verbose: |
186 | // EncodableValue(CustomEncodableValue(...)). |
187 | // NOLINTNEXTLINE(google-explicit-constructor) |
188 | EncodableValue(const CustomEncodableValue& v) : super(v) {} |
189 | |
190 | // Override the conversion constructors from std::variant to make them |
191 | // explicit, to avoid implicit conversion. |
192 | // |
193 | // While implicit conversion can be convenient in some cases, it can have very |
194 | // surprising effects. E.g., calling a function that takes an EncodableValue |
195 | // but accidentally passing an EncodableValue* would, instead of failing to |
196 | // compile, go through a pointer->bool->EncodableValue(bool) chain and |
197 | // silently call the function with a temp-constructed EncodableValue(true). |
198 | template <class T> |
199 | constexpr explicit EncodableValue(T&& t) noexcept : super(t) {} |
200 | |
201 | // Returns true if the value is null. Convenience wrapper since unlike the |
202 | // other types, std::monostate uses aren't self-documenting. |
203 | bool IsNull() const { return std::holds_alternative<std::monostate>(v: *this); } |
204 | |
205 | // Convenience method to simplify handling objects received from Flutter |
206 | // where the values may be larger than 32-bit, since they have the same type |
207 | // on the Dart side, but will be either 32-bit or 64-bit here depending on |
208 | // the value. |
209 | // |
210 | // Calling this method if the value doesn't contain either an int32_t or an |
211 | // int64_t will throw an exception. |
212 | int64_t LongValue() const { |
213 | if (std::holds_alternative<int32_t>(v: *this)) { |
214 | return std::get<int32_t>(v: *this); |
215 | } |
216 | return std::get<int64_t>(v: *this); |
217 | } |
218 | |
219 | // Explicitly provide operator<, delegating to std::variant's operator<. |
220 | // There are issues with with the way the standard library-provided |
221 | // < and <=> comparisons interact with classes derived from variant. |
222 | friend bool operator<(const EncodableValue& lhs, const EncodableValue& rhs) { |
223 | return static_cast<const super&>(lhs) < static_cast<const super&>(rhs); |
224 | } |
225 | }; |
226 | |
227 | } // namespace flutter |
228 | |
229 | #endif // FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ |
230 | |