Question

I am trying confirm the theory behind something that I have already got working in practice. The full setup is somewhat contorted, as the functionality is split between different dlls, but I'll try to describe the situation:

class __declspec( dllexport ) BaseClass
/** This class' definition is available to everything,
 *  via .h file, .dll and .lib. */
{
    protected:
        std::string name;

    public:
        std::string GetName();
        /** This is implemented in BaseClass, and just returns this->name. */
}

class DerivedClass: public BaseClass
/** This class is created within the executable, but is not 'visible' to other
 *  dlls - either through .h files, .lib, or anything else. */
{
    public:
        DerivedClass();
        /** This sets this->name based on its own propertied. */
}

This upcasting works, but it requires full access to the definition of DerivedClass:

void* pointer;
DerivedClass* derived_pointer = reinterpret_class<DerivedClass*>(pointer);
BaseClass* base_pointer = dynamic_cast<BaseClass*>(derived_pointer);
base_pointer->GetName();

However, the following does NOT work:

void* pointer;
BaseClass* base_pointer = reinterpret_class<BaseClass*>(pointer);
base_pointer->GetName();

In order to get round this problem, I've implemented an interface:

class __declspec( dllexport ) IBaseClass
/** Fully virtual 'interface' class, in same file as BaseClass. */
{
    public:
        virtual std::string GetName() = 0;
}

class __declspec( dllexport ) BaseClass: public IBaseClass
/** This class' definition is available to
 *  everything, via .h file, .dll and .lib. */
{
    protected:
        std::string name;

    public:
        std::string GetName();
        /** This is implemented in BaseClass, and just returns this->name. */
}

class DerivedClass: public BaseClass
/** This class is created within the executable, but is not 'visible'
 *  to other dlls - either through .h files, .lib, or anything else. */
{
    public:
        DerivedClass();
        /** This sets this->name based on its own propertied. */
}

And now the following code does work:

void* pointer;
IBaseClass* ibase_pointer = reinterpret_class<IBaseClass*>(pointer);
ibase_pointer->GetName();

I have some dim memory of somebody telling me that casting to a fully virtual class is a special case - but I can't remember why, or find anything on the web about it.

Please help me - why does my code work?!

Was it helpful?

Solution

This is completely dependent on class layout, which is implementation-defined and cannot be relied on. Specifically for MSVC, a good intro to class layout is http://www.openrce.org/articles/full_view/23 and it's worth knowing that you can ask for class layout with /d1reportSingleClassLayout flag.

In your case, since the first BaseClass has no virtual members it will be placed inside DerivedClass at a non-specified location. I'd guess that DerivedClass has some virtual members as otherwise I'd expect BaseClass to be at the start of DerivedClass and the reinterpret_cast to work. If it has virtual members you'd have:

+--------------------+
|DerivedClass vtable |
|BaseClass::name     |
|DerivedClass members|

By adding the interface IBaseClass nothing changes; DerivedClass is still laid out as:

+--------------------+
|DerivedClass vtable |
|BaseClass::name     |
|DerivedClass members|

However the DerivedClass vtable starts with the IBaseClass vtable:

+---------------------+
|IBaseClass::GetName  |
|DerivedClass virtuals|

so calling through the DerivedClass vtable works.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top