C++ Builder 獲取任意一個(gè)類或?qū)ο蟮念惷?/h1>

C++ Builder 參考手冊(cè) ? C++ Builder 獲取任意一個(gè)類或?qū)ο蟮念惷?/strong>


前面寫了兩篇關(guān)于 C++ Builder 反射的文章展懈,應(yīng)該寫第三篇了厌杜。前面兩篇都是關(guān)于從 TObject 繼承的類的反射箩言,那么其他類到底和 TObject 繼承的類有什么不同雷酪?又是因?yàn)槭裁从绊懥似渌惖姆瓷渌蟛樱颗c前一篇文章《C++ Builder 的反射 (二) - Reflection Factory》比較蟀架,其他的類只差一個(gè)獲取類名重挑,從 TObject 繼承的類可以直接通過類名或?qū)ο笾羔樀玫筋惷址夏兀渌念惒荒芄枚恪1疚某绦蚝屠釉?C++ Builder 10.2.3 版本 clang 32位 和 clang64位編譯器測(cè)試通過睡扬。

  1. C++ Builder 獲取類名的方法
    1.1. THsuanluClassName 類及使用方法
    1.2. THsuanluClassName 類的實(shí)現(xiàn)
  2. 獲取類名的示例程序

1. C++ Builder 獲取類名的方法

1.1. THsuanluClassName 類及使用方法

class THsuanluClassName
{
public:
    static UnicodeString _TObject_QualifiedClassName(UnicodeString s); // Vcl.StdCtrls.TMemo -> Vcl::Stdctrls::TMemo
    static UTF8String _TypeNameToQualifiedClassName32(UTF8String s);   // $Hsuanlu@Test@THsuanluClass -> Hsuanlu::Test::THsuanluClass
    static UTF8String _TypeNameToQualifiedClassName64(UTF8String s);   // N7Hsuanlu4Test13THsuanluClassE -> Hsuanlu::Test::THsuanluClass
    static UTF8String _TypeNameToQualifiedClassName(UTF8String s);     // Hsuanlu::Test::THsuanluClass
    static UTF8String _TypeNameToClassName(UTF8String s);              // THsuanluClass

    template <class T>
    static UnicodeString GetClassName(std::true_type)
    {
        return T::ClassName(); // 這是從 TObject 繼承的類的類名
    }
    template <class T>
    static UnicodeString GetClassName(std::false_type)
    {
        return _TypeNameToClassName(typeid(T).name()); // 不是從 TObject 繼承的類的類名
    }
    template <class T>
    static UnicodeString GetClassName(T*) // 通過對(duì)象指針獲取類名
    {
        return GetClassName<T>(std::bool_constant<__is_base_of(TObject,T)>());
    }
    template <class T>
    static UnicodeString GetClassName(void) // 直接獲取類的類名
    {
        return GetClassName<T>(std::bool_constant<__is_base_of(TObject,T)>());
    }

    template <class T>
    static UnicodeString GetQualifiedClassName(std::true_type)
    {
        return _TObject_QualifiedClassName(T::QualifiedClassName()); // 這是從 TObject 繼承的類的類名,帶命名空間
    }
    template <class T>
    static UnicodeString GetQualifiedClassName(std::false_type)
    {
        return _TypeNameToQualifiedClassName(typeid(T).name()); // 不是從 TObject 繼承的類的類名黍析,帶命名空間
    }
    template <class T>
    static UnicodeString GetQualifiedClassName(T*) // 通過對(duì)象指針獲取類名卖怜,帶命名空間
    {
        return GetQualifiedClassName<T>(std::bool_constant<__is_base_of(TObject,T)>());
    }
    template <class T>
    static UnicodeString GetQualifiedClassName(void) // 直接獲取類的類名,帶命名空間
    {
        return GetQualifiedClassName<T>(std::bool_constant<__is_base_of(TObject,T)>());
    }
};

這個(gè)類里面都是靜態(tài)函數(shù)和靜態(tài)函數(shù)模板阐枣,在實(shí)際應(yīng)用上马靠,一般只需要調(diào)用這兩個(gè)成員:
? THsuanluClassName::GetClassName 獲取類名;
? THsuanluClassName::GetQualifiedClassName 獲取帶命名空間的類名蔼两;
由于其他函數(shù)在某些特定環(huán)境可能會(huì)使用甩鳄,所以都放在了 public: 里面。

使用方法:由于類型只能做模板參數(shù)额划,對(duì)象指針可以做函數(shù)參數(shù)妙啃,所以帶參數(shù)的函數(shù)版本的參數(shù)是對(duì)象指針,不帶參數(shù)的函數(shù)利用模板參數(shù)直接獲取類的類名俊戳。

