Skip to content

Commit

Permalink
Add IntegerNumeric vs IntegerPointer concepts
Browse files Browse the repository at this point in the history
This allows code to concept-wise constrain code to integers that represent numbers
vs those that represent pointers when that matters. Usually it doesn't matter as
they share a lot of common API but sometimes it does. uptr has functions and
conversions that other integers don't, and vice versa.

Closes #379.
  • Loading branch information
danakj committed Sep 23, 2023
1 parent e7a41ac commit 183f0a6
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 30 deletions.
24 changes: 8 additions & 16 deletions sus/iter/__private/step.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,50 +21,42 @@

namespace sus::iter::__private {

template <::sus::num::Integer T>
requires(!std::same_as<T, ::sus::num::uptr>)
template <::sus::num::IntegerNumeric T>
constexpr T step_max() noexcept {
return T::MAX;
}
template <::sus::num::Integer T>
requires(!std::same_as<T, ::sus::num::uptr>)
template <::sus::num::IntegerNumeric T>
constexpr T step_forward(T l) noexcept {
// SAFETY: All `Integer` can hold `1`.
return l + T::try_from(1).unwrap_unchecked(::sus::marker::unsafe_fn);
}
template <::sus::num::Integer T>
requires(!std::same_as<T, ::sus::num::uptr>)
template <::sus::num::IntegerNumeric T>
constexpr T step_backward(T l) noexcept {
// SAFETY: All `Integer` can hold `1`.
return l - T::try_from(1).unwrap_unchecked(::sus::marker::unsafe_fn);
}
template <::sus::num::Integer T>
requires(!std::same_as<T, ::sus::num::uptr>)
template <::sus::num::IntegerNumeric T>
constexpr ::sus::Option<::sus::num::usize> steps_between(const T& l,
const T& r) noexcept {
return r.checked_sub(l).and_then(
[](T steps) { return ::sus::num::usize::try_from(steps).ok(); });
}

template <::sus::num::Integer T>
requires(std::same_as<T, ::sus::num::uptr>)
template <::sus::num::IntegerPointer T>
constexpr T step_max() noexcept {
// TODO: This is dumb, so maybe uptr::MAX should exist?
// https://github.com/chromium/subspace/issues/238#issuecomment-1730736193
return T::MAX_BIT_PATTERN;
}
template <::sus::num::Integer T>
requires(std::same_as<T, ::sus::num::uptr>)
template <::sus::num::IntegerPointer T>
constexpr T step_forward(T l) noexcept {
return l + usize(1u);
}
template <::sus::num::Integer T>
requires(std::same_as<T, ::sus::num::uptr>)
template <::sus::num::IntegerPointer T>
constexpr T step_backward(T l) noexcept {
return l - usize(1u);
}
template <::sus::num::Integer T>
requires(std::same_as<T, ::sus::num::uptr>)
template <::sus::num::IntegerPointer T>
constexpr ::sus::Option<::sus::num::usize> steps_between(const T& l,
const T& r) noexcept {
return r.checked_sub(l).and_then(
Expand Down
74 changes: 60 additions & 14 deletions sus/num/integer_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,64 @@

namespace sus::num {

/// Unsigned Subspace integer types (u8, u16, u32, etc).
/// Unsigned Subspace numeric integer types. All of [`Unsigned`](
/// $sus::num::Unsigned) but excluding [`uptr`]($sus::num::uptr).
///
/// The [`uptr`]($sus::num::uptr) type is an integer but has a different API
/// that restricts how its used compared to other integer types. This can be
/// used to exclude it when it does not fit with a use case.
template <class T>
concept Unsigned =
concept UnsignedNumeric =
std::same_as<u8, T> || std::same_as<u16, T> || std::same_as<u32, T> ||
std::same_as<u64, T> || std::same_as<usize, T> || std::same_as<uptr, T>;
std::same_as<u64, T> || std::same_as<usize, T>;

/// Unsigned Subspace pointer integer types. This is the rest of [`Unsigned`](
/// $sus::num::Unsigned) that is not included in [`UnsignedNumeric`](
/// $sus::num::UnsignedNumeric), which is just [`uptr`]($sus::num::uptr).
///
/// The [`uptr`]($sus::num::uptr) type is an integer but has a different API
/// that interacts with pointers in ways numeric integers can not. This can be
/// used to exclude numeric integers when they do not fit with a use case.
template <class T>
concept UnsignedPointer = std::same_as<uptr, T>;

/// Unsigned Subspace integer types: [`u8`]($sus::num::u8),
/// [`u16`]($sus::num::u16), [`u32`]($sus::num::u32), [`u64`]($sus::num::u64),
/// [`usize`]($sus::num::usize), and [`uptr`]($sus::num::uptr).
template <class T>
concept Unsigned = UnsignedNumeric<T> || UnsignedPointer<T>;

/// Signed Subspace integer types (i8, i16, i32, etc).
/// Signed Subspace integer types: [`i8`]($sus::num::i8),
/// [`i16`]($sus::num::i16), [`i32`]($sus::num::i32), [`i64`]($sus::num::i64),
/// and [`isize`]($sus::num::isize).
template <class T>
concept Signed =
std::same_as<i8, T> || std::same_as<i16, T> || std::same_as<i32, T> ||
std::same_as<i64, T> || std::same_as<isize, T>;

/// Signed or unsigned Subspace integer types (i8, u16, i32, u64, etc).
/// All Subspace numeric integer types. This includes all safe integer types
/// except [`uptr`]($sus::num::uptr) which represents pointers. See
/// [`UnsignedNumeric`]($sus::num::UnsignedNumeric) and
/// [`UnsignedPointer`]($sus::num::UnsignedPointer).
template <class T>
concept IntegerNumeric = UnsignedNumeric<T> || Signed<T>;

/// All Subspace pointer integer types. This includes safe integer types that
/// represent pointer values, which is just [`uptr`]($sus::num::uptr). See
/// [`UnsignedNumeric`]($sus::num::UnsignedNumeric) and
/// [`UnsignedPointer`]($sus::num::UnsignedPointer).
template <class T>
concept IntegerPointer = UnsignedPointer<T>;

/// Signed or unsigned Subspace integer types: [`u8`]($sus::num::u8),
/// [`u16`]($sus::num::u16), [`u32`]($sus::num::u32), [`u64`]($sus::num::u64),
/// [`usize`]($sus::num::usize), [`uptr`]($sus::num::uptr),
/// [`i8`]($sus::num::i8), [`i16`]($sus::num::i16), [`i32`]($sus::num::i32),
/// [`i64`]($sus::num::i64), and [`isize`]($sus::num::isize).
template <class T>
concept Integer = Unsigned<T> || Signed<T>;
concept Integer = IntegerNumeric<T> || IntegerPointer<T>;

/// Unsigned primitive integer types (unsigned char, unsigned int, etc).
/// Unsigned primitive integer types (`unsigned char`, `unsigned int`, etc).
template <class T>
concept UnsignedPrimitiveInteger =
std::same_as<size_t, T> || std::same_as<uintptr_t, T> ||
Expand All @@ -53,15 +94,15 @@ concept UnsignedPrimitiveInteger =
std::same_as<unsigned int, T> || std::same_as<unsigned long, T> ||
std::same_as<unsigned long long, T>;

/// Signed primitive integer types (char, int, long, etc).
/// Signed primitive integer types (`signed char`, `int`, `long`, etc).
template <class T>
concept SignedPrimitiveInteger =
(!std::is_unsigned_v<char> && std::same_as<char, T>) ||
std::same_as<signed char, T> || std::same_as<short, T> ||
std::same_as<int, T> || std::same_as<long, T> || std::same_as<long long, T>;

/// Signed or unsigned primitive integer types (char, int, unsigned int,
/// unsigned long, etc).
/// Signed or unsigned primitive integer types (`char`, `int`, `unsigned int`,
/// `unsigned long`, etc).
template <class T>
concept PrimitiveInteger =
UnsignedPrimitiveInteger<T> || SignedPrimitiveInteger<T>;
Expand All @@ -84,20 +125,25 @@ concept SignedPrimitiveEnum =
template <class T>
concept PrimitiveEnum = UnsignedPrimitiveEnum<T> || SignedPrimitiveEnum<T>;

/// Enum class (scoped enum) types that are backed by an unsigned value.
/// Enum class
/// ([scoped enumeration](https://en.cppreference.com/w/cpp/language/enum))
/// types that are backed by an unsigned value.
template <class T>
concept UnsignedPrimitiveEnumClass =
!UnsignedPrimitiveEnum<T> && std::is_enum_v<T> &&
UnsignedPrimitiveInteger<std::underlying_type_t<T>>;

/// Enum class (scoped enum) types that are backed by a signed value.
/// Enum class
/// ([scoped enumeration](https://en.cppreference.com/w/cpp/language/enum))
/// types that are backed by a signed value.
template <class T>
concept SignedPrimitiveEnumClass =
!SignedPrimitiveEnum<T> && std::is_enum_v<T> &&
SignedPrimitiveInteger<std::underlying_type_t<T>>;

/// Enum class (scoped enum) types that are backed by a signed or unsigned
/// value.
/// Enum class
/// ([scoped enumeration](https://en.cppreference.com/w/cpp/language/enum))
/// types that are backed by a signed or unsigned value.
template <class T>
concept PrimitiveEnumClass =
UnsignedPrimitiveEnumClass<T> || SignedPrimitiveEnumClass<T>;
Expand Down

0 comments on commit 183f0a6

Please sign in to comment.