8

Boost 在 C++11 和Variadic Templates之前是如何实现 Tuple 的?

换句话说:是否可以通过不使用C++11 中的内置Variadic Templates
功能 来实现Variadic Templates类或函数?

4

2 回答 2

5

Boost 对元组的大小有限制。与大多数实际场景一样,您不需要超过 10 个元素,您不会介意此限制。作为一个库维护者,我猜想,有了可变参数模板,世界变得简单多了。没有更多的宏黑客...

下面是关于 Boost tuple 的大小限制及其实现的深入讨论: boost tuple: increase maximum number of elements

回答你的第二个问题:不,这是不可能的。至少不是无限数量的元素。

于 2013-01-02T03:00:54.823 回答
0

作为库开发人员,我见过 2 个用于可变参数模板的常见用例。您可以为两者构建一个解决方法。

案例一:函数对象

std::function<> 和 lambdas 非常好,但即使是 c++11 也只能为您提供一组相当基本的东西,您可以“开箱即用”地使用它们。要在它们之上实现非常酷的东西和实用程序,您需要支持可变参数模板,因为 std::function 可以与任何普通函数签名一起使用。

解决方法:使用 std::bind 的递归调用是您的朋友。它比真正的可变参数模板效率低(并且像完美转发这样的一些技巧可能不起作用),但在移植到 c++11 之前,它对于适度的模板参数 #s 可以正常工作。

案例2:普通班

有时您需要一个普通的类来管理通用 std::function<>s(见上文)或公开像“printf”这样的 API。这里的解决方法归结为细节以及类的每个 API 正在做什么。

仅操作可变参数模板数据但不需要存储它的 API 可以作为递归调用运行。您需要编写它们,以便它们一次“消耗”一个参数,并在它们用完参数时停止。

需要存储可变参数模板数据的 API(包括构造函数)更难——如果类型真的是无限的并且可以是任何东西,那么你就搞砸了。但是,如果它们总是确定性地映射到二进制的原语,你可以做到。只需编写一个“序列化”调用,获取您支持的所有类型,然后使用它将整个集合序列化到二进制缓冲区中,并构建一个用于获取和设置它们的“类型信息”数据向量。在可用的特殊情况下,就内存和性能而言,它实际上是比 std::tuple 更好的解决方案。

这是“序列化元组”技巧:

// MemoryBuffer: A basic byte buffer w/ its size
class MemoryBuffer {
private:
   void* buffer;
   int   size;
   int   currentSeekPt;

protected:
   void  ResizeBuffer() {
      int   newSz  = size << 1; // Multiply by 2
      void* newBuf = calloc( newSz, 1); // Make sure it is zeroed
      memcpy( newBuf, buffer, target->size);
      free( buffer);

      size = newSz; 
      buffer = newBuf;
   }

 public:
    MemoryBuffer(int initSize) 
       : buffer(0), size(initSize), currentSeekPt(0)
    {
       buffer = calloc( size, 1);
    }
   ~MemoryBuffer() {
      if(buffer) {
         free( buffer);
      }
    }

    // Add data to buffer
    bool AddData(const void* data, int dataSz) {
        if(!data || !dataSz) return false;

        if(dataSz + currentSeekPt > size) { // resize to hold data
            ResizeBuffer();
        }
        memcpy( buffer, data, dataSz);
        return true;
    }

    void* GetDataPtr() const { return buffer; }
    int   GetSeekOffset() const { return currentSeekPt; }
    int   GetTotalSize() const { return size; }
};

struct BinaryTypeInfo {
   std::type_info type;     // RTTI type_info struct. You can use an "enum"
                            // instead- code will be faster, but harder to maintain.   
   ui64      bufferOffset;  // Lets me "jump" into the buffer to 
}

// Versions of "Serialize" for all 'tuple' data types I support
template<typename BASIC>
bool Serialize(BASIC data, MemoryBuffer* target,
              std::vector<BinaryTypeInfo>& types)
{
    // Handle boneheads
    if(!target) return false;

    // Setup our type info structure
    BinaryTypeInfo info;
    info.type = typeid(data);
    info.bufferOffset = target->GetSeekOffset();

    int binarySz = sizeof(data);
    void* binaryVersion = malloc( binarySz);
    if(!binaryVersion) return false;

    memcpy( binaryVersion, &data, binarySz); // Data type must support this
    if(!target->AddData( binaryVersion, binarySz)) {
        free( binaryVersion);
        return false;
    }
    free( binaryVersion);

    // Populate type vector     
    types.push_back( info);
    return true;
}

这只是一个快速而肮脏的版本;你会更好地隐藏真实的东西,并可能将这些碎片组合成 1 个可重复使用的类。请注意,如果您希望处理 std::string 和更复杂的类型,则需要特殊版本的 Serialize()。

于 2014-06-10T16:16:10.403 回答