I am using 2 x std::uint64_t
and 1 x std::uint32_t
in a high performance implementation of of operator<=>
in a struct conataining a std::array<std::byte, 20>
.
I am trying to make it cross compiler and architecture compatible.
As part of that I am trying to outright reject any architecture with std::endian::native
which is not std::endian::little
or std::endian::big
.
I think I am running foul of the "static_assert must depend on a template parameter rule", as the struct and the member function are not templated.
std::strong_ordering operator<=>(const pawned_pw& rhs) const {
static_assert(sizeof(std::uint64_t) == 8);
static_assert(sizeof(std::uint32_t) == 4);
if constexpr (std::endian::native == std::endian::little) {
// c 23 will have std::byteswap, so we won't need this
#ifdef _MSC_VER
#define BYTE_SWAP_32 _byteswap_ulong
#define BYTE_SWAP_64 _byteswap_uint64
#else
#define BYTE_SWAP_32 __builtin_bswap32
#define BYTE_SWAP_64 __builtin_bswap64
#endif
// this compiles to a load and `bswap` which should be fast
// measured > 33% faster than hash < rhs.hash, which compiles to `memcmp`
std::uint64_t head = BYTE_SWAP_64(*(std::uint64_t*)(&hash[0])); // NOLINT
std::uint64_t rhs_head = BYTE_SWAP_64(*(std::uint64_t*)(&rhs.hash[0])); // NOLINT
if (head != rhs_head) return head <=> rhs_head;
std::uint64_t mid = BYTE_SWAP_64(*(std::uint64_t*)(&hash[8])); // NOLINT
std::uint64_t rhs_mid = BYTE_SWAP_64(*(std::uint64_t*)(&rhs.hash[8])); // NOLINT
if (mid != rhs_mid) return mid <=> rhs_mid;
std::uint32_t tail = BYTE_SWAP_32(*(std::uint32_t*)(&hash[16])); // NOLINT
std::uint32_t rhs_tail = BYTE_SWAP_32(*(std::uint32_t*)(&rhs.hash[16])); // NOLINT
return tail <=> rhs_tail;
} else if constexpr (std::endian::native == std::endian::big) {
// can use big_endian directly
std::uint64_t head = *(std::uint64_t*)(&hash[0]); // NOLINT
std::uint64_t rhs_head = *(std::uint64_t*)(&rhs.hash[0]); // NOLINT
if (head != rhs_head) return head <=> rhs_head;
std::uint64_t mid = *(std::uint64_t*)(&hash[8]); // NOLINT
std::uint64_t rhs_mid = *(std::uint64_t*)(&rhs.hash[8]); // NOLINT
if (mid != rhs_mid) return mid <=> rhs_mid;
std::uint32_t tail = *(std::uint32_t*)(&hash[16]); // NOLINT
std::uint32_t rhs_tail = *(std::uint32_t*)(&rhs.hash[16]); // NOLINT
return tail <=> rhs_tail;
} else {
static_assert(std::endian::native != std::endian::big &&
std::endian::native != std::endian::little,
"mixed-endianess architectures are not supported");
}
}
I guess I could just fall back instead of static_assert
} else {
// fall back to the slow way
hash <=> rhs.hash;
}
CodePudding user response:
I suggest asserting that it's either big or little endian:
#include <bit>
#include <compare>
struct pawned_pw {
std::strong_ordering operator<=>(const pawned_pw& rhs) const {
static_assert(std::endian::native == std::endian::big ||
std::endian::native == std::endian::little,
"mixed-endianess architectures are not supported");
if constexpr (std::endian::native == std::endian::little) {
return ...;
} else {
// big
return ...;
}
}
};