【问题标题】:how do I use type_traits or template function specialization to consolidate template methods如何使用 type_traits 或模板函数特化来整合模板方法
【发布时间】:2014-05-30 23:50:02
【问题描述】:

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

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 方法做了类似的事情。这些显示在编译live example

/**
 * 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& rVal”参数的大小,以便决定是对参数执行 SWAP32 还是 SWAP16。这就是为什么我认为 type_traits 可以派上用场,而不必进行运行时检查来比较参数的大小。

我认为我在正确的轨道上,但我不知道如何使用 type_traits 根据参数类型检查和做某些事情。我认为或者我可以使用模板方法专业化对 16 位参数执行特殊操作,但我认为这不会节省太多精力,因为我还必须专注于 16 位参数类型的有符号和无符号变体(假设非专用版本用于 32 位值参数)。任何解决此问题的帮助将不胜感激。

【问题讨论】:

    标签: c++ templates template-specialization typetraits specialization


    【解决方案1】:

    你可以使用类似的东西:

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

    【讨论】:

    • 我喜欢您使用 sizeof 参数处理有符号和无符号变体的方式 - 不错的方法。
    • 即使在我将模板特化集成到我的类中后,上面的代码也无法编译。这是由于调用的宏需要访问 mPackNBO 和 mSwapNeeded 成员。此外,模板专用 operator() 方法需要 (T& val) 参数 - 缺少 val。 Visual Studio 2013 指出:错误 C2327: 'UtlSafeBuffer::mPackNBO' : 不是类型名称、静态或枚举器 - 不知何故,模板特化看不到这些成员变量 - 即使它们嵌套在 UtlSafeBuffer 中。
    • 您可以移动这些成员作为交换参数来解决这个问题。
    • 缺少val 参数添加顺便说一句。
    • 谢谢,这很好!顺便说一句,出于好奇,您知道使用 type_traits 进行类似类型检查的方法吗?我将使用这个解决方案,但我很好奇它是如何完成的。
    【解决方案2】:

    您可以使用模板专门化来执行专门的 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;
    }
    

    【讨论】:

    • 无符号变体 uint32_t 和 uint16_t 怎么样?与其他答案相比,它似乎有很多代码。这就是为什么我认为某种形式的类型特征方法是合适的——在编译时查询 rVal 参数的特定类型、符号和大小。
    【解决方案3】:

    您可以只为您的 4 种类型定义交换方法:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多