forked from LeenkxTeam/Kmake
121 lines
3.8 KiB
C
121 lines
3.8 KiB
C
|
|
// Copyright 2023 the V8 project authors. All rights reserved.
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#ifndef V8_BASE_DISCRIMINATED_UNION_H_
|
||
|
|
#define V8_BASE_DISCRIMINATED_UNION_H_
|
||
|
|
|
||
|
|
#include <type_traits>
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include "src/base/compiler-specific.h"
|
||
|
|
#include "src/base/template-utils.h"
|
||
|
|
|
||
|
|
namespace v8 {
|
||
|
|
namespace base {
|
||
|
|
|
||
|
|
// A variant-like discriminated union type, which takes a discriminating enum
|
||
|
|
// and a set of types. The enum must have as many elements as the number of
|
||
|
|
// types, with each enum value corresponding to one type in the set.
|
||
|
|
//
|
||
|
|
// Example usage:
|
||
|
|
//
|
||
|
|
// enum class FooType {
|
||
|
|
// kBar,
|
||
|
|
// kBaz
|
||
|
|
// }
|
||
|
|
// class Bar { ... };
|
||
|
|
// class Baz { ... };
|
||
|
|
//
|
||
|
|
// // FooType::kBar and FooType::kBaz match Bar and Baz, respectively.
|
||
|
|
// DiscriminatedUnion<FooType, Bar, Baz> union;
|
||
|
|
//
|
||
|
|
// switch (union.tag()) {
|
||
|
|
// case FooType::kBar:
|
||
|
|
// return process_bar(union.get<FooType::kBar>);
|
||
|
|
// case FooType::kBaz:
|
||
|
|
// return process_baz(union.get<FooType::kBaz>);
|
||
|
|
// }
|
||
|
|
template <typename TagEnum, typename... Ts>
|
||
|
|
class DiscriminatedUnion {
|
||
|
|
public:
|
||
|
|
// All Ts must be trivially destructible to avoid DiscriminatedUnion needing a
|
||
|
|
// destructor.
|
||
|
|
static_assert((std::is_trivially_destructible_v<Ts> && ...));
|
||
|
|
|
||
|
|
using Tag = TagEnum;
|
||
|
|
|
||
|
|
DiscriminatedUnion(DiscriminatedUnion&& other) V8_NOEXCEPT = default;
|
||
|
|
DiscriminatedUnion(const DiscriminatedUnion& other) V8_NOEXCEPT = default;
|
||
|
|
DiscriminatedUnion& operator=(DiscriminatedUnion&& other)
|
||
|
|
V8_NOEXCEPT = default;
|
||
|
|
DiscriminatedUnion& operator=(const DiscriminatedUnion& other)
|
||
|
|
V8_NOEXCEPT = default;
|
||
|
|
|
||
|
|
// TODO(leszeks): Add in-place constructor.
|
||
|
|
|
||
|
|
// Construct with known tag and type (the tag is DCHECKed).
|
||
|
|
template <typename T>
|
||
|
|
constexpr explicit DiscriminatedUnion(Tag tag, T&& data) V8_NOEXCEPT {
|
||
|
|
constexpr size_t index = index_of_type_v<std::decay_t<T>, Ts...>;
|
||
|
|
static_assert(index < sizeof...(Ts));
|
||
|
|
static_assert(index < std::numeric_limits<uint8_t>::max());
|
||
|
|
// TODO(leszeks): Support unions with repeated types.
|
||
|
|
DCHECK_EQ(tag, static_cast<Tag>(index));
|
||
|
|
tag_ = static_cast<uint8_t>(index);
|
||
|
|
new (data_) T(std::forward<T>(data));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Construct with known type.
|
||
|
|
template <typename T>
|
||
|
|
constexpr explicit DiscriminatedUnion(T&& data) V8_NOEXCEPT {
|
||
|
|
constexpr size_t index = index_of_type_v<std::decay_t<T>, Ts...>;
|
||
|
|
static_assert(index < sizeof...(Ts));
|
||
|
|
static_assert(index < std::numeric_limits<uint8_t>::max());
|
||
|
|
tag_ = static_cast<uint8_t>(index);
|
||
|
|
new (data_) T(std::forward<T>(data));
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr Tag tag() const { return static_cast<Tag>(tag_); }
|
||
|
|
|
||
|
|
// Get union member by tag.
|
||
|
|
template <Tag tag>
|
||
|
|
constexpr const auto& get() const {
|
||
|
|
using T = nth_type_t<static_cast<size_t>(tag), Ts...>;
|
||
|
|
DCHECK_EQ(tag, this->tag());
|
||
|
|
return *reinterpret_cast<const T*>(data_);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get union member by tag.
|
||
|
|
template <Tag tag>
|
||
|
|
constexpr auto& get() {
|
||
|
|
using T = nth_type_t<static_cast<size_t>(tag), Ts...>;
|
||
|
|
DCHECK_EQ(tag, this->tag());
|
||
|
|
return *reinterpret_cast<T*>(data_);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get union member by type.
|
||
|
|
template <typename T>
|
||
|
|
constexpr const auto& get() const {
|
||
|
|
DCHECK_EQ(static_cast<Tag>(index_of_type_v<T, Ts...>), this->tag());
|
||
|
|
return *reinterpret_cast<const T*>(data_);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get union member by type.
|
||
|
|
template <typename T>
|
||
|
|
constexpr auto& get() {
|
||
|
|
DCHECK_EQ(static_cast<Tag>(index_of_type_v<T, Ts...>), this->tag());
|
||
|
|
return *reinterpret_cast<T*>(data_);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
alignas(std::max({alignof(Ts)...})) char data_[std::max({sizeof(Ts)...})];
|
||
|
|
static_assert(sizeof...(Ts) <= std::numeric_limits<uint8_t>::max());
|
||
|
|
uint8_t tag_;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace base
|
||
|
|
} // namespace v8
|
||
|
|
|
||
|
|
#endif // V8_BASE_DISCRIMINATED_UNION_H_
|