class {{export_attribute}} {{union.name}} {
 public:
  using DataView = {{union.name}}DataView;
  using Data_ = internal::{{union.name}}_Data;
  using Tag = Data_::{{union.name}}_Tag;

  static {{union.name}}Ptr New() {
    return {{union.name}}Ptr(base::in_place);
  }

{%-  for field in union.fields %}
  // Construct an instance holding |{{field.name}}|.
  static {{union.name}}Ptr
  New{{field.name|under_to_camel}}(
      {{field.kind|cpp_wrapper_param_type_new}} {{field.name}}) {
    auto result = {{union.name}}Ptr(base::in_place);
    result->set_{{field.name}}(std::move({{field.name}}));
    return result;
  }
{%- endfor %}

  template <typename U>
  static {{union.name}}Ptr From(const U& u) {
    return mojo::TypeConverter<{{union.name}}Ptr, U>::Convert(u);
  }

  template <typename U>
  U To() const {
    return mojo::TypeConverter<U, {{union.name}}>::Convert(*this);
  }

  {{union.name}}();
  ~{{union.name}}();

  // Clone() is a template so it is only instantiated if it is used. Thus, the
  // bindings generator does not need to know whether Clone() or copy
  // constructor/assignment are available for members.
  template <typename UnionPtrType = {{union.name}}Ptr>
  {{union.name}}Ptr Clone() const;

  // Equals() is a template so it is only instantiated if it is used. Thus, the
  // bindings generator does not need to know whether Equals() or == operator
  // are available for members.
  template <typename T,
            typename std::enable_if<std::is_same<
                T, {{union.name}}>::value>::type* = nullptr>
  bool Equals(const T& other) const;

{%- if union|is_hashable %}
  size_t Hash(size_t seed) const;
{%- endif %}

  Tag which() const {
    return tag_;
  }

{%  for field in union.fields %}
  bool is_{{field.name}}() const { return tag_ == Tag::{{field.name|upper}}; }

  {{field.kind|cpp_union_getter_return_type}} get_{{field.name}}() const {
    DCHECK(tag_ == Tag::{{field.name|upper}});
{%-   if field.kind|is_object_kind or
         field.kind|is_any_handle_or_interface_kind %}
    return *(data_.{{field.name}});
{%-   else %}
    return data_.{{field.name}};
{%-   endif %}
  }

  void set_{{field.name}}(
      {{field.kind|cpp_wrapper_param_type_new}} {{field.name}});
{%- endfor %}

  template <typename UserType>
  static mojo::Message SerializeAsMessage(UserType* input) {
    return mojo::internal::SerializeAsMessageImpl<
        {{union.name}}::DataView>(input);
  }

  template <typename UserType>
  static bool DeserializeFromMessage(mojo::Message input,
                                     UserType* output) {
    return mojo::internal::DeserializeImpl<{{union.name}}::DataView>(
        input.payload(), input.payload_num_bytes(),
        std::move(*input.mutable_handles()), output, Validate);
  }

 private:
  union Union_ {
    Union_() {}
    ~Union_() {}

{%- for field in union.fields %}
{%-   if field.kind|is_object_kind or
         field.kind|is_any_handle_or_interface_kind %}
    {{field.kind|cpp_wrapper_type}}* {{field.name}};
{%-   else %}
    {{field.kind|cpp_wrapper_type}} {{field.name}};
{%-   endif %}
{%- endfor %}
  };

  static bool Validate(const void* data,
                       mojo::internal::ValidationContext* validation_context);

  void DestroyActive();
  Tag tag_;
  Union_ data_;
};
