BoostはC++11および可変個引数テンプレートの前にタプルをどのように実装しましたか?
言い換えると、C ++ 11に組み込まれている可変個引数テンプレート機能を使用せずに、可変個引数テンプレートのクラスまたは関数
を実装することは可能ですか?
Boostにはタプルのサイズに制限がありました。ほとんどの実際のシナリオでは、10個を超える要素は必要ないため、この制限を気にする必要はありません。ライブラリのメンテナとして、可変個引数テンプレートを使用することで、世界ははるかに単純になったと思います。これ以上のマクロハックはありません...
Boostタプルのサイズ制限とその実装に関する洞察に満ちた議論は次のとおりです 。Boosttuple:要素の最大数を増やす
2番目の質問に答えるには:いいえ、それは不可能です。少なくとも、要素の数に制限はありません。
ライブラリ開発者として、可変個引数テンプレートの一般的な使用例は2つあります。両方の回避策を構築できます。
ケース1:関数オブジェクト
std :: function <>とラムダは非常に優れていますが、c ++ 11でさえ、「箱から出して」それらを使用して実行できるかなり基本的なセットしか提供しません。std :: functionは通常の関数シグネチャで使用できるため、それらの上に本当にクールなものとユーティリティを実装するには、可変個引数テンプレートをサポートする必要があります。
回避策:std::bindを使用した再帰呼び出しはあなたの友達です。実際の可変個引数テンプレートよりも効率は劣りますが(完全な転送などのトリックはおそらく機能しません)、c ++ 11に移植するまでは、テンプレート引数の数が少なくても問題なく機能します。
ケース2:通常のクラス
一般的なstd::function <>(上記を参照)を管理したり、「printf」などのAPIを公開したりするために、通常のクラスが必要になる場合があります。ここでの回避策は、詳細と、クラスの各APIが実行していることです。
可変個引数テンプレートデータを操作するだけで、それを保存する必要がないAPIは、再帰呼び出しとして実行できます。一度に1つの引数を「消費」し、引数がなくなると停止するように記述する必要があります。
可変個引数テンプレートデータを格納する必要のあるAPI(コンストラクターを含む)はより困難です。型が本当に無制限で、何でもかまいません。ただし、それらが常に決定論的にバイナリにマップされるプリミティブになる場合は、それを行うことができます。サポートするすべての型を取得する「Serialize」呼び出しを記述し、それを使用してセット全体をバイナリバッファーにシリアル化し、それらをフェッチおよび設定するために使用する「typeinfo」データのベクトルを構築します。利用可能な特別な場合のメモリとパフォーマンスの点で、実際には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()の特別なバージョンが必要であることに注意してください。