Pointer to the start of an object (C++)

一笑奈何 提交于 2019-12-06 00:28:01
Matthieu M.

The exact error message by gcc is

error: cannot dynamic_cast &n (of type struct N*) to type void* (source type is not polymorphic)

This could be handled by using boost::is_polymorphic in combination with boost::enable_if and boost::disable_if, unfortunately gcc chokes with the obvious approach, so here is the workaround:

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

Where we use SFINAE at our advantage (the ellipsis is always considered last in overload resolution so the compiler first attempts to use the dynamic_cast version which fails for non polymorphic types because of enable_if).

I've test it on gcc 3.4 and it passed. I'm investigating in another question why using disable_if instead of ... doesn't work.

EDIT:

and it was a simple typo (forgot the ::type bit):

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

I did some investigation, and you may have found a bug in GCC here; I would report it. However, there may be some rule I can't find that says that the template function on the left side of the comma operator does need to be instantiated in this case, even though the overall expression is not evaluated; in that case the technique in the article is just wrong.

I suggest you see if boost::type_traits::is_polymorphic can be made to work for you.

MrD.

I've found a solution from another question that lets me work out at compile time if a type is polymorphic and I can then use this with template specialisation to use the correct type of cast. Apparently this method might break if the compiler adds padding between sub-objects, but I can hopefully add some compile time asserts on some known cases to catch that. It does compile and run correctly on both MSVC and GCC.

This is the code to work out if a type is polymorphic.

#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

The code to perform the casting based on whether the type is polymorphic.

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;
    }
};

And a test case for it.

#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;
};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!