186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler/*
286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * Copyright (C) 2016 The Android Open Source Project
386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler *
486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * Licensed under the Apache License, Version 2.0 (the "License");
586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * you may not use this file except in compliance with the License.
686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * You may obtain a copy of the License at
786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler *
886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler *      http://www.apache.org/licenses/LICENSE-2.0
986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler *
1086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * Unless required by applicable law or agreed to in writing, software
1186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * distributed under the License is distributed on an "AS IS" BASIS,
1286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * See the License for the specific language governing permissions and
1486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler * limitations under the License.
1586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler */
1686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
1786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// This file defines the |TaggedUnion| class template. It implements a tagged
1886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// union, i.e. it both holds a value of one of a set of pre-determined types, as
1986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// well as an enum value indicating which union member is active. The enum type
2086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// used as tag and the set of union member types a specified as template
2186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// parameters.
2286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
2386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// For example, consider this declaration of a simple variant type:
2486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
2586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   enum VariantType {
2686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     Variant_Int = 0,
2786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     Variant_Double = 1,
2886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     Variant_Boolean = 3,
2986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     Variant_String = 2,
3086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   };
3186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
3286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   using Variant = TaggedUnion<VariantType,
3386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//                        TaggedUnionMember<Variant_Int, int>,
3486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//                        TaggedUnionMember<Variant_Double, double>,
3586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//                        TaggedUnionMember<Variant_Boolean, bool>,
3686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//                        TaggedUnionMember<Variant_String, std::string>>;
3786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
3886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// |TaggedUnion::which()| can be used to determine what the active member is,
3986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// and |TaggedUnion::get()| returns a pointer to the member as long as that
4086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// member is active:
4186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
4286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   Variant value;
4386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   ASSERT_EQ(Variant_Int, value.which());
4486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   ASSERT_NE(nullptr, value.get<Variant_Int>());
4586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   ASSERT_EQ(0, *value.get<Variant_Int>());
4686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
4786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// |TaggedUnion::Activate()| activates a member. It returns a reference to the
4886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// activated member:
4986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
5086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   value.Activate<Variant_String>() = "-1";
5186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   ASSERT_EQ(Variant_String, value.which());
5286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
5386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// To allow generic code to process a |TaggedUnion|, you can use a variant of
5486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// what is commonly known as "the indices trick". The idea is to use template
5586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// parameter pack expansion on |TaggedUnion|'s |Member| parameter to invoke some
5686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// function for each member. For example, the following code determines the
5786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// integer value of a |Variant| value as declared above:
5886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
5986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   template <typename Type>
6086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   void MemberToInt(const Type* member, int* value) {
6186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     if (member) {
6286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//       *value = static_cast<int>(*member);
6386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     }
6486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   }
6586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
6686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   void MemberToInt(const std::string* member, int* value) {
6786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     if (member) {
6886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//       *value = std::stoi(*member);
6986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     }
7086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   }
7186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
7286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   template <typename... Member>
7386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   int VariantToInt(const TaggedUnion<VariantType, Member...>& variant) {
7486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     int value = 0;
7586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     int dummy[] = {
7686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//         (MemberToInt(
7786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//              variant.template get<static_cast<VariantType>(Member::kTag)>(),
7886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//              &value),
7986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//          0)...};
8086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     (void)dummy;
8186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//     return value;
8286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   };
8386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
8486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// Using this, you can convert a |Variant| in arbitrary state to an integer:
8586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//
8686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler//   ASSERT_EQ(-1, VariantToInt(value));
8786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
88a715cb1840f9a0c813c90707a351687f7a77950eMattias Nissler#ifndef NVRAM_MESSAGES_TAGGED_UNION_H_
89a715cb1840f9a0c813c90707a351687f7a77950eMattias Nissler#define NVRAM_MESSAGES_TAGGED_UNION_H_
9086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
9186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerextern "C" {
9286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler#include <stddef.h>
9386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler}
9486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
9586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler#include <new>
9686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
97a715cb1840f9a0c813c90707a351687f7a77950eMattias Nissler#include <nvram/messages/compiler.h>
9886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
9986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslernamespace nvram {
10086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
10186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <uint64_t tag, typename Member>
10286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerstruct TaggedUnionMember {
10386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  static constexpr uint64_t kTag = tag;
10486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  using Type = Member;
10586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler};
10686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
10786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <typename TagType, typename... Members>
10886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerclass TaggedUnion;
10986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
11086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslernamespace detail {
11186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
11286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// A compile-time maximum implementation.
11386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <size_t... Values>
11486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerstruct Max;
11586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
11686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <>
11786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerstruct Max<> {
11886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  static constexpr size_t value = 0;
11986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler};
12086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
12186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <size_t head, size_t... tail>
12286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerstruct Max<head, tail...> {
12386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  static constexpr size_t value =
12486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      head > Max<tail...>::value ? head : Max<tail...>::value;
12586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler};
12686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
12786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// A helper template that determines the |TaggedUnionMember| type corresponding
12886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// to |tag| via recursive expansion of the |Member| parameter list.
12986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <typename TagType, TagType tag, typename... Member>
13086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerstruct MemberForTag;
13186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
13286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <typename TagType,
13386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler          TagType tag,
13486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler          uint64_t member_tag,
13586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler          typename MemberType,
13686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler          typename... Tail>
13786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerstruct MemberForTag<TagType,
13886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler                    tag,
13986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler                    TaggedUnionMember<member_tag, MemberType>,
14086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler                    Tail...> {
14186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  using Type = typename MemberForTag<TagType, tag, Tail...>::Type;
14286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler};
14386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
14486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <typename TagType, TagType tag, typename MemberType, typename... Tail>
14586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerstruct MemberForTag<TagType,
14686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler                    tag,
14786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler                    TaggedUnionMember<static_cast<uint64_t>(tag), MemberType>,
14886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler                    Tail...> {
14986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  using Type = TaggedUnionMember<tag, MemberType>;
15086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler};
15186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
15286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler// Extracts the first element of its template parameter list.
15386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <typename Elem, typename...Tail>
15486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerstruct Head {
15586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  using Type = Elem;
15686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler};
15786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
15886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler}  // namespace detail
15986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
16086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslertemplate <typename TagType, typename... Member>
16186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nisslerclass TaggedUnion {
16286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler public:
16386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template <TagType tag>
16486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  struct MemberLookup {
16586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    using Type = typename detail::MemberForTag<TagType, tag, Member...>::Type;
16686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  };
16786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
16886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // Construct a |TaggedUnion| object. Note that the constructor will activate
16986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // the first declared union member.
17086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  TaggedUnion() {
17186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    Construct<static_cast<TagType>(detail::Head<Member...>::Type::kTag)>();
17286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
17386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
17486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  ~TaggedUnion() {
17586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    Destroy();
17686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
17786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
17886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // |TaggedUnion| is copyable and movable, provided the members have suitable
17986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // copy and move assignment operators.
18086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  TaggedUnion(const TaggedUnion<TagType, Member...>& other) {
18186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    CopyFrom(other);
18286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
18386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  TaggedUnion(TaggedUnion<TagType, Member...>&& other) {
18486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    MoveFrom(other);
18586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
18686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  TaggedUnion<TagType, Member...>& operator=(
18786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      const TaggedUnion<TagType, Member...>& other) {
18886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    CopyFrom(other);
18986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
19086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  TaggedUnion<TagType, Member...>& operator=(
19186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      TaggedUnion<TagType, Member...>&& other) {
19286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    MoveFrom(other);
19386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
19486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
19586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // Returns the tag value corresponding to the active member.
19686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  TagType which() const { return which_; }
19786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
19886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // Get a pointer to the member corresponding to |tag|. Returns nullptr if
19986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // |tag| doesn't correspond to the active member.
20086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template <TagType tag>
20186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  const typename MemberLookup<tag>::Type::Type* get() const {
20286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    return which_ == tag ? GetUnchecked<tag>() : nullptr;
20386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
20486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
20586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // Get a pointer to the member corresponding to |tag|. Returns nullptr if
20686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // |tag| doesn't correspond to the active member.
20786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template <TagType tag>
20886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  typename MemberLookup<tag>::Type::Type* get() {
20986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    return which_ == tag ? GetUnchecked<tag>() : nullptr;
21086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
21186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
21286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // Activate the member identified by |tag|. First, the currently active member
21386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // will be destroyed. Then, the member corresponding to |tag| will be
21486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // constructed (i.e. value-initialized). Returns a reference to the activated
21586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // member.
21686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template <TagType tag>
21786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  typename MemberLookup<tag>::Type::Type& Activate() {
21886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    Destroy();
21986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    Construct<tag>();
22086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    return *GetUnchecked<tag>();
22186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
22286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
22386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler private:
22486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template<TagType tag>
22586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  const typename MemberLookup<tag>::Type::Type* GetUnchecked() const {
22686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    return reinterpret_cast<const typename MemberLookup<tag>::Type::Type*>(
22786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler        storage_);
22886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
22986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
23086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template<TagType tag>
23186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  typename MemberLookup<tag>::Type::Type* GetUnchecked() {
23286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    return reinterpret_cast<typename MemberLookup<tag>::Type::Type*>(storage_);
23386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
23486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
23586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template <TagType tag>
23686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  void Construct() {
23786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    using MemberType = typename MemberLookup<tag>::Type::Type;
23886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    new (storage_) MemberType();
23986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    which_ = tag;
24086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
24186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
24286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template <typename CurrentMember>
24386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  void DestroyMember() {
24486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    using MemberType = typename CurrentMember::Type;
24586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    if (CurrentMember::kTag == which_) {
24686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      GetUnchecked<static_cast<TagType>(CurrentMember::kTag)>()->~MemberType();
24786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    }
24886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
24986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
25086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // This is marked noinline to prevent bloat due to the compiler inlining
25186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // |Destroy()| into each instance of the |Activate()| function.
25286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  NVRAM_NOINLINE void Destroy() {
25386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    int dummy[] = {(DestroyMember<Member>(), 0)...};
25486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    (void)dummy;
25586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
25686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
25786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template <typename CurrentMember>
25886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  void CopyMember(const typename CurrentMember::Type* member) {
25986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    if (member) {
26086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      if (CurrentMember::kTag != which_) {
26186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler        Activate<CurrentMember::kTag>();
26286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      }
26386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      *GetUnchecked<static_cast<TagType>(CurrentMember::kTag)>() = *member;
26486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    }
26586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
26686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
26786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  NVRAM_NOINLINE void CopyFrom(const TaggedUnion<TagType, Member...>& other) {
26886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    int dummy[] = {
26986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler        (CopyMember<Member>(
27086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler             other.template get<static_cast<TagType>(Member::kTag)>()),
27186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler         0)...};
27286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    (void)dummy;
27386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
27486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
27586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  template <typename CurrentMember>
27686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  void MoveMember(const typename CurrentMember::Type* member) {
27786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    if (member) {
27886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      if (CurrentMember::kTag != which_) {
27986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler        Activate<CurrentMember::kTag>();
28086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      }
28186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      *GetUnchecked<static_cast<TagType>(CurrentMember::kTag)>() =
28286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler          static_cast<typename CurrentMember::Type&&>(*member);
28386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    }
28486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
28586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
28686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  NVRAM_NOINLINE void MoveFrom(const TaggedUnion<TagType, Member...>& other) {
28786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    int dummy[] = {
28886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler        (MoveMember<Member>(
28986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler             other.template get<static_cast<TagType>(Member::kTag)>()),
29086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler         0)...};
29186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler    (void)dummy;
29286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  }
29386e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
29486e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // The + 0 is required to work around a G++ bug:
29586e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55382
29686e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  alignas(detail::Max<alignof(typename Member::Type)...>::value + 0)
29786e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler      uint8_t storage_[detail::Max<sizeof(typename Member::Type)...>::value];
29886e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler  TagType which_;
29986e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler};
30086e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
30186e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler}  // namespace nvram
30286e84547d37d7d32a5444756f839e11d81c9fc2dMattias Nissler
303a715cb1840f9a0c813c90707a351687f7a77950eMattias Nissler#endif  // NVRAM_MESSAGES_TAGGED_UNION_H_
304