Flutter iOS Embedder
encodable_value.h
Go to the documentation of this file.
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 <optional>
13 #include <string>
14 #include <utility>
15 #include <variant>
16 #include <vector>
17 
18 // Unless overridden, attempt to detect the RTTI state from the compiler.
19 #ifndef FLUTTER_ENABLE_RTTI
20 #if defined(_MSC_VER)
21 #ifdef _CPPRTTI
22 #define FLUTTER_ENABLE_RTTI 1
23 #endif
24 #elif defined(__clang__)
25 #if __has_feature(cxx_rtti)
26 #define FLUTTER_ENABLE_RTTI 1
27 #endif
28 #elif defined(__GNUC__)
29 #ifdef __GXX_RTTI
30 #define FLUTTER_ENABLE_RTTI 1
31 #endif
32 #endif
33 #endif // #ifndef FLUTTER_ENABLE_RTTI
34 
35 namespace flutter {
36 
37 static_assert(sizeof(double) == 8, "EncodableValue requires a 64-bit double");
38 
39 // A container for arbitrary types in EncodableValue.
40 //
41 // This is used in conjunction with StandardCodecExtension to allow using other
42 // types with a StandardMethodCodec/StandardMessageCodec. It is implicitly
43 // convertible to EncodableValue, so constructing an EncodableValue from a
44 // custom type can generally be written as:
45 // CustomEncodableValue(MyType(...))
46 // rather than:
47 // EncodableValue(CustomEncodableValue(MyType(...)))
48 //
49 // For extracting received custom types, it is implicitly convertible to
50 // std::any. For example:
51 // const MyType& my_type_value =
52 // std::any_cast<MyType>(std::get<CustomEncodableValue>(value));
53 //
54 // If RTTI is enabled, different extension types can be checked with type():
55 // if (custom_value->type() == typeid(SomeData)) { ... }
56 // Clients that wish to disable RTTI would need to decide on another approach
57 // for distinguishing types (e.g., in StandardCodecExtension::WriteValueOfType)
58 // if multiple custom types are needed. For instance, wrapping all of the
59 // extension types in an EncodableValue-style variant, and only ever storing
60 // that variant in CustomEncodableValue.
62  public:
63  explicit CustomEncodableValue(const std::any& value) : value_(value) {}
64  ~CustomEncodableValue() = default;
65 
66  // Allow implicit conversion to std::any to allow direct use of any_cast.
67  // NOLINTNEXTLINE(google-explicit-constructor)
68  operator std::any&() { return value_; }
69  // NOLINTNEXTLINE(google-explicit-constructor)
70  operator const std::any&() const { return value_; }
71 
72 #if defined(FLUTTER_ENABLE_RTTI) && FLUTTER_ENABLE_RTTI
73  // Passthrough to std::any's type().
74  const std::type_info& type() const noexcept { return value_.type(); }
75 #endif
76 
77  // This operator exists only to provide a stable ordering for use as a
78  // std::map key, to satisfy the compiler requirements for EncodableValue.
79  // It does not attempt to provide useful ordering semantics, and using a
80  // custom value as a map key is not recommended.
81  bool operator<(const CustomEncodableValue& other) const {
82  return this < &other;
83  }
84  bool operator==(const CustomEncodableValue& other) const {
85  return this == &other;
86  }
87 
88  private:
89  std::any value_;
90 };
91 
92 class EncodableValue;
93 
94 // Convenience type aliases.
95 using EncodableList = std::vector<EncodableValue>;
96 using EncodableMap = std::map<EncodableValue, EncodableValue>;
97 
98 namespace internal {
99 // The base class for EncodableValue. Do not use this directly; it exists only
100 // for EncodableValue to inherit from.
101 //
102 // Do not change the order or indexes of the items here; see the comment on
103 // EncodableValue
104 using EncodableValueVariant = std::variant<std::monostate,
105  bool,
106  int32_t,
107  int64_t,
108  double,
109  std::string,
110  std::vector<uint8_t>,
111  std::vector<int32_t>,
112  std::vector<int64_t>,
113  std::vector<double>,
115  EncodableMap,
117  std::vector<float>>;
118 } // namespace internal
119 
120 // An object that can contain any value or collection type supported by
121 // Flutter's standard method codec.
122 //
123 // For details, see:
124 // https://api.flutter-io.cn/flutter/services/StandardMessageCodec-class.html
125 //
126 // As an example, the following Dart structure:
127 // {
128 // 'flag': true,
129 // 'name': 'Thing',
130 // 'values': [1, 2.0, 4],
131 // }
132 // would correspond to:
133 // EncodableValue(EncodableMap{
134 // {EncodableValue("flag"), EncodableValue(true)},
135 // {EncodableValue("name"), EncodableValue("Thing")},
136 // {EncodableValue("values"), EncodableValue(EncodableList{
137 // EncodableValue(1),
138 // EncodableValue(2.0),
139 // EncodableValue(4),
140 // })},
141 // })
142 //
143 // The primary API surface for this object is std::variant. For instance,
144 // getting a string value from an EncodableValue, with type checking:
145 // if (std::holds_alternative<std::string>(value)) {
146 // std::string some_string = std::get<std::string>(value);
147 // }
148 //
149 // The order/indexes of the variant types is part of the API surface, and is
150 // guaranteed not to change.
151 //
152 // The variant types are mapped with Dart types in following ways:
153 // std::monostate -> null
154 // bool -> bool
155 // int32_t -> int
156 // int64_t -> int
157 // double -> double
158 // std::string -> String
159 // std::vector<uint8_t> -> Uint8List
160 // std::vector<int32_t> -> Int32List
161 // std::vector<int64_t> -> Int64List
162 // std::vector<float> -> Float32List
163 // std::vector<double> -> Float64List
164 // EncodableList -> List
165 // EncodableMap -> Map
167  public:
168  // Rely on std::variant for most of the constructors/operators.
170  using super::super;
171  using super::operator=;
172 
173  explicit EncodableValue() = default;
174 
175  // Avoid the C++17 pitfall of conversion from char* to bool. Should not be
176  // needed for C++20.
177  explicit EncodableValue(const char* string) : super(std::string(string)) {}
178  EncodableValue& operator=(const char* other) {
179  *this = std::string(other);
180  return *this;
181  }
182 
183  // Allow implicit conversion from CustomEncodableValue; the only reason to
184  // make a CustomEncodableValue (which can only be constructed explicitly) is
185  // to use it with EncodableValue, so the risk of unintended conversions is
186  // minimal, and it avoids the need for the verbose:
187  // EncodableValue(CustomEncodableValue(...)).
188  // NOLINTNEXTLINE(google-explicit-constructor)
190 
191  // Override the conversion constructors from std::variant to make them
192  // explicit, to avoid implicit conversion.
193  //
194  // While implicit conversion can be convenient in some cases, it can have very
195  // surprising effects. E.g., calling a function that takes an EncodableValue
196  // but accidentally passing an EncodableValue* would, instead of failing to
197  // compile, go through a pointer->bool->EncodableValue(bool) chain and
198  // silently call the function with a temp-constructed EncodableValue(true).
199  template <class T>
200  constexpr explicit EncodableValue(T&& t) noexcept : super(t) {}
201 
202  // Returns true if the value is null. Convenience wrapper since unlike the
203  // other types, std::monostate uses aren't self-documenting.
204  bool IsNull() const { return std::holds_alternative<std::monostate>(*this); }
205 
206  // Convenience method to simplify handling objects received from Flutter
207  // where the values may be larger than 32-bit, since they have the same type
208  // on the Dart side, but will be either 32-bit or 64-bit here depending on
209  // the value.
210  //
211  // Calling this method if the value doesn't contain either an int32_t or an
212  // int64_t will throw an exception.
213  int64_t LongValue() const {
214  if (std::holds_alternative<int32_t>(*this)) {
215  return std::get<int32_t>(*this);
216  }
217  return std::get<int64_t>(*this);
218  }
219 
220  // Convenience method to simplify handling objects received from Flutter
221  // where the values may be larger than 32-bit, since they have the same type
222  // on the Dart side, but will be either 32-bit or 64-bit here depending on
223  // the value.
224  //
225  // Calling this method if the value doesn't contain either an int32_t or an
226  // int64_t will return std::nullopt.
227  std::optional<int64_t> TryGetLongValue() const {
228  if (std::holds_alternative<int32_t>(*this)) {
229  return std::get<int32_t>(*this);
230  }
231  if (std::holds_alternative<int64_t>(*this)) {
232  return std::get<int64_t>(*this);
233  }
234  return std::nullopt;
235  }
236 
237 // The C++ Standard Library implementations can get into issues with recursive
238 // constraint satisfaction when (among other things) objects of this type (which
239 // is an `std::variant` subclass) are put into containers like `std::vector`.
240 //
241 // A definition of `operator<` is provided to break that recursion. However, in
242 // C++20 with the latest compilers (Clang compilers newer than 20 and the latest
243 // GCC variants, see https://gcc.godbolt.org/z/KM6n6qane) requiring that that
244 // the `std::three_way_comparable` constraint be satisfied requires the
245 // provision of `operator<=>` to do the same thing.
246 //
247 // The code below makes this translation unit be safe to include from both C++17
248 // and C++20 translation units while also using the newest compilers.
249 //
250 // The correctness of the compiler's gripes with this code and the subsequent
251 // need for these workarounds is not fully understood or explored. If you run
252 // into issues with this code again, the following breadcrumbs may prove
253 // useful. If you cannot access some or all of these links, the compiler
254 // explorer link above should serve a reduced test case to base an investigation
255 // off of.
256 //
257 // * b/423885648#comment8
258 // * b/423885648#comment19
259 // * cl/542631351
260 // * cl/542541552
261 // * https://github.com/flutter/engine/pull/43091
262 //
263 #if __cplusplus >= 202002L
264  friend std::partial_ordering operator<=>(const EncodableValue& lhs,
265  const EncodableValue& rhs) {
266  auto& lv = static_cast<const super&>(lhs);
267  auto& rv = static_cast<const super&>(rhs);
268 
269  if (lv < rv) {
270  return std::partial_ordering::less;
271  }
272 
273  if (rv < lv) {
274  return std::partial_ordering::greater;
275  }
276 
277  if (lv == rv) {
278  return std::partial_ordering::equivalent;
279  }
280 
281  return std::partial_ordering::unordered;
282  }
283 #else // __cplusplus >= 202002L
284  friend bool operator<(const EncodableValue& lhs, const EncodableValue& rhs) {
285  return static_cast<const super&>(lhs) < static_cast<const super&>(rhs);
286  }
287 #endif // __cplusplus >= 202002L
288 };
289 
290 } // namespace flutter
291 
292 #endif // FLUTTER_SHELL_PLATFORM_COMMON_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_
CustomEncodableValue(const std::any &value)
bool operator==(const CustomEncodableValue &other) const
bool operator<(const CustomEncodableValue &other) const
EncodableValue(const char *string)
std::optional< int64_t > TryGetLongValue() const
EncodableValue & operator=(const char *other)
internal::EncodableValueVariant super
EncodableValue(const CustomEncodableValue &v)
int64_t LongValue() const
friend bool operator<(const EncodableValue &lhs, const EncodableValue &rhs)
constexpr EncodableValue(T &&t) noexcept
std::variant< std::monostate, bool, int32_t, int64_t, double, std::string, std::vector< uint8_t >, std::vector< int32_t >, std::vector< int64_t >, std::vector< double >, EncodableList, EncodableMap, CustomEncodableValue, std::vector< float > > EncodableValueVariant
std::vector< EncodableValue > EncodableList
std::map< EncodableValue, EncodableValue > EncodableMap