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
この例では、__delphirtti 関数(TTypeInfo 変数のポインタを返す)を使ってクラス RTTI を取り出しています。 同じことを __classid 演算子を使って行うこともできます。 以下のコードを参照してください。
TRttiInstanceType *cls = dynamic_cast<TRttiInstanceType*>
(context.GetType(__classid(TBuffer)));
メモ: __delphirtti で抽出された RTTI 情報はプラットフォーム間で異なる可能性があります。 たとえば、iOS や macOS では、char16_t の __delphirtti は、Delphi WideChar の Delphi の RTTI に一致する PTypeInfo を返します。
たとえば、Windows プラットフォームでは、char16_t の __delphirtti は、Delphi WideChar の Delphi の RTTI に一致する PTypeInfo を返します。
この機能を以下のコード例で示します。
#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++11 属性をサポートしており、これは、Delphi 属性とは異なります:
- Delphi 属性はプログラマに追加情報を提供するもので、実行時に抽出可能です。
- C++11 属性はコンパイラに追加情報を提供するもので、RTTI に特有のものではありません。