指向对象开头的指针(C ++)

时间:2010-08-15 15:39:44

标签: c++ casting rtti typeid

我需要一种方法来获取指向C ++中对象开头的指针。此对象在模板中使用,因此它可以是任何类型(多态或非多态),并且可能是使用多继承的对象。

我发现this article描述了一种方法(在“T”是多态类型的情况下,使用typeid和“dynamic_cast”来描述一种方法(参见“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);
}

感谢。

3 个答案:

答案 0 :(得分:1)

gcc确切的错误消息是

  

错误:无法使用dynamic_cast &n(类型为struct N*)来键入void*(源类型不是多态的)

这可以通过将boost::is_polymorphicboost::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版本。 / p>

我在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); }

答案 1 :(得分:0)

我做了一些调查,你可能在GCC发现了一个错误;我会report it。但是,可能有一些我无法找到的规则,即在这种情况下需要实例化逗号运算符左侧的模板函数,即使未评估整个表达式;在这种情况下,文章中的技术是错误的。

我建议你看看boost::type_traits::is_polymorphic是否适合你。

答案 2 :(得分: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;
};