2010-08-15 171 views
3

我需要一種方法來獲取指向C++中對象開始的指針。該對象在模板內部使用,因此它可以是任何類型(多態或不),並且可能是使用多重繼承的對象。指向對象開始的指針(C++)

我發現this article它描述了一種在T是多態類型的情況下使用typeid和dynamic_cast來void *的方法(參見「Dynamic Casts」一節)。

這對MSVC非常有效,但是在GCC(4.x)上它似乎落在它的屁股上,並且在與非多態類型一起使用時吐出編譯器錯誤。

有誰知道一個辦法:

  • 讓GCC的行爲本身,並評估typeid的正確
  • 或者另一種方式來做到這一點,這將彙編GCC

下面是我目前正在使用的代碼嘗試實現這一點。

template <typename T> 
void* dynamicCastToVoidPtr(T *const ptr) 
{ 
    // This is done using a separate function to avoid a compiler error on some 
    // compilers about non-polymorphic types when calling startOfObject 
    return dynamic_cast<void*>(ptr); 
} 

template <typename T> 
void* startOfObject(T *const ptr) 
{ 
    // In cases of multiple inheritance, a pointer may point to an offset within 
    // another object 
    // This code uses a dynamic_cast to a void* to ensure that the pointer value 
    // is the start of an object and not some offset within an object 
    void *start = static_cast<void*>(ptr); 
    if(start) 
     typeid(start = dynamicCastToVoidPtr(ptr), *ptr); 
    return start; 
} 

template <typename T> 
void doSomethingWithInstance(T *const instance) 
{ 
    // Here is where I need to get a void* to the start of the object 
    // You can think of this as the deleteInstance function of my memory pool 
    // where the void* passed into freeMemory should point to the 
    // start of the memory that the memory pool returned previously 
    void *start = startOfObject(instance); 
    if(start) 
     allocator->freeMemory(start); 
} 

謝謝。

+0

$ 10/5明─「[注:甲基類子對象可能有一個佈局(3.7)從同一類型的最派生對象的佈局不同甲基類子對象可能有一個多態行爲(。 12.7)與同一類型的大多數派生對象的多態行爲不同,基類子對象的大小可以爲零(第9章);但是,兩個子對象具有相同的類類型並且屬於同一個最大派生對象不能在同一地址(5.10)中分配。]「 – Chubsdad 2010-08-15 15:47:46

+0

應該發生什麼'struct point {int x; int y; }; point * p = new point(); doSomethingWithInstance(&p->x);'? – 2010-08-15 15:50:16

+0

Logan;這將是該函數的一個無效使用,它只能用於已經分配到堆上的實例 – 2010-08-15 15:58:30

回答

0

我已經找到了解決方案,從another question,讓我在編譯的時候,如果一個工作了類型是多態的,然後我可以使用這個與模板特化來使用正確類型的轉換。顯然,如果編譯器在子對象之間添加填充,此方法可能會中斷,但是我希望可以在某些已知案例中添加一些編譯時斷言來捕獲它。它在MSVC和GCC上編譯並正確運行。

這是解決類型是多態的代碼。

#define SIMPLE_POLYMORPHIC(TYPE, POLYMORPHIC) \ 
    template <>         \ 
    struct IsPolymorphic<TYPE>     \ 
    {           \ 
     static const bool value = POLYMORPHIC; \ 
    }; 

template <typename T> 
struct IsPolymorphic 
{ 
    struct Derived : public T { virtual ~Derived(); }; 
    static const bool value = (sizeof(Derived) == sizeof(T)); 
}; 

SIMPLE_POLYMORPHIC(int, false); 
SIMPLE_POLYMORPHIC(unsigned int, false); 
// ... do this for all intrinsic or non-derivable types 

根據類型是否爲多態來執行轉換的代碼。

template <typename T, bool isPolymorphic = IsPolymorphic<T>::value> 
struct StartOfObject 
{ 
    static void* getStart(T *const ptr) 
    { 
     return static_cast<void*>(ptr); 
    } 
}; 

template <typename T> 
struct StartOfObject<T, true> 
{ 
    static void* getStart(T *const ptr) 
    { 
     if(ptr) 
      return dynamic_cast<void*>(ptr); 
     return NULL; 
    } 
}; 

以及它的測試用例。

#define CLASS_STUFF(CLASS)  \ 
    public:      \ 
     CLASS() {}    \ 
     virtual ~CLASS() {}  \ 
     int m_##CLASS; 

class A 
{ 
    CLASS_STUFF(A); 
}; 

class B : public A 
{ 
    CLASS_STUFF(B); 
}; 

class C 
{ 
}; 

#include <iostream> 

int main() 
{ 
    std::cout << IsPolymorphic<A>::value << std::endl; 
    std::cout << IsPolymorphic<B>::value << std::endl; 
    std::cout << IsPolymorphic<C>::value << std::endl; 
    std::cout << IsPolymorphic<int>::value << std::endl; 

    StartOfObject<A>::getStart(new A()); 
    StartOfObject<B>::getStart(new B()); 
    StartOfObject<C>::getStart(new C()); 
    StartOfObject<int>::getStart(new int()); 

    return 0; 
}; 
0

我做了一些調查,而你可能在這裏發現了GCC中的一個bug;我會report it。然而,可能有一些規則我找不到說,逗號運算符左側的模板函數確實需要在這種情況下實例化,即使未對整體表達式進行求值;在這種情況下,文章中的技巧就是錯誤的。

我建議你看看boost::type_traits::is_polymorphic是否可以爲你工作。

+0

感謝您的建議,但是,此代碼是庫的一部分,我試圖避免對Boost進行必要的依賴。 – 2010-08-15 17:31:40

+0

你可以閱讀boost/type_traits/is_polymorphic.hpp並找出它在做什麼,並自己做:)(祝你好運,雖然,Boost是很多東西,但可理解性不是其中之一。) – zwol 2010-08-15 19:06:09

+0

是的,我可能會看看這個。如果我能在編譯時判斷類型是否是多態的,那麼我可以使用模板特化來解決問題。 – 2010-08-16 08:00:39

1

與gcc確切的錯誤信息是

錯誤:不能dynamic_cast的&n(的struct N*型)鍵入void*(源類型不是多態)

這可以通過處理使用boost::is_polymorphic結合boost::enable_ifboost::disable_if,不幸的是與明顯的方法gcc扼流圈,所以這裏是解決方法:

template <class T> 
void* address_of_impl(T* p, boost::enable_if< boost::is_polymorphic<T>, int >) 
{ 
    return dynamic_cast<void*>(p); 
} 

template <class T> 
void* address_of_impl(T* p, ...) { return static_cast<void*>(p); } 

template <class T> 
void* address_of(T* p) { return address_of_impl(p, 0); } 

當我們使用SFINAE在我們的優勢(省略號始終被認爲是去年在重載所以編譯器首先嚐試使用dynamic_cast版本,無法對非多態類型,因爲enable_if)。

我已經在gcc 3.4上測試過了,它通過了。我正在調查another question爲什麼使用disable_if而不是...不起作用。

編輯

,這是一個簡單的拼寫錯誤(忘了::type位):

template <class T> 
typename boost::enable_if< boost::is_polymorphic<T>, void* >::type 
address_of(T* p) { return dynamic_cast<void*>(p); } 

template <class T> 
typename boost::disable_if< boost::is_polymorphic<T>, void* >::type 
address_of(T* p) { return static_cast<void*>(p); }