Delphi RTTI と C++Builder
RTTI の操作 への移動
目次
実行時型検索(リフレクション)
あいまい性の解消
Delphi RTTI は、標準の C++ RTTI とは以下の点が異なります。
- Delphi RTTI では、クラスのフィールドやメソッドなどを調べることができます。
- C++ RTTI では、変数の型を特定したり、型を比較して等しいかどうかや照合順序を確認することができます。
メモ:[プロジェクト|オプション...|C++ コンパイラ|実行時型情報 (RTTI) を有効にする]のコンパイラ オプションは、標準の C++ RTTI を指しています。
RTTI の生成
C++ で Delphi 形式の RTTI を持つことができるのは Delphi 形式のクラスだけです(「__declspec(delphiclass)」を参照)。
RTTI は、どのような可視性(__published、public、protected、private)のどのような種類のクラス メンバ(メソッド、フィールド、プロパティ)に対しても生成することができます。RTTI の生成を制御するには、以下の指定を使用できます。
- #pragma explicit_rtti({$RTTI} に相当する C++ の機能です)
- __declspec(delphirtti)({$M}/{$TYPEINFO} に相当する C++ の機能です)
- __published
以下に例を示します。
#include <System.hpp>
#include <Rtti.hpp>
#include <tchar.h>
#include <stdio.h>
// Enable RTTI generation for private fields
#pragma explicit_rtti fields(private)
class __declspec(delphiclass) TBuffer {
private:
int wrHead, rdHead;
// ...
public:
TBuffer() {
}
};
// ---------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[]) {
TRttiContext context;
// Get class RTTI
TRttiInstanceType *cls = dynamic_cast<TRttiInstanceType*>
(context.GetType(__delphirtti(TBuffer)));
if (cls) {
// Get field RTTI
TRttiField *field = cls->GetField("wrHead");
if (field) {
printf("%ls", field->ToString().c_str());
}
else {
puts("\"wrHead\" doesn't have RTTI");
}
}
else {
puts("\"TBuffer\" doesn't have RTTI");
}
return 0;
}
コンソール出力は以下のとおりです。
wrHead: int @ 00
この例では、(TTypeInfo 型変数のポインタを返す)__delphirtti 関数を使ってクラスの RTTI を抽出しています。同じことを __classid 演算子を使って行うこともできます。以下のコードを参照してください。
TRttiInstanceType *cls = dynamic_cast<TRttiInstanceType*>
(context.GetType(__classid(TBuffer)));
メモ: __delphirtti で抽出された RTTI 情報はプラットフォーム間で異なる可能性があります。たとえば、iOS と OSX では、DelphiWideChar の Delphi RTTI に一致する PTypeInfo を返すのは、char16_t の __delphirtti です。
Windows プラットフォームでは、DelphiWideChar の Delphi RTTI に一致する PTypeInfo を返すのは、wchar_t の __delphirtti です。
この機能を以下のコード例で示します。
#include <stdio.h>
static System::UTF8String getTypeInfoName(System::Typinfo::PTypeInfo ti)
{
#ifndef _DELPHI_NEXTGEN
return System::UTF8String(ti->Name);
#else
int len = ti->Name;
const char* p = reinterpret_cast<char*>(&(ti->Name));
return System::UTF8String(p+1, len);
#endif
}
int main()
{
System::Typinfo::PTypeInfo pti;
pti = __delphirtti(char16_t);
printf("Kind=%d, Name='%s'\n", pti->Kind, getTypeInfoName(pti).c_str());
pti = __delphirtti(wchar_t);
printf("Kind=%d, Name='%s'\n", pti->Kind, getTypeInfoName(pti).c_str());
return 0;
}
出力は以下のとおりです。
プラットフォーム | 出力 |
---|---|
WIN64 |
Kind=1, Name='char16_t' Kind=9, Name='Char' |
iOS |
Kind=9, Name='Char' Kind=9, Name='wchar_t' |
型情報の格納
FindType メソッドや GetTypes メソッドを使用して、C++ 言語で定義されたクラスの Delphi RTTI を取得することはできません。これは、FindType や GetTypes では実行時パッケージ内で検索を実行しますが、C++ リンカでは型情報の格納が行われないからです。
これに対して、Delphi リンカでは パッケージ情報テーブルに RTTI を格納します。つまり、次のコードのように、Delphi モジュール(パッケージ)に含まれているユニットの RTTI を C++ で参照できることになります。
TRttiContext context;
TRttiType *rttiType = context.FindType("Classes.TStringList");
printf("%ls", rttiType->ToString()); // displays TStringList
メモ: iOS プラットフォームでは、リンクが静的に行われるため、RTTI は格納されません。ただし、次のコード例に示すように、GetType メソッドを使用する場合は、RTTI を取得できます。
TRttiContext context;
TRttiType *rttiType = context.GetType(__delphirtti(Fmx::Layouts::TLayout));
if (rttiType == NULL) {
ShowMessage("RTTI not generated");
} else {
ShowMessage(rttiType->ToString());
}
属性
Delphi 属性は C++Builder でサポートされていません。C++Builder では C++0x 属性をサポートしていて、これは Delphi 属性と以下の点が異なります。
- Delphi 属性はプログラマに追加情報を提供するもので、実行時に抽出可能です。
- C++0x 属性はコンパイラに追加情報を提供するもので、RTTI に特有のものではありません。