Указатель на начало объекта (C ++) - PullRequest
3 голосов
/ 15 августа 2010

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

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

Это прекрасно работает на 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);
}

Спасибо.

Ответы [ 3 ]

1 голос
/ 16 августа 2010

Точное сообщение об ошибке от 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, и он прошел. Я исследую в другой вопрос , почему использование 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); }
0 голосов
/ 16 августа 2010

Я нашел решение из другого вопроса , который позволяет мне работать во время компиляции, если тип является полиморфным, и затем я могу использовать его со специализацией шаблона для использования правильного типа приведения. Очевидно, этот метод может сломаться, если компилятор добавляет заполнение между подобъектами, но я надеюсь, что я добавлю некоторые утверждения времени компиляции в некоторых известных случаях, чтобы поймать это. Он правильно компилируется и работает как на 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 голосов
/ 15 августа 2010

Я провел некоторое исследование, и вы можете нашли ошибку в GCC здесь; Я бы сообщил об этом . Тем не менее, может быть какое-то правило, которое я не могу найти, которое говорит, что функция шаблона в левой части оператора запятой должна быть создана в этом случае, даже если общее выражение не оценивается; в этом случае методика в статье просто неверна.

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

...