実行時に明らかにIDフラグに基づいて型が変化するC++(vc2008)を使用して構造体を読み書きしています。正しいタイプの作成および/または読み取りと書き込みには、切り替えが必要になります。最も近い既存の例は、スイッチの代わりにテンプレートを使用することですが、これでは実行時にタイプを指定できません。複数の場所で同じスイッチを作成しないようにするために、この問題を解決するために再帰テンプレートの使用を調査してきました。これらを使用するのはこれが初めてなので、コード例にいくつかの大きな改善を加えることができるかもしれません!
以下は実際の例です。'main()'でわかるように、使用される型IDは、任意のランタイム値に設定できる変数intです。TypeList <>で関数を呼び出すと、一致するIDまたはvoid型に到達するまで、型を繰り返し処理します。
#include <stdio.h>
#include <iostream>
//Base type
struct Base
{
//NOTE: The virtual destructor can be added to aid with debugging
//virtual ~Base(){}
friend std::ostream& operator << ( std::ostream& stream, const Base& rhs )
{ return stream << "Base"; }
};
struct A : Base
{
friend std::ostream& operator << ( std::ostream& stream, const A& rhs )
{ return stream << "A"; }
};
struct B : Base
{
friend std::ostream& operator << ( std::ostream& stream, const B& rhs )
{ return stream << "B"; }
};
struct C : Base
{
friend std::ostream& operator << ( std::ostream& stream, const C& rhs )
{ return stream << "C"; }
};
//Recursive template type
// - If the ID/key does not match the next type is checked and so on
template < unsigned int kID, typename _Type, typename _TNext >
struct TypeList
{
typedef _Type Type;
typedef typename _TNext::Base Base;
static Base* doNew( unsigned int id )
{ return id == kID ? new _Type() : (Base*)_TNext::doNew(id); }
static void doDelete(unsigned int id, Base* rhs )
{ id == kID ? delete (_Type*)rhs : _TNext::doDelete(id, rhs ); }
static std::ostream& doWrite( unsigned int id, std::ostream& stream, const Base* rhs )
{ return id == kID ? stream << (*(const _Type*)rhs) : _TNext::doWrite(id, stream, rhs); }
};
//Specialise the 'void' case to terminate the list
// TODO; this doesn't seem as elegant as possible!? How can we separate the logic from the functionality better...
template < unsigned int kID, typename _Type >
struct TypeList<kID, _Type, void>
{
typedef _Type Type;
typedef _Type Base;
static _Type* doNew( unsigned int id )
{ return id == kID ? new _Type() :0; }
static void doDelete(unsigned int id, _Type* rhs )
{ if ( id == kID ) delete rhs; }
static std::ostream& doWrite( unsigned int id, std::ostream& stream, const _Type* rhs )
{ return id == kID ? stream << (*(const _Type*)rhs) : stream; }
};
// ID values used to identify the different structure types
enum eID
{
ID_A,
ID_B,
ID_C,
};
//Create our ID and Type list
typedef TypeList< ID_A, A,
TypeList< ID_B, B,
TypeList< ID_C, C,
TypeList< -1 , Base, void> > > > TypesList;
int _tmain(int argc, _TCHAR* argv[])
{
eID type = ID_C; //, We are dealing with a type of 'C'
Base* newInst = TypesList::doNew( type ); //Create a new C
TypesList::doWrite( type, std::cout, newInst ); //Write 'C' to the console
TypesList::doDelete( type, newInst ); //Delete C
return 0;
}
これとこれを行うための他の/より良い方法についての人々の見解は何ですか?主に、ロジックをクラスの機能からうまく分離して、TypeList <,,_Type>およびTypeList<,,void>のインスタンス化で重複したコードを保存する方法があります。
編集:ソリューションは、ルックアップなどにタイプを「追加」するための実行時のセットアップを必要としないことが好ましい。
乾杯、クレイグ