blob: 7b00b3284c02bf30b52cfbd3a839a271f3b1f7e7 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Helper class for a strongly-typed multiple variant token. Allows creating
// a token that can represent one of a collection of distinct token types.
// It would be great to replace this with a much simpler C++17 std::variant
// when that is available.
#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_MULTI_TOKEN_H_
#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_MULTI_TOKEN_H_
#include <type_traits>
#include "base/unguessable_token.h"
#include "third_party/blink/public/common/tokens/multi_token_internal.h"
namespace blink {
// Defines MultiToken, which is effectively a variant over 2 or more
// instances of util::TokenType.
//
// A MultiToken<..> emulates a token like interface. When default constructed
// it will construct itself as an instance of |TokenVariant0|. Additionally it
// offers the following functions allowing casting and querying token types at
// runtime:
//
// // Determines whether this token stores an instance of a TokenType.
// bool Is<TokenType>() const;
//
// // Extracts the stored token in its original type. The stored token must
// // be of the provided type otherwise this will explode at runtime.
// const TokenType& GetAs<TokenType>() const;
//
// A variant must have at least 2 valid input types, but can have arbitrarily
// many. They must all be distinct, and they must all be instances of
// util::TokenType.
template <typename TokenVariant0,
typename TokenVariant1,
typename... TokenVariants>
class MultiToken : public internal::MultiTokenBase<TokenVariant0,
TokenVariant1,
TokenVariants...> {
public:
using Base =
internal::MultiTokenBase<TokenVariant0, TokenVariant1, TokenVariants...>;
// The total number of types.
static const uint32_t kVariantCount = Base::VariantCount::kValue;
// Default constructor. The resulting token will be a valid token of type
// TokenVariant0.
MultiToken() = default;
// Copy constructors.
MultiToken(const MultiToken& other) = default;
template <typename InputTokenType,
typename = typename std::enable_if<
Base::template ValidType<InputTokenType>::kValue>::type>
// NOLINTNEXTLINE(google-explicit-constructor)
MultiToken(const InputTokenType& input_token)
: value_(input_token.value()),
variant_index_(Base::template TypeIndex<InputTokenType>::kValue) {}
~MultiToken() = default;
// Assignment operators.
MultiToken& operator=(const MultiToken& other) = default;
template <typename InputTokenType,
typename = typename std::enable_if<
Base::template ValidType<InputTokenType>::kValue>::type>
MultiToken& operator=(const InputTokenType& input_token) {
value_ = input_token.value();
variant_index_ = Base::template TypeIndex<InputTokenType>::kValue;
return *this;
}
const base::UnguessableToken& value() const { return value_; }
uint32_t variant_index() const { return variant_index_; }
std::string ToString() const { return value().ToString(); }
// Type checking.
template <typename InputTokenType,
typename = typename std::enable_if<
Base::template ValidType<InputTokenType>::kValue>::type>
bool Is() const {
return variant_index_ == Base::template TypeIndex<InputTokenType>::kValue;
}
// Type conversion. Allows extracting the underlying token type. This should
// only be called for the actual type that is stored in this token. This can
// be checked by calling "Is<>" first.
template <typename InputTokenType,
typename = typename std::enable_if<
Base::template ValidType<InputTokenType>::kValue>::type>
InputTokenType GetAs() const {
CHECK(Is<InputTokenType>()) << "invalid token type cast";
// Type-punning via casting is undefined behaviour, so we return by value.
return InputTokenType(value_);
}
// Comparison with untyped tokens. Only compares the token value, ignoring the
// type.
int Compare(const base::UnguessableToken& other) const {
return Base::CompareImpl(value_, other);
}
bool operator<(const base::UnguessableToken& other) const {
return Compare(other) == -1;
}
bool operator==(const base::UnguessableToken& other) const {
return Compare(other) == 0;
}
bool operator!=(const base::UnguessableToken& other) const {
return Compare(other) != 0;
}
// Comparison with other MultiTokens. Compares by token, then type.
int Compare(const MultiToken& other) const {
return Base::CompareImpl(std::tie(value_, variant_index_),
std::tie(other.value_, other.variant_index_));
}
bool operator<(const MultiToken& other) const { return Compare(other) == -1; }
bool operator==(const MultiToken& other) const { return Compare(other) == 0; }
bool operator!=(const MultiToken& other) const { return Compare(other) != 0; }
// Comparison with individual typed tokens. Compares by token, then type.
template <typename InputTokenType,
typename = typename std::enable_if<
Base::template ValidType<InputTokenType>::kValue>::type>
int Compare(const InputTokenType& other) const {
static constexpr uint32_t kInputTokenTypeIndex =
Base::template TypeIndex<InputTokenType>::kValue;
return Base::CompareImpl(std::tie(value_, variant_index_),
std::tie(other.value(), kInputTokenTypeIndex));
}
template <typename InputTokenType,
typename = typename std::enable_if<
Base::template ValidType<InputTokenType>::kValue>::type>
bool operator<(const InputTokenType& other) const {
return Compare(other) == -1;
}
template <typename InputTokenType,
typename = typename std::enable_if<
Base::template ValidType<InputTokenType>::kValue>::type>
bool operator==(const InputTokenType& other) const {
return Compare(other) == 0;
}
template <typename InputTokenType,
typename = typename std::enable_if<
Base::template ValidType<InputTokenType>::kValue>::type>
bool operator!=(const InputTokenType& other) const {
return Compare(other) != 0;
}
// Hash functor for use in unordered containers.
struct Hasher {
using argument_type = MultiToken;
using result_type = size_t;
result_type operator()(const MultiToken& token) const {
return base::UnguessableTokenHash()(token.value_);
}
};
private:
// The underlying untyped token value. This will *never* be null initialized.
base::UnguessableToken value_ = base::UnguessableToken::Create();
// The index of the variant type that is currently stored in this token.
uint32_t variant_index_ = 0;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_MULTI_TOKEN_H_