我如何静态断言以禁止“混合字节顺序”在非模板化成员函数中
我在 operator<=>
的高性能实现中使用了 2 个 std::uint64_t
和 1 个 std::uint32_t
包含 std::array
的结构。
我正在努力使其交叉编译器和体系结构兼容。
作为其中的一部分,我试图彻底拒绝任何带有 std::endian::native
的架构,它不是 std::endian::little
或 std: :endian::big
。
我认为我违反了“static_assert 必须依赖于模板参数规则”,因为结构和成员函数没有模板化。
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");
}
}
我想我可以回退而不是 static_assert
} else {
// fall back to the slow way
hash <=> rhs.hash;
}
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
根据反馈整理的版本,还添加了一些抽象:
A tidied up version, based on the feedback, which also adds a touch of abstraction:
我建议断言它是大尾数或小尾数:
I suggest asserting that it's either big or little endian: