Files
scylla/utils/tagged_integer.hh
Benny Halevy a70b53b6e7 utils: tagged_integer: implement std::numeric_limits::{min,max}
Add add a respective unit test.

It turns out that numeric_limits defines an implicit implementation
for std::numeric_limits<utils::tagged_integer<Tag, ValueType>>
which apprently returns a default-constructed tagged_integer
for min() and max(), and this broke
`gms::heart_beat_state::force_highest_possible_version_unsafe()`
since 4cdad8bc8b
(merged in 7f04d8231d)

Implementing min/max correctly
Fixes #13801

Signed-off-by: Benny Halevy <bhalevy@scylladb.com>
2023-05-15 10:19:39 +03:00

117 lines
3.4 KiB
C++

/*
* Copyright 2020-present ScyllaDB
*/
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <cstdint>
#include <compare>
#include <iostream>
#include <type_traits>
namespace utils {
// Note: do not use directly, use utils::tagged_integer instead.
// The reason this double-tagged template exist
// is to distinguish between utils::tagged_integer
// and raft::internal::tagged_uint64 that have incompatible
// idl types and therefore must not be convertible to each other.
template <typename Final, typename Tag, std::integral ValueType>
class tagged_tagged_integer {
public:
using value_type = ValueType;
private:
value_type _value;
public:
tagged_tagged_integer() noexcept : _value(0) {}
explicit tagged_tagged_integer(value_type v) noexcept : _value(v) {}
tagged_tagged_integer& operator=(value_type v) noexcept {
_value = v;
return *this;
}
value_type value() const noexcept { return _value; }
operator value_type() const noexcept { return _value; }
explicit operator bool() const { return _value != 0; }
auto operator<=>(const tagged_tagged_integer& o) const = default;
tagged_tagged_integer& operator++() noexcept {
++_value;
return *this;
}
tagged_tagged_integer& operator--() noexcept {
--_value;
return *this;
}
tagged_tagged_integer operator++(int) noexcept {
auto ret = *this;
++_value;
return ret;
}
tagged_tagged_integer operator--(int) noexcept {
auto ret = *this;
--_value;
return ret;
}
tagged_tagged_integer operator+(const tagged_tagged_integer& o) const {
return tagged_tagged_integer(_value + o._value);
}
tagged_tagged_integer operator-(const tagged_tagged_integer& o) const {
return tagged_tagged_integer(_value - o._value);
}
tagged_tagged_integer& operator+=(const tagged_tagged_integer& o) {
_value += o._value;
return *this;
}
tagged_tagged_integer& operator-=(const tagged_tagged_integer& o) {
_value -= o._value;
return *this;
}
};
template <typename Tag, std::integral ValueType>
using tagged_integer = tagged_tagged_integer<struct final, Tag, ValueType>;
} // namespace utils
namespace std {
template <typename Final, typename Tag, std::integral ValueType>
struct hash<utils::tagged_tagged_integer<Final, Tag, ValueType>> {
size_t operator()(const utils::tagged_tagged_integer<Final, Tag, ValueType>& x) const noexcept {
return hash<ValueType>{}(x.value());
}
};
template <typename Final, typename Tag, std::integral ValueType>
[[maybe_unused]] ostream& operator<<(ostream& s, const utils::tagged_tagged_integer<Final, Tag, ValueType>& x) {
return s << x.value();
}
template <typename Final, typename Tag, std::integral ValueType>
struct numeric_limits<utils::tagged_tagged_integer<Final, Tag, ValueType>> : public numeric_limits<ValueType> {
using tagged_tagged_integer_t = utils::tagged_tagged_integer<Final, Tag, ValueType>;
using value_limits = numeric_limits<ValueType>;
static_assert(numeric_limits<ValueType>::is_specialized && numeric_limits<ValueType>::is_bounded);
static constexpr tagged_tagged_integer_t min() {
return tagged_tagged_integer_t(value_limits::min());
}
static constexpr tagged_tagged_integer_t max() {
return tagged_tagged_integer_t(value_limits::max());
}
};
} // namespace std