File: TaggedUnion.hpp

package info (click to toggle)
reflect-cpp 0.18.0%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 12,524 kB
  • sloc: cpp: 44,484; python: 131; makefile: 30; sh: 3
file content (146 lines) | stat: -rw-r--r-- 4,581 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#ifndef RFL_TAGGEDUNION_HPP_
#define RFL_TAGGEDUNION_HPP_

#include "Variant.hpp"
#include "define_literal.hpp"
#include "internal/Getter.hpp"
#include "internal/StringLiteral.hpp"
#include "internal/tag_t.hpp"

namespace rfl {

// https://serde.rs/enum-representations.html
template <internal::StringLiteral _discriminator, class... Ts>
struct TaggedUnion {
  static constexpr internal::StringLiteral discrimininator_ = _discriminator;

  /// The type of the underlying variant.
  using VariantType = rfl::Variant<Ts...>;

  TaggedUnion(const VariantType& _variant) : variant_(_variant) {}

  TaggedUnion(VariantType&& _variant) noexcept
      : variant_(std::move(_variant)) {}

  TaggedUnion(const TaggedUnion<_discriminator, Ts...>& _tagged_union) =
      default;

  TaggedUnion(TaggedUnion<_discriminator, Ts...>&& _tagged_union) noexcept =
      default;

  template <class T,
            typename std::enable_if<std::is_convertible_v<T, VariantType>,
                                    bool>::type = true>
  TaggedUnion(const T& _t) : variant_(_t) {}

  template <class T,
            typename std::enable_if<std::is_convertible_v<T, VariantType>,
                                    bool>::type = true>
  TaggedUnion(T&& _t) noexcept : variant_(std::forward<T>(_t)) {}

  ~TaggedUnion() = default;

  /// Assigns the underlying object.
  TaggedUnion<_discriminator, Ts...>& operator=(const VariantType& _variant) {
    variant_ = _variant;
    return *this;
  }

  /// Assigns the underlying object.
  TaggedUnion<_discriminator, Ts...>& operator=(VariantType&& _variant) {
    variant_ = std::move(_variant);
    return *this;
  }

  /// Assigns the underlying object.
  template <class T,
            typename std::enable_if<std::is_convertible_v<T, VariantType>,
                                    bool>::type = true>
  TaggedUnion<_discriminator, Ts...>& operator=(T&& _variant) {
    variant_ = std::forward<T>(_variant);
    return *this;
  }

  /// Assigns the underlying object.
  template <class T,
            typename std::enable_if<std::is_convertible_v<T, VariantType>,
                                    bool>::type = true>
  TaggedUnion<_discriminator, Ts...>& operator=(const T& _variant) {
    variant_ = _variant;
    return *this;
  }

  /// Assigns the underlying object.
  TaggedUnion<_discriminator, Ts...>& operator=(
      const TaggedUnion<_discriminator, Ts...>& _other) = default;

  /// Assigns the underlying object.
  TaggedUnion<_discriminator, Ts...>& operator=(
      TaggedUnion<_discriminator, Ts...>&& _other) = default;

  /// Returns the underlying variant.
  VariantType& variant() { return variant_; }

  /// Returns the underlying variant.
  const VariantType& variant() const { return variant_; }

  /// Applies function _f to all underlying alternatives.
  template <class F>
  auto visit(F&& _f)
      -> decltype(std::declval<VariantType>().visit(std::declval<F&&>())) {
    return variant_.visit(std::forward<F>(_f));
  }

  /// Applies function _f to all underlying alternatives.
  template <class F>
  auto visit(F&& _f) const
      -> decltype(std::declval<VariantType>().visit(std::declval<F&&>())) {
    return variant_.visit(std::forward<F>(_f));
  }

  /// The underlying variant - a TaggedUnion is a thin wrapper
  /// around a variant that is mainly used for parsing.
  VariantType variant_;
};

template <typename T>
concept TaggedUnionBased = requires(T t) {
  []<internal::StringLiteral _discriminator, typename... Args>(
      TaggedUnion<_discriminator, Args...> const&) {}(t);
};

template <class T>
struct PossibleTags;

template <internal::StringLiteral _discriminator, class... Ts>
struct PossibleTags<TaggedUnion<_discriminator, Ts...>> {
  using Type = define_literal_t<internal::tag_t<_discriminator, Ts>...>;
};

template <class T>
using possible_tags_t = typename PossibleTags<T>::Type;

template <internal::StringLiteral _discriminator, class... Ts>
bool operator==(
  const TaggedUnion<_discriminator, Ts...>& lhs,
  const TaggedUnion<_discriminator, Ts...>& rhs
  ) {

  return (lhs.variant().index() == rhs.variant().index()) &&
         lhs.variant().visit(
           [&rhs](const auto& l) {
               return rhs.variant().visit(
                 [&l](const auto& r) -> bool {
                   if constexpr (std::is_same_v<std::decay_t<decltype(l)>, std::decay_t<decltype(r)>>)
                     return l == r;
                   else
                     return false;
               }
             );
           }
         );
}

}  // namespace rfl

#endif  // RFL_TAGGEDUNION_HPP_