例如:
s = THsuanluClassName::GetClassName<TMemo>();
s = THsuanluClassName::GetClassName(Memo1);
都將得到 L"TMemo" 字符串揖赴。

s = THsuanluClassName::GetQualifiedClassName<TMemo>();
s = THsuanluClassName::GetQualifiedClassName(Memo1);
都將得到 L"Vcl::Stdctrls::TMemo" 字符串

通過 this 指針也可以得到當(dāng)前代碼所在類的類名茁瘦,例如在窗口 Form1 的代碼里面 THsuanluClassName::GetClassName(this) 可以得到字符串 "TForm1"

1.2. THsuanluClassName 類的實(shí)現(xiàn)

#include <memory>
#include <cxxabi.h> // $(BDS)\source\cpprtl\Source\libcxxabi\win64\include

UnicodeString THsuanluClassName::_TObject_QualifiedClassName(UnicodeString s)
{
    std::auto_ptr<TStringList> slNames(new TStringList);
    slNames->TrailingLineBreak = false;
    slNames->LineBreak = L".";
    slNames->Text = s;
    slNames->LineBreak = L"::";
    int iCount = slNames->Count;
    for(int iIdx=0; iIdx<iCount-1; iIdx++) // 處理命名空間
    {
        UnicodeString s = slNames->Strings[iIdx];      // 從 TObject 繼承的類的命名空間
        slNames->Strings[iIdx] = s.SubString(1,1).UpperCase()         // 第一個(gè)字母大寫
                               + s.SubString(2,s.Length()).LowerCase(); // 其余字母小寫
    }
    return slNames->Text;
}

UTF8String THsuanluClassName::_TypeNameToQualifiedClassName32(UTF8String s)
{
    UTF8String sReal;
    const char *pChar = s.c_str();
    if(*pChar++ == '$')
    {
        bool bTemplate = false;
        while(*pChar)
        {
            if(bTemplate)
            {
                if(*pChar == '$')
                    break;
                sReal += *pChar;
            }
            else switch(*pChar)
            {
                case '%': bTemplate = true; break;
                case '@': sReal += "::"; break;
                default : sReal += *pChar; break;
            }
            pChar++;
        }
    }
    return sReal;
}

UTF8String THsuanluClassName::_TypeNameToQualifiedClassName64(UTF8String s)
{
    UTF8String sReal;
    char *psname = abi::__cxa_demangle(s.c_str(), nullptr, nullptr, nullptr);
    if(psname)
    {
        sReal = psname;
        free(psname);
    }
    return sReal;
}

UTF8String THsuanluClassName::_TypeNameToQualifiedClassName(UTF8String s)
{
    UTF8String sRealName = _TypeNameToQualifiedClassName32(s);
    if(sRealName.IsEmpty())
        sRealName = _TypeNameToQualifiedClassName64(s);
    if(sRealName.IsEmpty())
        sRealName = s;
    int iPos = sRealName.Pos("<");
    if(iPos > 0)
        sRealName = sRealName.SubString(1, iPos-1);
    return sRealName;
}

UTF8String THsuanluClassName::_TypeNameToClassName(UTF8String s)
{
    UTF8String sRealName = _TypeNameToQualifiedClassName(s);
    const char *pLastColon = std::strrchr(sRealName.c_str(),':');
    if(pLastColon)
        return pLastColon+1;
    return sRealName;
}

標(biāo)準(zhǔn) C++ 的類,可以通過 typeid(T).name() 獲取類型名储笑,對(duì)于類來說,不僅僅是類名圆恤,還包含了命名空間和一些其他信息突倍,這個(gè)格式并沒有統(tǒng)一標(biāo)準(zhǔn),gcc 和 clang 都給出了 abi::__cxa_demangle 函數(shù)把類型名轉(zhuǎn)成類名盆昙,包含在頭文件 cxxabi.h 里面羽历。

C++ Builder 使用 clang 編譯器,所以也應(yīng)該有這個(gè)頭文件淡喜,結(jié)果在 C++ Builder 的文件夾里面找到了在 $(BDS)\source\cpprtl\Source\libcxxabi\win64\include 這個(gè)文件夾里面秕磷,為什么在 Win64 里面,其他平臺(tái)里面沒有炼团?經(jīng)過測(cè)試澎嚣,Win32 使用這個(gè)頭文件可以編譯通過,但是無法把類型名轉(zhuǎn)成類名瘟芝,Win64 可以轉(zhuǎn)換成功易桃,所以頭文件就在 Win64 文件夾里面。

