2010-08-15 2 views
3

Мне нужен способ получить указатель на начало объекта в C++. Этот объект используется внутри шаблона, поэтому он может быть любым типом (полиморфным или нет) и потенциально может быть объектом, который использует множественное наследование.Указатель на начало объекта (C++)

Я нашел this article, который описывает способ его выполнения (см. Раздел «Динамические касты») с использованием typeid и dynamic_cast для void * в случае, когда T является полиморфным типом.

Это отлично работает на MSVC, однако на GCC (4.x) он, похоже, падает на свою задницу и выплевывает ошибку компилятора, когда используется с не полиморфным типом.

Кто-нибудь знает путь к:

  • Сделать НКУ вести себя, и оценить 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); 
} 

Thanks.

+0

$ 10/5 states- «[Примечание: Базовый класс подобъектом может иметь компоновку (3.7), отличную от расположения наиболее производный объект того же типа Базовый класс подобъектом может иметь полиморфное поведение (. 12.7) отличается от полиморфного поведения самого производного объекта того же типа. Субобъект базового класса может иметь нулевой размер (раздел 9), однако два подобъекта, которые имеют один и тот же тип класса и которые принадлежат одному и тому же самому производному объекту не должно выделяться по тому же адресу (5.10).] " – Chubsdad

+0

Что должно произойти с' struct point {int x; int y; }; точка * p = новая точка(); doSomethingWithInstance (&p->x); '? –

+0

Логан, это было бы недопустимым использованием функции. Оно должно использоваться только с экземплярами, которые были выделены в куче. –

ответ

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 здесь; Я бы report it. Тем не менее, может быть какое-то правило, которое я не могу найти, что говорит о том, что функция шаблона в левой части оператора запятой должна быть создана в этом случае, хотя общее выражение не оценивается; в этом случае техника в статье ошибочна.

Предлагаю вам посмотреть, можно ли сделать boost::type_traits::is_polymorphic, чтобы работать на вас.

+0

Спасибо за предложение, однако этот код является частью библиотеки, и я стараюсь избежать необходимой зависимости от Boost. –

+0

Вы можете просто прочитать boost/type_traits/is_polymorphic.hpp и выяснить, что он делает и сделать это самостоятельно :) (Удачи вам в этом, хотя, Boost - это много вещей, но приемлемый не один из них.) – zwol

+0

Да, я Вероятно, я посмотрю на это. Если бы я мог сказать, был ли тип полиморфным во время компиляции, тогда я мог бы использовать специализированную специализацию для решения проблемы. –

1

Точное сообщение об ошибке с помощью GCC является

ошибка: не dynamic_cast &n (типа struct N*) к типу void* (тип источника не полиморфный)

Это может быть обработано используя boost::is_polymorphic в сочетании с boost::enable_if и boost::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 вместо ... не работает.

EDIT:

и это была просто опечатка (забыл ::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); }