如何将C++模板专门用于所有32位POD类型

How can a C++ template be specialized for all 32-bit POD types?

本文关键字:用于 32位 类型 POD C++      更新时间:2023-10-16

我开发了一个简单的模板函数,用于交换单个字段的字节顺序:

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=intfloat的情况下使用它。这两种类型在目标平台上都由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);
}

如果有疑问,请标记dispatch。

该实现有两个特点——is_podget_sizeof_t。基本覆盖调度到带有这两个标记的特征的SwapEndian。对于非pod类型,还有一个is_pod覆盖和一个覆盖(我建议=deleteing)。

扩展到新的性状和类型相对容易。

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 << "podn";
  // 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 podn";
  // 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_BIT8

您应该很少专门化模板函数。相反,过载。标记调度为您提供了解决过载的能力,可以在编译时调度要运行的代码。

实例

我使用了一个单独的SwapEndianSwapEndianImpl,这样我们就可以使用模板推导和部分专门化。

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 Swappingn";
        }
    };
    template <typename T>
    struct SwapEndian<T, 16, true>
    {
        static void apply(T&) {
            std::cout << "Swappingn";
        }
    };
    template <typename T>
    struct SwapEndian<T, 32, true>
    {
        static void apply(T&) {
            std::cout << "Swappingn";
        }
    };
    template <typename T>
    struct SwapEndian<T, 64, true>
    {
        static void apply(T&) {
            std::cout << "Swappingn";
        }
    };
}
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);
}