C++ 模板如何专用于所有 32 位 POD 类型?
How can a C++ template be specialized for all 32-bit POD types?
我开发了一个简单的模板函数来交换单个字段的字节顺序:
template <typename T> inline void SwapEndian(T& ptr) {
char *bytes = reinterpret_cast<char*>(&ptr);
int a = sizeof(T) / 2;
while (a--) {
char tmp = bytes[a];
int b = sizeof(T) - 1 - a;
bytes[a] = bytes[b];
bytes[b] = tmp;
}
}
我会经常在 T = int
或 float
的地方使用它。这两种类型在目标平台上均由 4 个字节表示,并且可以由模板的相同特化处理。
因为这个函数有时负责处理大量的原始数据缓冲区,所以我创建了一个优化的特化:
template<> inline void SwapEndian(float& ptr) {
#if defined(__GNUC__)
*reinterpret_cast<unsigned*>(&ptr) = __builtin_bswap32(*reinterpret_cast<unsigned*>(&ptr));
#elif defined(_MSC_VER)
*reinterpret_cast<unsigned*>(&ptr) = __byteswap_ulong(*reinterpret_cast<unsigned*>(&ptr));
#endif
}
这个专业化也适用于 32 位整数,有符号或无符号,所以我有一大堆难闻的重复项,只是类型名称不同。
如何通过这个模板路由 4 字节 POD 类型的所有实例化? (PS。我愿意以不同的方式解决这个问题,但在那种情况下,我想明确地知道是否可以构建这种元专用模板。)
编辑:谢谢大家,在阅读答案并意识到算术是比 pod 更好的限制后,我受到启发写了一些东西。所有的答案都有用,但我只能接受一个,所以我接受了看起来结构相同的那个。
template<bool, bool> struct SwapEndian_ { template<typename T> static inline void _(T&); };
template<> template<typename T> inline void SwapEndian_<true, true>::_(T& ptr) {
// ... stuff here ...
}
// ... more stuff here ...
template<typename T> inline void SwapEndian(T& ptr) {
static_assert(is_arithmetic<T>::value, "Endian swap not supported for non-arithmetic types.");
SwapEndian_<sizeof(T) & (8 | 4), sizeof(T) & (8 | 2)>::template _<T>(ptr);
}
如有疑问,请标记派送。
此实现有 2 个特征——is_pod
和 get_sizeof_t
。基础覆盖分派到 SwapEndian
s 并标记了这两个特征。还有一个 is_pod
覆盖,以及一个针对非 pod 类型的覆盖(我建议 =delete
ing)。
新特征和类型的扩展相对容易。
template<size_t n>
using sizeof_t = std::integral_constant<size_t, n>;
template<class T>
using get_sizeof_t = sizeof_t<sizeof(T)>;
template <class T>
void SwapEndian(T& t, std::true_type /*is pod*/, sizeof_t<4>) {
std::cout << "4 bytes!\n";
// code to handle 32 bit pods
}
template <class T, size_t n>
void SwapEndian(T& t, std::true_type /*is pod*/, sizeof_t<n>) {
std::cout << "pod\n";
// code to handle generic case
}
template <class T, size_t n>
void SwapEndian(T& t, std::false_type /*is pod*/, sizeof_t<n>) {
std::cout << "not pod\n";
// probably want to =delete this overload actually
}
template<class T>
void SwapEndian(T& t) {
SwapEndian(t, std::is_pod<T>{}, get_sizeof_t<T>{});
}
我不确定这是否是个好主意,但上面应该可以做到。
使用一些 C++14 功能。假设 CHAR_BIT
是 8
.
您应该很少特化模板函数。而是超载。标签调度赋予您重载决策的能力,可以在编译时将什么代码调度到 运行。
我正在使用单独的 SwapEndian
和 SwapEndianImpl
以便我们可以使用模板推导和偏特化。
template<bool> struct SwapEndianImpl
{
template<typename t> static inline void Func(t& n);
};
template<> template<typename t> void SwapEndianImpl<false>::Func(t& n)
{
std::cout << "not 32bit pod" << std::endl;
}
template<> template<typename t> void SwapEndianImpl<true>::Func(t& n)
{
std::cout << "32bit pod" << std::endl;
}
template<typename t> inline void SwapEndian(t& n)
{
SwapEndianImpl<std::is_pod<t>::value && sizeof(t) == (32 / CHAR_BIT)>::template Func<t>(n);
}
我相信如果你专攻两个以上的条件,这是比 SFINAE 更好的方法。
您可能会限制对算术类型的交换(不使用所有 POD 类型)并使用专用模板 类 以获得灵活性:
#include <climits>
#include <iostream>
#include <type_traits>
namespace Detail {
template <
typename T,
unsigned N = sizeof(T) * CHAR_BIT,
bool Swap = std::is_arithmetic<T>::value>
struct SwapEndian
{
static void apply(T&) {
std::cout << "Not Swapping\n";
}
};
template <typename T>
struct SwapEndian<T, 16, true>
{
static void apply(T&) {
std::cout << "Swapping\n";
}
};
template <typename T>
struct SwapEndian<T, 32, true>
{
static void apply(T&) {
std::cout << "Swapping\n";
}
};
template <typename T>
struct SwapEndian<T, 64, true>
{
static void apply(T&) {
std::cout << "Swapping\n";
}
};
}
template <typename T>
void SwapEndian(T& value) {
Detail::SwapEndian<T>::apply(value);
}
struct Structure
{
char s[4];
};
static_assert(std::is_pod<Structure>::value, "Should be POD");
int main() {
char c;
short s;
int i;
long long l;
float f;
double d;
void* p;
Structure structure;
SwapEndian(c);
SwapEndian(s);
SwapEndian(i);
SwapEndian(l);
SwapEndian(f);
SwapEndian(d);
SwapEndian(p);
SwapEndian(structure);
}
我开发了一个简单的模板函数来交换单个字段的字节顺序:
template <typename T> inline void SwapEndian(T& ptr) {
char *bytes = reinterpret_cast<char*>(&ptr);
int a = sizeof(T) / 2;
while (a--) {
char tmp = bytes[a];
int b = sizeof(T) - 1 - a;
bytes[a] = bytes[b];
bytes[b] = tmp;
}
}
我会经常在 T = int
或 float
的地方使用它。这两种类型在目标平台上均由 4 个字节表示,并且可以由模板的相同特化处理。
因为这个函数有时负责处理大量的原始数据缓冲区,所以我创建了一个优化的特化:
template<> inline void SwapEndian(float& ptr) {
#if defined(__GNUC__)
*reinterpret_cast<unsigned*>(&ptr) = __builtin_bswap32(*reinterpret_cast<unsigned*>(&ptr));
#elif defined(_MSC_VER)
*reinterpret_cast<unsigned*>(&ptr) = __byteswap_ulong(*reinterpret_cast<unsigned*>(&ptr));
#endif
}
这个专业化也适用于 32 位整数,有符号或无符号,所以我有一大堆难闻的重复项,只是类型名称不同。
如何通过这个模板路由 4 字节 POD 类型的所有实例化? (PS。我愿意以不同的方式解决这个问题,但在那种情况下,我想明确地知道是否可以构建这种元专用模板。)
编辑:谢谢大家,在阅读答案并意识到算术是比 pod 更好的限制后,我受到启发写了一些东西。所有的答案都有用,但我只能接受一个,所以我接受了看起来结构相同的那个。
template<bool, bool> struct SwapEndian_ { template<typename T> static inline void _(T&); };
template<> template<typename T> inline void SwapEndian_<true, true>::_(T& ptr) {
// ... stuff here ...
}
// ... more stuff here ...
template<typename T> inline void SwapEndian(T& ptr) {
static_assert(is_arithmetic<T>::value, "Endian swap not supported for non-arithmetic types.");
SwapEndian_<sizeof(T) & (8 | 4), sizeof(T) & (8 | 2)>::template _<T>(ptr);
}
如有疑问,请标记派送。
此实现有 2 个特征——is_pod
和 get_sizeof_t
。基础覆盖分派到 SwapEndian
s 并标记了这两个特征。还有一个 is_pod
覆盖,以及一个针对非 pod 类型的覆盖(我建议 =delete
ing)。
新特征和类型的扩展相对容易。
template<size_t n>
using sizeof_t = std::integral_constant<size_t, n>;
template<class T>
using get_sizeof_t = sizeof_t<sizeof(T)>;
template <class T>
void SwapEndian(T& t, std::true_type /*is pod*/, sizeof_t<4>) {
std::cout << "4 bytes!\n";
// code to handle 32 bit pods
}
template <class T, size_t n>
void SwapEndian(T& t, std::true_type /*is pod*/, sizeof_t<n>) {
std::cout << "pod\n";
// code to handle generic case
}
template <class T, size_t n>
void SwapEndian(T& t, std::false_type /*is pod*/, sizeof_t<n>) {
std::cout << "not pod\n";
// probably want to =delete this overload actually
}
template<class T>
void SwapEndian(T& t) {
SwapEndian(t, std::is_pod<T>{}, get_sizeof_t<T>{});
}
我不确定这是否是个好主意,但上面应该可以做到。
使用一些 C++14 功能。假设 CHAR_BIT
是 8
.
您应该很少特化模板函数。而是超载。标签调度赋予您重载决策的能力,可以在编译时将什么代码调度到 运行。
我正在使用单独的 SwapEndian
和 SwapEndianImpl
以便我们可以使用模板推导和偏特化。
template<bool> struct SwapEndianImpl
{
template<typename t> static inline void Func(t& n);
};
template<> template<typename t> void SwapEndianImpl<false>::Func(t& n)
{
std::cout << "not 32bit pod" << std::endl;
}
template<> template<typename t> void SwapEndianImpl<true>::Func(t& n)
{
std::cout << "32bit pod" << std::endl;
}
template<typename t> inline void SwapEndian(t& n)
{
SwapEndianImpl<std::is_pod<t>::value && sizeof(t) == (32 / CHAR_BIT)>::template Func<t>(n);
}
我相信如果你专攻两个以上的条件,这是比 SFINAE 更好的方法。
您可能会限制对算术类型的交换(不使用所有 POD 类型)并使用专用模板 类 以获得灵活性:
#include <climits>
#include <iostream>
#include <type_traits>
namespace Detail {
template <
typename T,
unsigned N = sizeof(T) * CHAR_BIT,
bool Swap = std::is_arithmetic<T>::value>
struct SwapEndian
{
static void apply(T&) {
std::cout << "Not Swapping\n";
}
};
template <typename T>
struct SwapEndian<T, 16, true>
{
static void apply(T&) {
std::cout << "Swapping\n";
}
};
template <typename T>
struct SwapEndian<T, 32, true>
{
static void apply(T&) {
std::cout << "Swapping\n";
}
};
template <typename T>
struct SwapEndian<T, 64, true>
{
static void apply(T&) {
std::cout << "Swapping\n";
}
};
}
template <typename T>
void SwapEndian(T& value) {
Detail::SwapEndian<T>::apply(value);
}
struct Structure
{
char s[4];
};
static_assert(std::is_pod<Structure>::value, "Should be POD");
int main() {
char c;
short s;
int i;
long long l;
float f;
double d;
void* p;
Structure structure;
SwapEndian(c);
SwapEndian(s);
SwapEndian(i);
SwapEndian(l);
SwapEndian(f);
SwapEndian(d);
SwapEndian(p);
SwapEndian(structure);
}