Boost 在 C++11 和Variadic Templates之前是如何实现 Tuple 的?
换句话说:是否可以通过不使用C++11 中的内置Variadic Templates
功能
来实现Variadic Templates类或函数?
Boost 对元组的大小有限制。与大多数实际场景一样,您不需要超过 10 个元素,您不会介意此限制。作为一个库维护者,我猜想,有了可变参数模板,世界变得简单多了。没有更多的宏黑客...
下面是关于 Boost tuple 的大小限制及其实现的深入讨论: boost tuple: increase maximum number of elements
回答你的第二个问题:不,这是不可能的。至少不是无限数量的元素。
作为库开发人员,我见过 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()。