模板 constexpr endian 转换器(无 UB)
template constexpr endian converter (without UB)
我看到其他一些答案建议使用联合进行字节交换(这是 UB 或无法在编译时完成)。
我已经写了我的,它一直有效,直到我遇到一些表明我的实施无效的案例。我找不到错误,你能帮我吗?
namespace impl
{
// ENDIAN is defined via CMake TestBigEndian
constexpr bool native_is_big_endian()
{
#ifdef ENDIAN
return true;
#else
return false;
#endif
}
}
/*!
* \brief std compliant type for endianness
* \details
* If all scalar types are little-endian, endian::native equals endian::little
* If all scalar types are big-endian, endian::native equals endian::big
*/
enum class endian
{
little,
big,
native = impl::native_is_big_endian() ? big : little
};
template<typename T>
class swap_endian
{
constexpr static size_t sz_minus_one = sizeof(T) - 1;
template<size_t> struct tag_s
{
};
constexpr static T bitwise_or(tag_s<0>, T original, T res)
{
return res | (original >> sz_minus_one * 8);
}
template<size_t i>
constexpr static T bitwise_or(tag_s<i>, T original, T res)
{
return bitwise_or(tag_s<i - 1>(), original, original << i * 8 >> sz_minus_one * 8 << i * 8);
}
public:
constexpr static T swap(T u)
{
return bitwise_or(tag_s<sz_minus_one>(), u, 0);
}
};
template<typename T>
constexpr T swap_endian_v(T u)
{
return swap_endian<T>::swap(u);
}
template<endian From, typename T>
constexpr T to_native_endian(T u)
{
return From == endian::native ? u : swap_endian_v(u);
}
int main()
{
static_assert(uint8_t(0xFA) == swap_endian_v(uint8_t(0xFA)), "Invalid result for endian swapping");
static_assert(uint16_t(0x00AA) == swap_endian_v(uint16_t(0xAA00)), "Invalid result for endian swapping");
static_assert(uint16_t(0xF0AA) == swap_endian_v(uint16_t(0xAAF0)), "Invalid result for endian swapping");
static_assert(uint32_t(0x00'00'CC'00) == swap_endian_v(uint32_t(0x00'CC'00'00)),
"Invalid result for endian swapping");
// this fails
// static_assert(uint32_t(0x6A'25'65'75) == swap_endian_v(uint32_t(0x75'65'25'6A)),
// "Invalid result for endian swapping");
return 0;
}
请不要建议使用BOOST。
在这一点上,我非常想知道我在算法中犯了什么样的错误。
您忽略了通过 res
参数传递给 bitwise_or
的循环重载的第三个参数。 if
似乎有效
return bitwise_or(tag_s<i - 1>(), original,
original << i * 8 >> sz_minus_one * 8 << i * 8);
更改为:
return bitwise_or(tag_s<i - 1>(), original,
res | original << i * 8 >> sz_minus_one * 8 << i * 8);
我看到其他一些答案建议使用联合进行字节交换(这是 UB 或无法在编译时完成)。
我已经写了我的,它一直有效,直到我遇到一些表明我的实施无效的案例。我找不到错误,你能帮我吗?
namespace impl
{
// ENDIAN is defined via CMake TestBigEndian
constexpr bool native_is_big_endian()
{
#ifdef ENDIAN
return true;
#else
return false;
#endif
}
}
/*!
* \brief std compliant type for endianness
* \details
* If all scalar types are little-endian, endian::native equals endian::little
* If all scalar types are big-endian, endian::native equals endian::big
*/
enum class endian
{
little,
big,
native = impl::native_is_big_endian() ? big : little
};
template<typename T>
class swap_endian
{
constexpr static size_t sz_minus_one = sizeof(T) - 1;
template<size_t> struct tag_s
{
};
constexpr static T bitwise_or(tag_s<0>, T original, T res)
{
return res | (original >> sz_minus_one * 8);
}
template<size_t i>
constexpr static T bitwise_or(tag_s<i>, T original, T res)
{
return bitwise_or(tag_s<i - 1>(), original, original << i * 8 >> sz_minus_one * 8 << i * 8);
}
public:
constexpr static T swap(T u)
{
return bitwise_or(tag_s<sz_minus_one>(), u, 0);
}
};
template<typename T>
constexpr T swap_endian_v(T u)
{
return swap_endian<T>::swap(u);
}
template<endian From, typename T>
constexpr T to_native_endian(T u)
{
return From == endian::native ? u : swap_endian_v(u);
}
int main()
{
static_assert(uint8_t(0xFA) == swap_endian_v(uint8_t(0xFA)), "Invalid result for endian swapping");
static_assert(uint16_t(0x00AA) == swap_endian_v(uint16_t(0xAA00)), "Invalid result for endian swapping");
static_assert(uint16_t(0xF0AA) == swap_endian_v(uint16_t(0xAAF0)), "Invalid result for endian swapping");
static_assert(uint32_t(0x00'00'CC'00) == swap_endian_v(uint32_t(0x00'CC'00'00)),
"Invalid result for endian swapping");
// this fails
// static_assert(uint32_t(0x6A'25'65'75) == swap_endian_v(uint32_t(0x75'65'25'6A)),
// "Invalid result for endian swapping");
return 0;
}
请不要建议使用BOOST。 在这一点上,我非常想知道我在算法中犯了什么样的错误。
您忽略了通过 res
参数传递给 bitwise_or
的循环重载的第三个参数。 if
return bitwise_or(tag_s<i - 1>(), original,
original << i * 8 >> sz_minus_one * 8 << i * 8);
更改为:
return bitwise_or(tag_s<i - 1>(), original,
res | original << i * 8 >> sz_minus_one * 8 << i * 8);