简体   繁体   English

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

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

I've developed a simple template function for swapping the byte order of a single field: 我开发了一个简单的模板函数来交换单个字段的字节顺序:

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;
    }
}

I'll often use it where T = int or float . 我经常在T = intfloat地方使用它。 Both of these types are represented by 4 bytes on the target platforms, and can be processed by the same specialization of the template. 这两种类型在目标平台上由4个字节表示,并且可以由模板的相同特化处理。

Because this function sometimes is responsible for processing large buffers of raw data, I've created an optimized specialization: 因为这个函数有时负责处理原始数据的大缓冲区,所以我创建了一个优化的特化:

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
}

This specialization also works with 32-bit integers, signed or unsigned, so I have a big smelly pile of duplicates with only the type name different. 这个专门化也适用于32位整数,有符号或无符号,所以我有一大堆重复,只有类型名称不同。

How do I route all instantiations of 4 byte POD types through this one template? 如何通过这个模板路由4字节POD类型的所有实例? (PS. I'm open to solving this in a different way, but in that case I'd like to know definitively whether or not it's possible to build these kind of meta-specialized templates.) (PS。我愿意以不同的方式解决这个问题,但在这种情况下,我想明确知道是否可以构建这些元专用模板。)


EDIT: Thanks everyone, after reading the answers and realizing that arithmetic is a better restriction than pod, I was inspired to write something. 编辑:谢谢大家,在阅读了答案并意识到算术比pod更好的限制之后,我受到了启发,写了一些东西。 All the answers were useful but I could only accept one, so I accepted the one that appears to be structurally the same. 所有答案都很有用,但我只能接受一个,所以我接受了一个似乎在结构上相同的答案。

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);
}

When in doubt, tag dispatch. 如有疑问,请发送标签。

This implementation has 2 traits -- is_pod and get_sizeof_t . 此实现有2个特征 - is_podget_sizeof_t The base override dispatches to SwapEndian s with those two traits tagged. 基本覆盖将调度到具有标记的两个特征的SwapEndian There is also a is_pod override, and an override (which I'd advise =delete ing) for non-pod types. 对于非pod类型,还有一个is_pod覆盖和覆盖(我建议=delete )。

Extension to new traits and types is relatively easy. 扩展到新的特征和类型相对容易。

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>{});
}

I am not sure if this is a good idea, but the above should do it. 我不确定这是不是一个好主意,但上面应该这样做。

Uses some C++14 features. 使用一些C ++ 14功能。 Assumes CHAR_BIT is 8 . 假设CHAR_BIT8

You should only rarely specialize template functions. 您应该很少专门化模板功能。 Instead overload. 而是超载。 Tag dispatching gives you the power of overload resolution to dispatch what code to run at compile time. 标签调度为您提供了重载解析功能,可以调度在编译时运行的代码。

live example 实例

I'm using a separate SwapEndian and SwapEndianImpl so that we can use template deduction and partial specialization. 我正在使用单独的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);
}

I believe that this is a better way to go than SFINAE if you specialize to more than two conditions. 我相信如果你专注于两个以上的条件,这比SFINAE更好。

You might limit your swap on arithmetic types (not using all POD types) and use specialized template classes for flexibility: 您可能会限制算术类型的交换(不使用所有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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM