如何使用type_traits或模板函数专门化来整合模板方法

how do I use type_traits or template function specialization to consolidate template methods

本文关键字:专门化 函数 模板方法 type 何使用 traits      更新时间:2023-10-16

我试图从类似于下面所示的类中合并许多非常相似的函数方法,我认为有效实现这一点的最佳方法是通过使用模板与模板函数专门化或类型特征相结合。我是模板专业化和类型特征的新手,但我理解基本概念,这就是为什么我在细节上要求一些指导。无论如何,作为起点,我的类是一个智能缓冲类,它有许多类似于下面列出的方法签名。

class OldSafeBuffer {
public:
    intmax_t writeAt(const intmax_t& rIndex, const uint32_t val32);
    intmax_t writeAt(const intmax_t& rIndex, const int32_t val32);
    intmax_t readFrom(const intmax_t& rIndex, uint32_t& rVal32);
    intmax_t readFrom(const intmax_t& rIndex, int32_t& rVal32);
    intmax_t writeAt(const intmax_t& rIndex, const uint16_t val16);
    intmax_t writeAt(const intmax_t& rIndex, const int16_t val16);
    intmax_t readFrom(const intmax_t& rIndex, uint16_t& rVal16);
    intmax_t readFrom(const intmax_t& rIndex, int16_t& rVal16);
    intmax_t read(uint32_t& rVal32);
    intmax_t read(int32_t& rVal32);
    intmax_t read(uint16_t& rVal16);
    intmax_t read(int16_t& rVal16);
protected:
    // Actual memory storage.
    std::unique_ptr<char[]> mBuffer;
    // Buffer length
    intmax_t mBufferLength;
    // Represents the largest byte offset referenced.
    // Can be used to retrieve written length of buffer.
    intmax_t mHighWaterMark;
    // If set, caller wanted to pack data in network-byte-order.
    bool mPackNBO;
    // Set on construction, determines whether value needs to be byte-swapped.
    bool mSwapNeeded;
    // Used for file compatibility
    intmax_t mPosition;
};

我认为这将是一个完美的候选转换使用模板函数,因为这些函数非常相似,我有很多重复的代码在每个方法。两种方法的区别主要在于符号和16位或32位值参数的大小。

无论如何,为了巩固readFrom方法,我把下面的方法放在一起。我也为write方法做了类似的事情。这些都显示在编译的实例中。

/**
 * Read value (signed or unsigned) from buffer at given byte offset.
 *
 * @param rIndex [in]
 * @param rVal   [out]
 *
 * @return BytesRead or -1 on error
 */
template <typename T>
inline intmax_t readFrom(const intmax_t& rIndex, T& rVal)
{
    if ((rIndex + static_cast<intmax_t>(sizeof(T))) <= mBufferLength) {
        T* pVal = (T *)&mBuffer[rIndex];
        rVal = *pVal;
        // @JC Partial Template Specialization for 16 bit entities?
        if (sizeof(rVal) > sizeof(int16_t)) {
            SWAP32(rVal);
        } else {
            SWAP16(rVal);
        }
        mPosition = rIndex + sizeof(T);
        return sizeof(rVal);
    }
    return -1;
}

从我的评论中可以看出,我还需要知道'T&以决定是对该参数执行SWAP32还是SWAP16。这就是为什么我认为type_traits会更有用,而不是必须在运行时检查来比较参数的大小。

我认为我是在正确的轨道上,但我无法弄清楚如何使用type_traits来检查和做某些事情取决于参数类型。我认为我可以选择使用模板方法专门化对16位参数做特殊的事情,但我认为这不会节省太多的精力,因为我还必须专门化16位参数类型的有符号和无符号变体(假设非专门化版本适用于32位值参数)。任何帮助弄清楚这将是非常感激的。

你可以这样写:

template<typename T, std::size_t N = sizeof(T)> struct Swap;
template<typename T> struct Swap<T, 1> {
    void operator() (T&) const { /* Do nothing*/ }
};
template<typename T> struct Swap<T, 2> {
    void operator() (T& val) const { SWAP16(val); }
};
template<typename T> struct Swap<T, 4> {
    void operator() (T& val) const { SWAP32(val); }
};

然后命名为:

Swap<T>()(rVal);

所以在上下文中:

if (sizeof(T) > sizeof(int16_t)) {
    SWAP32(val);
} else {
    SWAP16(val);
}

可以写成

Swap<T>()(val);

您可以使用模板专门化来执行专门化的swap函数,如下面的示例:

template<typename T>
struct Swap;
template<>
struct Swap<int16_t> {
  static void swap(int16_t val) { SWAP16(val); }
};
template<>
struct Swap<int32_t> {
  static void swap(int32_t val) { SWAP32(val); }
};

那么你可以在代码中这样调用它:

template <typename T>
inline intmax_t readFrom(const intmax_t& rIndex, T& rVal)
{
    if ((rIndex + static_cast<intmax_t>(sizeof(T))) <= mBufferLength) {
        T* pVal = (T *)&mBuffer[rIndex];
        rVal = *pVal;
        Swap<T>::swap(rVal);
        mPosition = rIndex + sizeof(T);
        return sizeof(rVal);
    }
    return -1;
}

您可以为您的4种类型定义swap方法:

inline void swap_endianess(int16_t& value) { SWAP16(value); }
inline void swap_endianess(uint16_t& value) { SWAP16(value); }
inline void swap_endianess(int32_t& value) { SWAP32(value); }
inline void swap_endianess(uint32_t& value) { SWAP32(value); }

,并让模板函数分派到正确的那个。

所以不用

if (sizeof(T) > sizeof(int16_t)) {
    SWAP32(val);
} else {
    SWAP16(val);
}

就叫

swap_endianess(val);