fixed_point: Use variable templates and concepts where applicable

Makes a few things a little less noisy and removes the need for SFINAE
in quite a few functions.
This commit is contained in:
Lioncash 2022-10-18 12:54:53 -04:00
parent 8649c46c74
commit 5000d814af
2 changed files with 56 additions and 72 deletions

View File

@ -34,4 +34,12 @@ concept DerivedFrom = requires {
template <typename From, typename To> template <typename From, typename To>
concept ConvertibleTo = std::is_convertible_v<From, To>; concept ConvertibleTo = std::is_convertible_v<From, To>;
// No equivalents in the stdlib
template <typename T>
concept IsArithmetic = std::is_arithmetic_v<T>;
template <typename T>
concept IsIntegral = std::is_integral_v<T>;
} // namespace Common } // namespace Common

View File

@ -12,6 +12,8 @@
#include <ostream> #include <ostream>
#include <type_traits> #include <type_traits>
#include <common/concepts.h>
namespace Common { namespace Common {
template <size_t I, size_t F> template <size_t I, size_t F>
@ -50,8 +52,8 @@ struct type_from_size<64> {
static constexpr size_t size = 64; static constexpr size_t size = 64;
using value_type = int64_t; using value_type = int64_t;
using unsigned_type = std::make_unsigned<value_type>::type; using unsigned_type = std::make_unsigned_t<value_type>;
using signed_type = std::make_signed<value_type>::type; using signed_type = std::make_signed_t<value_type>;
using next_size = type_from_size<128>; using next_size = type_from_size<128>;
}; };
@ -61,8 +63,8 @@ struct type_from_size<32> {
static constexpr size_t size = 32; static constexpr size_t size = 32;
using value_type = int32_t; using value_type = int32_t;
using unsigned_type = std::make_unsigned<value_type>::type; using unsigned_type = std::make_unsigned_t<value_type>;
using signed_type = std::make_signed<value_type>::type; using signed_type = std::make_signed_t<value_type>;
using next_size = type_from_size<64>; using next_size = type_from_size<64>;
}; };
@ -72,8 +74,8 @@ struct type_from_size<16> {
static constexpr size_t size = 16; static constexpr size_t size = 16;
using value_type = int16_t; using value_type = int16_t;
using unsigned_type = std::make_unsigned<value_type>::type; using unsigned_type = std::make_unsigned_t<value_type>;
using signed_type = std::make_signed<value_type>::type; using signed_type = std::make_signed_t<value_type>;
using next_size = type_from_size<32>; using next_size = type_from_size<32>;
}; };
@ -83,8 +85,8 @@ struct type_from_size<8> {
static constexpr size_t size = 8; static constexpr size_t size = 8;
using value_type = int8_t; using value_type = int8_t;
using unsigned_type = std::make_unsigned<value_type>::type; using unsigned_type = std::make_unsigned_t<value_type>;
using signed_type = std::make_signed<value_type>::type; using signed_type = std::make_signed_t<value_type>;
using next_size = type_from_size<16>; using next_size = type_from_size<16>;
}; };
@ -101,7 +103,7 @@ struct divide_by_zero : std::exception {};
template <size_t I, size_t F> template <size_t I, size_t F>
constexpr FixedPoint<I, F> divide( constexpr FixedPoint<I, F> divide(
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
using next_type = typename FixedPoint<I, F>::next_type; using next_type = typename FixedPoint<I, F>::next_type;
using base_type = typename FixedPoint<I, F>::base_type; using base_type = typename FixedPoint<I, F>::base_type;
@ -121,7 +123,7 @@ constexpr FixedPoint<I, F> divide(
template <size_t I, size_t F> template <size_t I, size_t F>
constexpr FixedPoint<I, F> divide( constexpr FixedPoint<I, F> divide(
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
using unsigned_type = typename FixedPoint<I, F>::unsigned_type; using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
@ -191,7 +193,7 @@ constexpr FixedPoint<I, F> divide(
template <size_t I, size_t F> template <size_t I, size_t F>
constexpr FixedPoint<I, F> multiply( constexpr FixedPoint<I, F> multiply(
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
using next_type = typename FixedPoint<I, F>::next_type; using next_type = typename FixedPoint<I, F>::next_type;
using base_type = typename FixedPoint<I, F>::base_type; using base_type = typename FixedPoint<I, F>::base_type;
@ -210,7 +212,7 @@ constexpr FixedPoint<I, F> multiply(
template <size_t I, size_t F> template <size_t I, size_t F>
constexpr FixedPoint<I, F> multiply( constexpr FixedPoint<I, F> multiply(
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
using base_type = typename FixedPoint<I, F>::base_type; using base_type = typename FixedPoint<I, F>::base_type;
@ -270,10 +272,8 @@ public: // constructors
FixedPoint(FixedPoint&&) = default; FixedPoint(FixedPoint&&) = default;
FixedPoint& operator=(const FixedPoint&) = default; FixedPoint& operator=(const FixedPoint&) = default;
template <class Number> template <IsArithmetic Number>
constexpr FixedPoint( constexpr FixedPoint(Number n) : data_(static_cast<base_type>(n * one)) {}
Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
: data_(static_cast<base_type>(n * one)) {}
public: // conversion public: // conversion
template <size_t I2, size_t F2> template <size_t I2, size_t F2>
@ -411,15 +411,13 @@ public: // binary math operators, effects underlying bit pattern since these
return *this; return *this;
} }
template <class Integer, template <IsIntegral Integer>
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
constexpr FixedPoint& operator>>=(Integer n) { constexpr FixedPoint& operator>>=(Integer n) {
data_ >>= n; data_ >>= n;
return *this; return *this;
} }
template <class Integer, template <IsIntegral Integer>
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
constexpr FixedPoint& operator<<=(Integer n) { constexpr FixedPoint& operator<<=(Integer n) {
data_ <<= n; data_ <<= n;
return *this; return *this;
@ -490,10 +488,10 @@ public:
// if we have the same fractional portion, but differing integer portions, we trivially upgrade the // if we have the same fractional portion, but differing integer portions, we trivially upgrade the
// smaller type // smaller type
template <size_t I1, size_t I2, size_t F> template <size_t I1, size_t I2, size_t F>
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator+( constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+(
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
const T l = T::from_base(lhs.to_raw()); const T l = T::from_base(lhs.to_raw());
const T r = T::from_base(rhs.to_raw()); const T r = T::from_base(rhs.to_raw());
@ -501,10 +499,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
} }
template <size_t I1, size_t I2, size_t F> template <size_t I1, size_t I2, size_t F>
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator-( constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-(
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
const T l = T::from_base(lhs.to_raw()); const T l = T::from_base(lhs.to_raw());
const T r = T::from_base(rhs.to_raw()); const T r = T::from_base(rhs.to_raw());
@ -512,10 +510,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
} }
template <size_t I1, size_t I2, size_t F> template <size_t I1, size_t I2, size_t F>
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator*( constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*(
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
const T l = T::from_base(lhs.to_raw()); const T l = T::from_base(lhs.to_raw());
const T r = T::from_base(rhs.to_raw()); const T r = T::from_base(rhs.to_raw());
@ -523,10 +521,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
} }
template <size_t I1, size_t I2, size_t F> template <size_t I1, size_t I2, size_t F>
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator/( constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/(
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
const T l = T::from_base(lhs.to_raw()); const T l = T::from_base(lhs.to_raw());
const T r = T::from_base(rhs.to_raw()); const T r = T::from_base(rhs.to_raw());
@ -561,54 +559,46 @@ constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs)
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) { constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
lhs += FixedPoint<I, F>(rhs); lhs += FixedPoint<I, F>(rhs);
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) { constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
lhs -= FixedPoint<I, F>(rhs); lhs -= FixedPoint<I, F>(rhs);
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) { constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
lhs *= FixedPoint<I, F>(rhs); lhs *= FixedPoint<I, F>(rhs);
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) { constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
lhs /= FixedPoint<I, F>(rhs); lhs /= FixedPoint<I, F>(rhs);
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) { constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
FixedPoint<I, F> tmp(lhs); FixedPoint<I, F> tmp(lhs);
tmp += rhs; tmp += rhs;
return tmp; return tmp;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) { constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
FixedPoint<I, F> tmp(lhs); FixedPoint<I, F> tmp(lhs);
tmp -= rhs; tmp -= rhs;
return tmp; return tmp;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) { constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
FixedPoint<I, F> tmp(lhs); FixedPoint<I, F> tmp(lhs);
tmp *= rhs; tmp *= rhs;
return tmp; return tmp;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) { constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
FixedPoint<I, F> tmp(lhs); FixedPoint<I, F> tmp(lhs);
tmp /= rhs; tmp /= rhs;
@ -616,78 +606,64 @@ constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
} }
// shift operators // shift operators
template <size_t I, size_t F, class Integer, template <size_t I, size_t F, IsIntegral Integer>
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) { constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
lhs <<= rhs; lhs <<= rhs;
return lhs; return lhs;
} }
template <size_t I, size_t F, class Integer, template <size_t I, size_t F, IsIntegral Integer>
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) { constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
lhs >>= rhs; lhs >>= rhs;
return lhs; return lhs;
} }
// comparison operators // comparison operators
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
return lhs > FixedPoint<I, F>(rhs); return lhs > FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
return lhs < FixedPoint<I, F>(rhs); return lhs < FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
return lhs >= FixedPoint<I, F>(rhs); return lhs >= FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
return lhs <= FixedPoint<I, F>(rhs); return lhs <= FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
return lhs == FixedPoint<I, F>(rhs); return lhs == FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
return lhs != FixedPoint<I, F>(rhs); return lhs != FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) > rhs; return FixedPoint<I, F>(lhs) > rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) < rhs; return FixedPoint<I, F>(lhs) < rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) >= rhs; return FixedPoint<I, F>(lhs) >= rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) <= rhs; return FixedPoint<I, F>(lhs) <= rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) == rhs; return FixedPoint<I, F>(lhs) == rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) != rhs; return FixedPoint<I, F>(lhs) != rhs;
} }