經(jīng)過測(cè)試锌俱,C++ Builder 的 Win32 編譯器類的類型名都是 $ 字符開頭晤郑,命名空間之間用 @ 分割,例如 $Hsuanlu@Test@THsuanluClass 即為 Hsuanlu::Test::THsuanluClass贸宏,而 Win64 編譯器生成的類名都是字母或數(shù)字開頭的造寝,所以程序就簡(jiǎn)單了,先判斷如果 $ 開頭使用這個(gè)規(guī)則吭练,不是 $ 開頭的使用 abi::__cxa_demangle 函數(shù)轉(zhuǎn)換诫龙。

由于從 TObject 繼承的類與標(biāo)準(zhǔn) C++ 的類使用不同的 RTTI,TObject 是為了兼容 Delphi 程序线脚,使用的是兼容的 Delphi RTTI赐稽,而其他的不從 TObject 繼承的類,都是使用的標(biāo)準(zhǔn) C++ RTTI浑侥,這兩種 RTTI 不兼容姊舵,如果用 typeid(T).name() 獲取 TObject 繼承的類的類型名會(huì)拋出內(nèi)存訪問錯(cuò)誤的異常,所以程序必須先判斷是否從 TObject 繼承寓落,然后再判斷是否 $ 開頭的類型名括丁。

函數(shù) _TObject_QualifiedClassName 是把 TObject::QualifiedClassName 獲取到的帶命名空間的類名轉(zhuǎn)成 C++ 格式,因?yàn)橹苯荧@取到的命名空間和類名之間用 . 分割伶选,需要改成 ::史飞,并且命名空間的大小寫也和實(shí)際不符尖昏,所有這些類的命名空間應(yīng)該是開頭字母大寫,其余小寫构资,所以用這個(gè)函數(shù)轉(zhuǎn)換抽诉。

函數(shù) _TypeNameToQualifiedClassName32 把 $ 開頭的類型名轉(zhuǎn)成帶命名空間的類名,如果類型是模板吐绵,類名會(huì)在第一個(gè) % 和之后的第一個(gè) $ 之間迹淌。

函數(shù) _TypeNameToQualifiedClassName64 把字母和數(shù)字開頭的類型名,使用 abi::__cxa_demangle 轉(zhuǎn)成類名己单。

函數(shù) _TypeNameToQualifiedClassName 把類型名轉(zhuǎn)成帶命名空間的類名唉窃,先使用 32 位那個(gè) $ 開頭的轉(zhuǎn)換,如果轉(zhuǎn)換失敗纹笼,即開頭不是 $纹份,那么就使用 64 位那個(gè)字母或數(shù)字開頭的類型名轉(zhuǎn)換,如果都失敗了廷痘,直接返回類型名蔓涧。如果是模板,把模板參數(shù)去掉牍疏,即把第一個(gè) < 和后面的東西刪掉蠢笋。

函數(shù) _TypeNameToClassName 把類型名轉(zhuǎn)成類名,先調(diào)用 _TypeNameToQualifiedClassName 得到帶命名空間的類名鳞陨,然后把命名空間去掉昨寞,保留最后一個(gè) : 后面的部分。

由于 typeid(T).name() 返回的類型名是 UTF-8 編碼的厦滤,所以處理類型名的函數(shù)都是 UTF8String 類型的參數(shù)和返回值援岩,在 GetClassName 和 GetQualifiedClassName 里面調(diào)用并轉(zhuǎn)成 UnicodeString 類型的,這也說明類型是可以用漢字或其他非英語(yǔ)語(yǔ)言的掏导。

函數(shù)模板 GetClassName 使用 __is_base_of(TObject,T) 判斷 T 是為 TObject 或他的子類享怀,如果是,使用 std::true_type 參數(shù)的函數(shù)趟咆,如果否添瓷,使用 std::false_type 參數(shù)的函數(shù)。使用這個(gè)方法值纱,而不是使用 if else鳞贷,原因是 if else 無論是否滿足條件都要編譯,不滿足條件會(huì)語(yǔ)法錯(cuò)誤虐唠,而這個(gè)方法搀愧,不滿足條件不但不會(huì)執(zhí)行,也不會(huì)編譯和檢查語(yǔ)法錯(cuò)誤。


2. 獲取類名的示例程序

通過自己寫的類 (非 TObject 繼承) 和控件類 (TObject 繼承) 測(cè)試獲取類名咱筛。
包括帶命名空間和不帶命名空間的搓幌,同時(shí)也測(cè)試了漢字類名。

