Commit e9c66c2f authored by Jan Wilken Dörrie's avatar Jan Wilken Dörrie Committed by Commit Bot

[base] Make StrongAlias constexpr

This change makes util::StrongAlias's APIs constexpr and adds small API
improvements, such as a mutable overload for value() and an explicit
operator bool for IdType.

Bug: 954080
Change-Id: I66fe9e407b9e2b6e64f6a0d8a5664b4573aa26bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2150431Reviewed-by: default avatarŁukasz Anforowicz <lukasza@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#759539}
parent 517d32ba
...@@ -62,7 +62,6 @@ class IdType : public StrongAlias<TypeMarker, WrappedType> { ...@@ -62,7 +62,6 @@ class IdType : public StrongAlias<TypeMarker, WrappedType> {
class Generator { class Generator {
public: public:
Generator() = default; Generator() = default;
~Generator() = default;
// Generates the next unique ID. // Generates the next unique ID.
IdType GenerateNextId() { return FromUnsafeValue(next_id_++); } IdType GenerateNextId() { return FromUnsafeValue(next_id_++); }
...@@ -76,15 +75,19 @@ class IdType : public StrongAlias<TypeMarker, WrappedType> { ...@@ -76,15 +75,19 @@ class IdType : public StrongAlias<TypeMarker, WrappedType> {
}; };
// Default-construct in the null state. // Default-construct in the null state.
IdType() : StrongAlias<TypeMarker, WrappedType>::StrongAlias(kInvalidValue) {} constexpr IdType()
: StrongAlias<TypeMarker, WrappedType>::StrongAlias(kInvalidValue) {}
bool is_null() const { return this->value() == kInvalidValue; } constexpr bool is_null() const { return this->value() == kInvalidValue; }
constexpr explicit operator bool() const { return !is_null(); }
// TODO(mpawlowski) Replace these with constructor/value() getter. The // TODO(mpawlowski) Replace these with constructor/value() getter. The
// conversions are safe as long as they're explicit (which is taken care of by // conversions are safe as long as they're explicit (which is taken care of by
// StrongAlias). // StrongAlias).
static IdType FromUnsafeValue(WrappedType value) { return IdType(value); } constexpr static IdType FromUnsafeValue(WrappedType value) {
WrappedType GetUnsafeValue() const { return this->value(); } return IdType(value);
}
constexpr WrappedType GetUnsafeValue() const { return this->value(); }
}; };
// Type aliases for convenience: // Type aliases for convenience:
......
...@@ -52,6 +52,28 @@ TEST(IdType, GeneratorWithBigUnsignedInvalidValue) { ...@@ -52,6 +52,28 @@ TEST(IdType, GeneratorWithBigUnsignedInvalidValue) {
} }
} }
TEST(IdType, EnsureConstexpr) {
using TestId = IdType32<class TestTag>;
// Test constructors.
static constexpr TestId kZero;
static constexpr auto kOne = TestId::FromUnsafeValue(1);
// Test getting the underlying value.
static_assert(kZero.value() == 0, "");
static_assert(kOne.value() == 1, "");
static_assert(kZero.GetUnsafeValue() == 0, "");
static_assert(kOne.GetUnsafeValue() == 1, "");
// Test is_null().
static_assert(kZero.is_null(), "");
static_assert(!kOne.is_null(), "");
// Test operator bool.
static_assert(!kZero, "");
static_assert(kOne, "");
}
class IdTypeSpecificValueTest : public ::testing::TestWithParam<int> { class IdTypeSpecificValueTest : public ::testing::TestWithParam<int> {
protected: protected:
FooId test_id() { return FooId::FromUnsafeValue(GetParam()); } FooId test_id() { return FooId::FromUnsafeValue(GetParam()); }
......
...@@ -66,35 +66,33 @@ namespace util { ...@@ -66,35 +66,33 @@ namespace util {
template <typename TagType, typename UnderlyingType> template <typename TagType, typename UnderlyingType>
class StrongAlias { class StrongAlias {
public: public:
StrongAlias() = default; constexpr StrongAlias() = default;
explicit StrongAlias(const UnderlyingType& v) : value_(v) {} constexpr explicit StrongAlias(const UnderlyingType& v) : value_(v) {}
explicit StrongAlias(UnderlyingType&& v) : value_(std::move(v)) {} constexpr explicit StrongAlias(UnderlyingType&& v) : value_(std::move(v)) {}
~StrongAlias() = default;
StrongAlias(const StrongAlias& other) = default; constexpr UnderlyingType& value() & { return value_; }
StrongAlias& operator=(const StrongAlias& other) = default; constexpr const UnderlyingType& value() const& { return value_; }
StrongAlias(StrongAlias&& other) = default; constexpr UnderlyingType&& value() && { return std::move(value_); }
StrongAlias& operator=(StrongAlias&& other) = default; constexpr const UnderlyingType&& value() const&& { return std::move(value_); }
const UnderlyingType& value() const { return value_; } constexpr explicit operator UnderlyingType() const { return value_; }
explicit operator UnderlyingType() const { return value_; }
bool operator==(const StrongAlias& other) const { constexpr bool operator==(const StrongAlias& other) const {
return value_ == other.value_; return value_ == other.value_;
} }
bool operator!=(const StrongAlias& other) const { constexpr bool operator!=(const StrongAlias& other) const {
return value_ != other.value_; return value_ != other.value_;
} }
bool operator<(const StrongAlias& other) const { constexpr bool operator<(const StrongAlias& other) const {
return value_ < other.value_; return value_ < other.value_;
} }
bool operator<=(const StrongAlias& other) const { constexpr bool operator<=(const StrongAlias& other) const {
return value_ <= other.value_; return value_ <= other.value_;
} }
bool operator>(const StrongAlias& other) const { constexpr bool operator>(const StrongAlias& other) const {
return value_ > other.value_; return value_ > other.value_;
} }
bool operator>=(const StrongAlias& other) const { constexpr bool operator>=(const StrongAlias& other) const {
return value_ >= other.value_; return value_ >= other.value_;
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <memory>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
...@@ -93,6 +94,11 @@ TYPED_TEST(StrongAliasTest, CanBeMoveConstructed) { ...@@ -93,6 +94,11 @@ TYPED_TEST(StrongAliasTest, CanBeMoveConstructed) {
FooAlias move_assigned; FooAlias move_assigned;
move_assigned = std::move(alias2); move_assigned = std::move(alias2);
EXPECT_EQ(move_assigned, FooAlias(GetExampleValue<TypeParam>(2))); EXPECT_EQ(move_assigned, FooAlias(GetExampleValue<TypeParam>(2)));
// Check that FooAlias is nothrow move constructible. This matters for
// performance when used in std::vectors.
static_assert(std::is_nothrow_move_constructible<FooAlias>::value,
"Error: Alias is not nothow move constructible");
} }
TYPED_TEST(StrongAliasTest, CanBeConstructedFromMoveOnlyType) { TYPED_TEST(StrongAliasTest, CanBeConstructedFromMoveOnlyType) {
...@@ -107,6 +113,25 @@ TYPED_TEST(StrongAliasTest, CanBeConstructedFromMoveOnlyType) { ...@@ -107,6 +113,25 @@ TYPED_TEST(StrongAliasTest, CanBeConstructedFromMoveOnlyType) {
EXPECT_EQ(*b.value(), GetExampleValue<TypeParam>(1)); EXPECT_EQ(*b.value(), GetExampleValue<TypeParam>(1));
} }
TYPED_TEST(StrongAliasTest, MutableValue) {
// Note, using a move-only unique_ptr to T:
using Ptr = std::unique_ptr<TypeParam>;
using FooAlias = StrongAlias<class FooTag, Ptr>;
FooAlias a(std::make_unique<TypeParam>());
FooAlias b(std::make_unique<TypeParam>());
EXPECT_TRUE(a.value());
EXPECT_TRUE(b.value());
// Check that both the mutable l-value and r-value overloads work and we can
// move out of the aliases.
{ Ptr ignore(std::move(a).value()); }
{ Ptr ignore(std::move(b.value())); }
EXPECT_FALSE(a.value());
EXPECT_FALSE(b.value());
}
TYPED_TEST(StrongAliasTest, CanBeWrittenToOutputStream) { TYPED_TEST(StrongAliasTest, CanBeWrittenToOutputStream) {
using FooAlias = StrongAlias<class FooTag, TypeParam>; using FooAlias = StrongAlias<class FooTag, TypeParam>;
...@@ -124,6 +149,11 @@ TYPED_TEST(StrongAliasTest, IsDefaultConstructible) { ...@@ -124,6 +149,11 @@ TYPED_TEST(StrongAliasTest, IsDefaultConstructible) {
using FooAlias = StrongAlias<class FooTag, TypeParam>; using FooAlias = StrongAlias<class FooTag, TypeParam>;
static_assert(std::is_default_constructible<FooAlias>::value, static_assert(std::is_default_constructible<FooAlias>::value,
"Should be possible to default-construct a StrongAlias."); "Should be possible to default-construct a StrongAlias.");
static_assert(
std::is_trivially_default_constructible<FooAlias>::value ==
std::is_trivially_default_constructible<TypeParam>::value,
"Should be possible to trivially default-construct a StrongAlias iff the "
"underlying type is trivially default constructible.");
} }
TEST(StrongAliasTest, TrivialTypeAliasIsStandardLayout) { TEST(StrongAliasTest, TrivialTypeAliasIsStandardLayout) {
...@@ -263,4 +293,28 @@ TYPED_TEST(StrongAliasTest, CanDifferentiateOverloads) { ...@@ -263,4 +293,28 @@ TYPED_TEST(StrongAliasTest, CanDifferentiateOverloads) {
EXPECT_EQ("BarAlias", Scope::Overload(BarAlias())); EXPECT_EQ("BarAlias", Scope::Overload(BarAlias()));
} }
TEST(StrongAliasTest, EnsureConstexpr) {
using FooAlias = StrongAlias<class FooTag, int>;
// Check constructors.
static constexpr FooAlias kZero{};
static constexpr FooAlias kOne(1);
// Check value().
static_assert(kZero.value() == 0, "");
static_assert(kOne.value() == 1, "");
// Check explicit conversions to underlying type.
static_assert(static_cast<int>(kZero) == 0, "");
static_assert(static_cast<int>(kOne) == 1, "");
// Check comparison operations.
static_assert(kZero == kZero, "");
static_assert(kZero != kOne, "");
static_assert(kZero < kOne, "");
static_assert(kZero <= kOne, "");
static_assert(kOne > kZero, "");
static_assert(kOne >= kZero, "");
}
} // namespace util } // namespace util
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment