blob: 09c005b238a8ae6b2bf2027b35e29d8e3244bba5 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <any>
#include <cassert>
#include <cstdint>
#include <map>
#include <string>
#include <utility>
#include <variant>
#include <vector>
// Unless overridden, attempt to detect the RTTI state from the compiler.
#if defined(_MSC_VER)
#ifdef _CPPRTTI
#elif defined(__clang__)
#if __has_feature(cxx_rtti)
#elif defined(__GNUC__)
#ifdef __GXX_RTTI
#endif // #ifndef FLUTTER_ENABLE_RTTI
namespace standard_message_codec {
static_assert(sizeof(double) == 8, "EncodableValue requires a 64-bit double");
// A container for arbitrary types in EncodableValue.
// This is used in conjunction with StandardCodecExtension to allow using other
// types with a StandardMethodCodec/StandardMessageCodec. It is implicitly
// convertible to EncodableValue, so constructing an EncodableValue from a
// custom type can generally be written as:
// CustomEncodableValue(MyType(...))
// rather than:
// EncodableValue(CustomEncodableValue(MyType(...)))
// For extracting received custom types, it is implicitly convertible to
// std::any. For example:
// const MyType& my_type_value =
// std::any_cast<MyType>(std::get<CustomEncodableValue>(value));
// If RTTI is enabled, different extension types can be checked with type():
// if (custom_value->type() == typeid(SomeData)) { ... }
// Clients that wish to disable RTTI would need to decide on another approach
// for distinguishing types (e.g., in StandardCodecExtension::WriteValueOfType)
// if multiple custom types are needed. For instance, wrapping all of the
// extension types in an EncodableValue-style variant, and only ever storing
// that variant in CustomEncodableValue.
class CustomEncodableValue {
explicit CustomEncodableValue(const std::any& value) : value_(value) {}
~CustomEncodableValue() = default;
// Allow implicit conversion to std::any to allow direct use of any_cast.
// NOLINTNEXTLINE(google-explicit-constructor)
operator std::any&() { return value_; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator const std::any&() const { return value_; }
// Passthrough to std::any's type().
const std::type_info& type() const noexcept { return value_.type(); }
// This operator exists only to provide a stable ordering for use as a
// std::map key, to satisfy the compiler requirements for EncodableValue.
// It does not attempt to provide useful ordering semantics, and using a
// custom value as a map key is not recommended.
bool operator<(const CustomEncodableValue& other) const { return this < &other; }
bool operator==(const CustomEncodableValue& other) const { return this == &other; }
std::any value_;
class EncodableValue;
// Convenience type aliases.
using EncodableList = std::vector<EncodableValue>;
using EncodableMap = std::map<EncodableValue, EncodableValue>;
namespace internal {
// The base class for EncodableValue. Do not use this directly; it exists only
// for EncodableValue to inherit from.
// Do not change the order or indexes of the items here; see the comment on
// EncodableValue
using EncodableValueVariant =
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>>;
} // namespace internal
// An object that can contain any value or collection type supported by
// Flutter's standard method codec.
// For details, see:
// As an example, the following Dart structure:
// {
// 'flag': true,
// 'name': 'Thing',
// 'values': [1, 2.0, 4],
// }
// would correspond to:
// EncodableValue(EncodableMap{
// {EncodableValue("flag"), EncodableValue(true)},
// {EncodableValue("name"), EncodableValue("Thing")},
// {EncodableValue("values"), EncodableValue(EncodableList{
// EncodableValue(1),
// EncodableValue(2.0),
// EncodableValue(4),
// })},
// })
// The primary API surface for this object is std::variant. For instance,
// getting a string value from an EncodableValue, with type checking:
// if (std::holds_alternative<std::string>(value)) {
// std::string some_string = std::get<std::string>(value);
// }
// The order/indexes of the variant types is part of the API surface, and is
// guaranteed not to change.
// The variant types are mapped with Dart types in following ways:
// std::monostate -> null
// bool -> bool
// int32_t -> int
// int64_t -> int
// double -> double
// std::string -> String
// std::vector<uint8_t> -> Uint8List
// std::vector<int32_t> -> Int32List
// std::vector<int64_t> -> Int64List
// std::vector<float> -> Float32List
// std::vector<double> -> Float64List
// EncodableList -> List
// EncodableMap -> Map
class EncodableValue : public internal::EncodableValueVariant {
// Rely on std::variant for most of the constructors/operators.
using super = internal::EncodableValueVariant;
using super::super;
using super::operator=;
explicit EncodableValue() = default;
// Avoid the C++17 pitfall of conversion from char* to bool. Should not be
// needed for C++20.
explicit EncodableValue(const char* string) : super(std::string(string)) {}
EncodableValue& operator=(const char* other) {
*this = std::string(other);
return *this;
// Allow implicit conversion from CustomEncodableValue; the only reason to
// make a CustomEncodableValue (which can only be constructed explicitly) is
// to use it with EncodableValue, so the risk of unintended conversions is
// minimal, and it avoids the need for the verbose:
// EncodableValue(CustomEncodableValue(...)).
// NOLINTNEXTLINE(google-explicit-constructor)
EncodableValue(const CustomEncodableValue& v) : super(v) {}
// Override the conversion constructors from std::variant to make them
// explicit, to avoid implicit conversion.
// While implicit conversion can be convenient in some cases, it can have very
// surprising effects. E.g., calling a function that takes an EncodableValue
// but accidentally passing an EncodableValue* would, instead of failing to
// compile, go through a pointer->bool->EncodableValue(bool) chain and
// silently call the function with a temp-constructed EncodableValue(true).
template <class T>
constexpr explicit EncodableValue(T&& t) noexcept : super(t) {}
// Returns true if the value is null. Convenience wrapper since unlike the
// other types, std::monostate uses aren't self-documenting.
bool IsNull() const { return std::holds_alternative<std::monostate>(*this); }
// Convenience method to simplify handling objects received from Flutter
// where the values may be larger than 32-bit, since they have the same type
// on the Dart side, but will be either 32-bit or 64-bit here depending on
// the value.
// Calling this method if the value doesn't contain either an int32_t or an
// int64_t will throw an exception.
int64_t LongValue() const {
if (std::holds_alternative<int32_t>(*this)) {
return std::get<int32_t>(*this);
return std::get<int64_t>(*this);
} // namespace standard_message_codec