namespace Hsuanlu {
    namespace Test {
        class THsuanluClass
        {
        public:
            THsuanluClass(){}
            virtual ~THsuanluClass(){}

            class TTest1{};
        };
    }

    class 玄坴測(cè)試類
    {
    public:
        玄坴測(cè)試類(){}
    };

    template<class T>
    class THsuanluTemp : public Test::THsuanluClass
    {
    };
}

using namespace Hsuanlu;
using namespace Hsuanlu::Test;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    THsuanluClass Obj;
    THsuanluClass::TTest1 t1;
    玄坴測(cè)試類 Test;
    const THsuanluClass Obj1;
    THsuanluTemp<double> Temp;

    Memo1->Lines->Add(THsuanluClassName::GetClassName(this));
    Memo1->Lines->Add(THsuanluClassName::GetClassName(Memo1));
    Memo1->Lines->Add(THsuanluClassName::GetClassName(&Obj));
    Memo1->Lines->Add(THsuanluClassName::GetClassName(&Obj1));
    Memo1->Lines->Add(THsuanluClassName::GetClassName(&t1));
    Memo1->Lines->Add(THsuanluClassName::GetClassName(&Test));
    Memo1->Lines->Add(L"");
    Memo1->Lines->Add(THsuanluClassName::GetClassName<TForm>());
    Memo1->Lines->Add(THsuanluClassName::GetClassName<TMemo>());
    Memo1->Lines->Add(THsuanluClassName::GetClassName<THsuanluClass>());
    Memo1->Lines->Add(THsuanluClassName::GetClassName<THsuanluClass::TTest1>());
    Memo1->Lines->Add(THsuanluClassName::GetClassName<玄坴測(cè)試類>());
    Memo1->Lines->Add(THsuanluClassName::GetClassName<THsuanluTemp<UnicodeString>>());
    Memo1->Lines->Add(THsuanluClassName::GetClassName(&Temp));
    Memo1->Lines->Add(L"");
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName(this));
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName(Memo1));
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName(&Obj));
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName(&Obj1));
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName(&t1));
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName(&Test));
    Memo1->Lines->Add(L"");
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName<TForm>());
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName<TMemo>());
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName<THsuanluClass>());
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName<THsuanluClass::TTest1>());
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName<玄坴測(cè)試類>());
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName<THsuanluTemp<UnicodeString>>());
    Memo1->Lines->Add(THsuanluClassName::GetQualifiedClassName(&Temp));
}

運(yùn)行結(jié)果:

獲取類名測(cè)試程序的運(yùn)行結(jié)果

相關(guān):


C++ Builder 參考手冊(cè) ? C++ Builder 獲取任意一個(gè)類或?qū)ο蟮念惷?/strong>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末饲趋,一起剝皮案震驚了整個(gè)濱河市叉钥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌篙贸,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枫疆,死亡現(xiàn)場(chǎng)離奇詭異爵川,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)息楔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門寝贡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人值依,你說我怎么就攤上這事圃泡。” “怎么了愿险?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵颇蜡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我辆亏,道長(zhǎng)风秤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任扮叨,我火速辦了婚禮缤弦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘彻磁。我一直安慰自己碍沐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布衷蜓。 她就那樣靜靜地躺著累提,像睡著了一般。 火紅的嫁衣襯著肌膚如雪恍箭。 梳的紋絲不亂的頭發(fā)上刻恭,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼鳍贾。 笑死鞍匾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骑科。 我是一名探鬼主播橡淑,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼咆爽!你這毒婦竟也來了梁棠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤斗埂,失蹤者是張志新(化名)和其女友劉穎符糊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呛凶,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡男娄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漾稀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片模闲。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖崭捍,靈堂內(nèi)的尸體忽然破棺而出尸折,到底是詐尸還是另有隱情,我是刑警寧澤殷蛇,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布实夹,位于F島的核電站,受9級(jí)特大地震影響粒梦,放射性物質(zhì)發(fā)生泄漏收擦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一谍倦、第九天 我趴在偏房一處隱蔽的房頂上張望塞赂。 院中可真熱鬧,春花似錦昼蛀、人聲如沸宴猾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仇哆。三九已至,卻和暖如春夫植,著一層夾襖步出監(jiān)牢的瞬間讹剔,已是汗流浹背油讯。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留延欠,地道東北人陌兑。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像由捎,于是被迫代替她去往敵國(guó)和親兔综。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容