bsnes/nall/primitives/operators.hpp

175 lines
10 KiB
C++
Raw Normal View History

Update to v106r77 release. byuu says: So this turned out to be a rather unproductive ten-hour rabbit hole, but ... I reworked nall/primitives.hpp a lot. And because the changes are massive, testing of this WIP for regressions is critically important. I really can't stress that enough, we're almost certainly going to have some hidden regressions here ... We now have a nall/primitives/ subfolder that splits up the classes into manageable components. The bit-field support is now shared between both Natural and Integer. All of the assignment operator overloads are now templated and take references instead of values. Things like the GSU::Register class are non-copyable on account of the function<> object inside of it, and previously only operator= would work with classes like that. The big change is nall/primitives/operators.hpp, which is a really elaborate system to compute the minimum number of bits needed for any operation, and to return a Natural<T> or Integer<T> when one or both of the arguments are such a type. Unfortunately, it doesn't really work yet ... Kirby's Dream Land 3 breaks if we include operators.hpp. Zelda 3 runs fine with this, but I had to make a huge amount of core changes, including introducing a new ternary(bool, lhs, rhs) function to nall/algorithm to get past Natural<X> and Natural<Y> not being equivalent (is_integral types get a special exemption to ternary ?: type equivalence, yet it's impossible to simulate with our own classes, which is bullshit.) The horrifying part is that ternary() will evaluate both lhs and rhs, unlike ?: I converted some of the functions to test ? uint(x) : uint(y), and others to ternary(test, x, y) ... I don't have a strong preference either way yet. But the part where things may have gotten broken is in the changes to where ternary() was placed. Some cases like in the GBA PPU renderer, it was rather unclear the order of evaluations, so I may have made a mistake somewhere. So again, please please test this if you can. Or even better, look over the diff. Longer-term, I'd really like the enable nall/primitives/operators.hpp, but right now I'm not sure why Kirby's Dream Land 3 is breaking. Help would be appreciated, but ... it's gonna be really complex and difficult to debug, so I'm probably gonna be on my own here ... sigh.
2019-01-13 06:25:14 +00:00
#pragma once
namespace nall {
//attempt to keep Natural / Integer types when performing a math operation against another Natural / Integer or primitive type
//try not to lose any data through truncation (eg Natural<32> + Natural<32> -> Natural<64>)
//complex SFINAE is needed to prevent the creation of ambiguous operator overloads
#define Type PrimitiveType
#define Compatible PrimitiveCompatible
#define CastExpand PrimitiveCastExpand
#define CastShrink PrimitiveCastShrink
#define CastDouble PrimitiveCastDouble
#define CastLarger PrimitiveCastLarger
#define CastLesser PrimitiveCastLesser
template<typename T> struct Type {
using type = T;
static inline constexpr uint bits = 8 * sizeof(T);
};
template<> struct Type<Boolean> {
using type = typename Boolean::type;
static inline constexpr uint bits = Boolean::bits();
};
template<int Precision> struct Type<Natural<Precision>> {
using type = typename Natural<Precision>::type;
static inline constexpr uint bits = Natural<Precision>::bits();
};
template<int Precision> struct Type<Integer<Precision>> {
using type = typename Integer<Precision>::type;
static inline constexpr uint bits = Integer<Precision>::bits();
};
template<int Precision> struct Type<Real<Precision>> {
using type = typename Real<Precision>::type;
static inline constexpr uint bits = Real<Precision>::bits();
};
//compatible: SFINAE operator enable
template<typename LHS, typename RHS, typename = void> struct Compatible { static const bool value = false; };
template<int LHS, typename RHS> struct Compatible<Natural<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { static const bool value = true; };
template<typename LHS, int RHS> struct Compatible<LHS, Natural<RHS>, enable_if_t<is_integral_v<LHS>>> { static const bool value = true; };
template<int LHS, int RHS> struct Compatible<Natural<LHS>, Natural<RHS>> { static const bool value = true; };
template<int LHS, typename RHS> struct Compatible<Integer<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { static const bool value = true; };
template<typename LHS, int RHS> struct Compatible<LHS, Integer<RHS>, enable_if_t<is_integral_v<LHS>>> { static const bool value = true; };
template<int LHS, int RHS> struct Compatible<Integer<LHS>, Integer<RHS>> { static const bool value = true; };
//expand: LHS+RHS bits
template<typename LHS, typename RHS, typename = void> struct CastExpand { using type = void; };
template<int LHS, typename RHS> struct CastExpand<Natural<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Natural<LHS + Type<RHS>::bits>; };
template<typename LHS, int RHS> struct CastExpand<LHS, Natural<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Natural<Type<LHS>::bits + RHS>; };
template<int LHS, int RHS> struct CastExpand<Natural<LHS>, Natural<RHS>> { using type = Natural<LHS + RHS>; };
template<int LHS, typename RHS> struct CastExpand<Integer<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Integer<LHS + Type<RHS>::bits>; };
template<typename LHS, int RHS> struct CastExpand<LHS, Integer<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Integer<Type<LHS>::bits + RHS>; };
template<int LHS, int RHS> struct CastExpand<Integer<LHS>, Integer<RHS>> { using type = Integer<LHS + RHS>; };
//shrink: LHS-RHS bits
template<typename LHS, typename RHS, typename = void> struct CastShrink { using type = void; };
template<int LHS, typename RHS> struct CastShrink<Natural<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Natural<LHS - Type<RHS>::bits>; };
template<typename LHS, int RHS> struct CastShrink<LHS, Natural<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Natural<Type<LHS>::bits - RHS>; };
template<int LHS, int RHS> struct CastShrink<Natural<LHS>, Natural<RHS>> { using type = Natural<LHS - RHS>; };
template<int LHS, typename RHS> struct CastShrink<Integer<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Integer<LHS - Type<RHS>::bits>; };
template<typename LHS, int RHS> struct CastShrink<LHS, Integer<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Integer<Type<LHS>::bits - RHS>; };
template<int LHS, int RHS> struct CastShrink<Integer<LHS>, Integer<RHS>> { using type = Integer<LHS - RHS>; };
//double: 1+max(LHS,RHS) bits
template<typename LHS, typename RHS, typename = void> struct CastDouble { using type = void; };
template<int LHS, typename RHS> struct CastDouble<Natural<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Natural<1 + (LHS >= Type<RHS>::bits ? LHS : Type<RHS>::bits)>; };
template<typename LHS, int RHS> struct CastDouble<LHS, Natural<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Natural<1 + (Type<LHS>::bits >= RHS ? Type<LHS>::bits : RHS)>; };
template<int LHS, int RHS> struct CastDouble<Natural<LHS>, Natural<RHS>> { using type = Natural<1 + (LHS >= RHS ? LHS : RHS)>; };
template<int LHS, typename RHS> struct CastDouble<Integer<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Integer<1 + (LHS >= Type<RHS>::bits ? LHS : Type<RHS>::bits)>; };
template<typename LHS, int RHS> struct CastDouble<LHS, Integer<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Integer<1 + (Type<LHS>::bits >= RHS ? Type<LHS>::bits : RHS)>; };
template<int LHS, int RHS> struct CastDouble<Integer<LHS>, Integer<RHS>> { using type = Integer<1 + (LHS >= RHS ? LHS : RHS)>; };
//larger: max(LHS,RHS) bits
template<typename LHS, typename RHS, typename = void> struct CastLarger { using type = void; };
template<int LHS, typename RHS> struct CastLarger<Natural<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Natural<LHS >= Type<RHS>::bits ? LHS : Type<RHS>::bits>; };
template<typename LHS, int RHS> struct CastLarger<LHS, Natural<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Natural<Type<LHS>::bits >= RHS ? Type<LHS>::bits : RHS>; };
template<int LHS, int RHS> struct CastLarger<Natural<LHS>, Natural<RHS>> { using type = Natural<LHS >= RHS ? LHS : RHS>; };
template<int LHS, typename RHS> struct CastLarger<Integer<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Integer<LHS >= Type<RHS>::bits ? LHS : Type<RHS>::bits>; };
template<typename LHS, int RHS> struct CastLarger<LHS, Integer<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Integer<Type<LHS>::bits >= RHS ? Type<LHS>::bits : RHS>; };
template<int LHS, int RHS> struct CastLarger<Integer<LHS>, Integer<RHS>> { using type = Integer<LHS >= RHS ? LHS : RHS>; };
//lesser: min(LHS,RHS) bits
template<typename LHS, typename RHS, typename = void> struct CastLesser { using type = void; };
template<int LHS, typename RHS> struct CastLesser<Natural<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Natural<LHS <= Type<RHS>::bits ? LHS : Type<RHS>::bits>; };
template<typename LHS, int RHS> struct CastLesser<LHS, Natural<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Natural<Type<LHS>::bits <= RHS ? Type<LHS>::bits : RHS>; };
template<int LHS, int RHS> struct CastLesser<Natural<LHS>, Natural<RHS>> { using type = Natural<LHS <= RHS ? LHS : RHS>; };
template<int LHS, typename RHS> struct CastLesser<Integer<LHS>, RHS, enable_if_t<is_integral_v<RHS>>> { using type = Integer<LHS <= Type<RHS>::bits ? LHS : Type<RHS>::bits>; };
template<typename LHS, int RHS> struct CastLesser<LHS, Integer<RHS>, enable_if_t<is_integral_v<LHS>>> { using type = Integer<Type<LHS>::bits <= RHS ? Type<LHS>::bits : RHS>; };
template<int LHS, int RHS> struct CastLesser<Integer<LHS>, Integer<RHS>> { using type = Integer<LHS <= RHS ? LHS : RHS>; };
#define TP template<typename LHS, typename RHS, typename = enable_if_t<Compatible<LHS, RHS>::value>>
#define Source typename Type<LHS>::type
#define Expand typename CastExpand<LHS, RHS>::type
#define Shrink typename CastShrink<LHS, RHS>::type
#define Double typename CastDouble<LHS, RHS>::type
#define Larger typename CastLarger<LHS, RHS>::type
#define Lesser typename CastLesser<LHS, RHS>::type
#define TLE typename Type<typename CastExpand<LHS, RHS>::type>::type
#define TLN typename Type<LHS>::type
#define TRN typename Type<RHS>::type
#if 1
TP inline auto operator *(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs * (TRN)rhs}; }
TP inline auto operator /(const LHS& lhs, const RHS& rhs) -> Source { return {(TLN)lhs / (TRN)rhs}; }
TP inline auto operator %(const LHS& lhs, const RHS& rhs) -> Source { return {(TLN)lhs % (TRN)rhs}; }
TP inline auto operator +(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs + (TRN)rhs}; }
TP inline auto operator -(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs - (TRN)rhs}; }
TP inline auto operator<<(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs << (TRN)rhs}; }
TP inline auto operator>>(const LHS& lhs, const RHS& rhs) -> Source { return {(TLN)lhs >> (TRN)rhs}; }
TP inline auto operator &(const LHS& lhs, const RHS& rhs) -> Lesser { return {(TLN)lhs & (TRN)rhs}; }
TP inline auto operator ^(const LHS& lhs, const RHS& rhs) -> Larger { return {(TLN)lhs ^ (TRN)rhs}; }
TP inline auto operator |(const LHS& lhs, const RHS& rhs) -> Larger { return {(TLN)lhs | (TRN)rhs}; }
#endif
#if 0
TP inline auto operator *(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs * (TRN)rhs}; }
TP inline auto operator /(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs / (TRN)rhs}; }
TP inline auto operator %(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs % (TRN)rhs}; }
TP inline auto operator +(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs + (TRN)rhs}; }
TP inline auto operator -(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs - (TRN)rhs}; }
TP inline auto operator<<(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs << (TRN)rhs}; }
TP inline auto operator>>(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs >> (TRN)rhs}; }
TP inline auto operator &(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs & (TRN)rhs}; }
TP inline auto operator ^(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs ^ (TRN)rhs}; }
TP inline auto operator |(const LHS& lhs, const RHS& rhs) -> Expand { return {(TLE)lhs | (TRN)rhs}; }
#endif
#undef TP
#undef Source
#undef Expand
#undef Shrink
#undef Double
#undef Larger
#undef Lesser
#undef TLE
#undef TLN
#undef TRN
#undef Type
#undef Compatible
#undef CastExpand
#undef CastShrink
#undef CastDouble
#undef CastLarger
#undef CastLesser
}