私のエンジンには、コンパイル時にクラス情報で満たされた単純なリフレクション システムがあります (つまり、一連のテンプレートを中心に構築され、メタ情報の生成プロセスを自動化できます)。
次の例を検討してください。
class Type
{
//...
Map<PropertyHash, TypeProperty> _properties;
};
タイプごとに関数があります。
template <class T>
void InitializeType(TypeInitializer* typeInitializer);
型の初期化を担当します。TypeInitializer には、フィールドと基本型を型に追加するために使用されるメソッドがほとんどありません。したがって、基本的に、すべての新しい型には、この関数の特殊化のみが必要です。その後、型が初めて照会されると、TypeDatabase は具体的な Type オブジェクトを作成し、それに対して InitializeType() を呼び出します (TypeInitializer は、構築中に型へのポインターを取得します)。例えば:
struct CST
{
const float* fptr;
volatile Uint32 vuint;
void** vptr;
};
template <>
SvrInline void InitializeType<CST>(TypeInitializer* typeInitializer)
{
typeInitializer->AddProperty("fptr", &CST::fptr);
typeInitializer->AddProperty("vuint", &CST::vuint);
typeInitializer->AddProperty("vptr", &CST::vptr);
}
以上です。すべての魔法は、次のように宣言される TypeProperty コンストラクターで行われます。
template <class Object_type, class Property_type>
TypeProperty(const char* fieldName, Property_type (Object_type::* propertyPtr));
これにより、プロパティの正確なタイプを知ることができます。サイズ、const-ness、volatile-ness などをテストし、この情報を TypeProperty オブジェクトに保存します。良い。
さて、メンバー関数にも同じものが必要です。「同一」とは、現在プロパティを追加しているのとまったく同じ方法で関数を追加できることを意味します。
私が最初に考えたのは可変個引数テンプレートでした (私のエンジンは C++11 機能を完全にサポートすることを念頭に置いて構築されています):
template <typename Object_t, typename Return_t, typename... Args>
TypeMethod(const char* methodName, Return_t (Object_t::*)(Args&&... args)
{
//What now?
}
ただし、引数から型を抽出する方法がわかりません。関数のオーバーロードを使用するアプローチの記事を見ました。
template <typename P, typename R, typename Arg1, typename... Args>
void Func(R (P::*)(Arg1 arg1, Args&&... args))
{
}
template <typename P, typename R, typename... Args>
void Func(R (P::*)(Args&&... args))
{
}
template <typename P, typename R>
void Func(R (P::*)())
{
}
関数は再帰的に「転送」され(実際の再帰ではないことはわかっています)、毎回パラメーターが1つ少なくなりました。しかし、これが私の場合にどのように適しているかはわかりません。