簡體   English   中英

規避傳統代碼的RTTI

[英]Circumventing RTTI on legacy code

我一直在尋找避免動態轉換類型檢查緩慢的方法。 在您開始說我應該重新設計一切之前,讓我通知您設計是在5年前決定的。 我無法修復之后的所有40萬行代碼(我希望可以),但是我可以進行一些更改。 我已經在類型識別上運行了這個小測試:

#include <iostream>
#include <typeinfo>
#include <stdint.h>
#include <ctime>

using namespace std;

#define ADD_TYPE_ID \
    static intptr_t type() { return reinterpret_cast<intptr_t>(&type); }\
    virtual intptr_t getType() { return type(); }

struct Base
{
    ADD_TYPE_ID;
};

template <typename T>
struct Derived : public Base
{
    ADD_TYPE_ID;
};

int main()
{
    Base* b = new Derived<int>();
    cout << "Correct Type: " << (b->getType() == Derived<int>::type()) << endl; // true
    cout << "Template Type: " << (b->getType() == Derived<float>::type()) << endl; // false
    cout << "Base Type: " << (b->getType() == Base::type()) << endl; // false

    clock_t begin = clock();
    {
        for (size_t i = 0; i < 100000000; i++)
        {
            if (b->getType() == Derived<int>::type())
                Derived <int>* d = static_cast<Derived<int>*> (b);
        }
    }
    clock_t end = clock();
    double elapsed = double(end - begin) / CLOCKS_PER_SEC;

    cout << "Type elapsed: " << elapsed << endl;

    begin = clock();
    {
        for (size_t i = 0; i < 100000000; i++)
        {
            Derived<int>* d = dynamic_cast<Derived<int>*>(b);
            if (d);
        }
    }
    end = clock();
    elapsed = double(end - begin) / CLOCKS_PER_SEC;

    cout << "Type elapsed: " << elapsed << endl;

    begin = clock();
    {
        for (size_t i = 0; i < 100000000; i++)
        {
            Derived<int>* d = dynamic_cast<Derived<int>*>(b);
            if ( typeid(d) == typeid(Derived<int>*) )
                static_cast<Derived<int>*> (b);
        }
    }
    end = clock();
    elapsed = double(end - begin) / CLOCKS_PER_SEC;

    cout << "Type elapsed: " << elapsed << endl;

   return 0;
}

似乎使用類ID(上面的第一次解決方案)將是在運行時進行類型檢查的最快方法。 這會導致線程問題嗎? 有沒有更好的方法在運行時檢查類型(沒有太多的重構)?

編輯:可能我還要補充一點,這需要與TI編譯器一起使用,目前僅支持'03

首先,請注意, dynamic_cast和RTTI之間有很大的區別:強制轉換告訴您是否可以將基礎對象視為某些進一步派生的對象,但不一定是最衍生的對象。 RTTI告訴您最精確的最派生類型。 自然,前者功能更強大且價格更高。

因此,如果您具有多態層次結構,則有兩種自然的方法可以選擇類型。 他們是不同的。 使用實際適用的那個。

void method1(Base * p)
{
    if (Derived * q = dynamic_cast<Derived *>(p))
    {
        // use q
    }
}

void method2(Base * p)
{
    if (typeid(*p) == typeid(Derived))
    {
        auto * q = static_cast<Derived *>(p);

        // use q
    }
}

還要注意,如果基類是虛擬基類,則方法2通常不可用。 如果您的類不是多態的,則這兩種方法都不適用。

在快速測試中,我發現方法2比基於手動ID的手動解決方案要快得多,而基於ID的手動解決方案又比動態強制轉換解決方案(方法1)快。

比較類的虛函數表怎么樣?

快速而骯臟的概念證明:

void* instance_vtbl(void* c)
{
    return *(void**)c;
}

template<typename C>
void* class_vtbl()
{
    static C c;
    return instance_vtbl(&c);
}

// ...

begin = clock();
{
    for (size_t i = 0; i < 100000000; i++)
    {
        if (instance_vtbl(b) == class_vtbl<Derived<int>>())
            Derived <int>* d = static_cast<Derived<int>*> (b);
    }
}
end = clock();
elapsed = double(end - begin) / CLOCKS_PER_SEC;

cout << "Type elapsed: " << elapsed << endl;

使用Visual C ++的/Ox開關,它的顯示速度比type / getType技巧快3倍。

給定這種類型的代碼

class A {
};

class B : public A {
}

A * a;
B * b = dynamic_cast<B*> (a);
if( b != 0 ) // do something B specific

修復它的多態方法(對嗎?)是這樣的

class A {
public:
    virtual void specific() { /* do nothing */ }
};

class B : public A {
public:
    virtual void specific() { /* do something B specific */ }
}

A * a;
if( a != 0 ) a->specific();

當MSVC 2005首次發布時,針對64位代碼的dynamic_cast <>比針對32位代碼的dynamic_cast <>要慢得多。 我們想要一個快速簡單的修復程序。 這就是我們的代碼。 它可能違反了各種好的設計規則,但是刪除dynamic_cast <>的轉換可以使用腳本自動進行。

class dbbMsgEph {
public:
    virtual dbbResultEph *              CastResultEph() { return 0; }
    virtual const dbbResultEph *        CastResultEph() const { return 0; }
};

class dbbResultEph : public dbbMsgEph {
public:
    virtual dbbResultEph *              CastResultEph() { return this; }
    virtual const dbbResultEph *        CastResultEph() const { return this; }
    static dbbResultEph *               Cast( dbbMsgEph * );
    static const dbbResultEph *         Cast( const dbbMsgEph * );
};

dbbResultEph *
dbbResultEph::Cast( dbbMsgEph * arg )
{
    if( arg == 0 ) return 0;
    return arg->CastResultEph();
}

const dbbResultEph *
dbbResultEph::Cast( const dbbMsgEph * arg )
{
    if( arg == 0 ) return 0;
    return arg->CastResultEph();
}

當我們曾經有

dbbMsgEph * pMsg;
dbbResultEph * pResult = dynamic_cast<dbbResultEph *> (pMsg);

我們將其更改為

dbbResultEph * pResult = dbbResultEph::Cast (pMsg);

使用簡單的sed(1)腳本。 虛擬函數調用非常有效。

//在發布模塊(VS2008)中,這是正確的:

cout << "Base Type: " << (b->getType() == Base::type()) << endl;

我想這是因為優化,所以我更改了Derived :: type()的實現

template <typename T>
struct Derived : public Base
{
    static intptr_t type() 
    { 
        cout << "different type()" << endl;
        return reinterpret_cast<intptr_t>(&type); 
    }
    virtual intptr_t getType() { return type(); }
};

那就不一樣了,所以如果使用這種方法該如何處理呢?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM