Question

For example:

class Base1 {};
class Base2 {};
class Derived: publid Base1, public Base2 {};

// object is stored on a void* slot
void* void_slot = new Derived();

// ... many decades after ...

//object is fetched from the void* slot
Base2* obj = (Base2*) void_slot;
obj->some_base2_method();

I think it is probably unsafe. Does dynamic_cast<> solve this issue?

Base2* obj = dynamic_cast<Base2*> void_slot;

More backgrounds:

I'm working on calling C++ library from Perl. When you construct an C++ object, it is stored in the integer slot of a Perl value (the IV value of a SV), which is like a void*; and when you call methods, the object pointer is cast from IV, and the corresponding C++ method is called using the object pointer. Thus I guess it could be problematic, as the pointer to base type can be different with the pointer to derived type, especially when there are multiple inheritance.

I've posted a similar question on PerlMonks, but did not get much response from there. So I ask it here, from the aspect of C++.

Was it helpful?

Solution

Yes it is unsafe, but will probably cause no errors in your example due to the empty base optimization. Consider instead the following example:

class Base1 { int b1; };
class Base2 { int b2; };
class Derived : public Base1, public Base2 { int d; };

The memory layout for an object of type Derived will probably look like this:

0123456789AB   
[b1][b2][ d]
^ begin of Derived
^ begin of Base1
    ^ begin of Base2

Now, a pointer to Derived and to Base1 will have the same numerical value, but one to Base2 will be different. To change the numerical value appropriately, the compiler has to know that you are converting a Derived* to a Base2*. This is not possible when casting it to void* in between, since the value of the void* could just as well have come from a Base2*.

In fact, a conversion sequence like static_cast<T*>(static_cast<void*>(x)) is exactly how reinterpret_cast<T*>(x) is defined. And you would not assume that reinterpret_cast is safe to randomly use an arbitrary types - would you?

What about dynamic_cast?

While one might believe that dynamic_cast might help here, it is in fact not even applicable! Since dynamic_cast is supposed to use run time type information to guarantee that a cast is possible, its target needs to be a pointer (or reference) to a class type with at least one virtual member. In this case, the target is not even a pointer to a complete type, but to void.

How to deal with the conundrum?

No matter what you do afterwards you must retrieve the same type of pointer that you stored (with a sole exception for interpreting your object as a char array). The obvious solution would be, to either always store a pointer to a common base class like

void* void_slot = static_cast<CommonBase*>(input);
CommonBase* output = static_cast<CommonBase*>(void_slot);

or to use an intermediate class that knows which kind of pointer you are talking about

struct Slotty {
    enum class type_t {
        Base1,
        Base2,
        Derived
    } type;
    void* ptr;

    Slotty(Base1* ptr) : type(type_t::Base1), ptr(ptr) { }
    Slotty(Base2* ptr) : type(type_t::Base2), ptr(ptr) { }
    Slotty(Derived* ptr) : type(type_t::Derived), ptr(ptr) { }
};

void* void_slot = static_cast<void*>(new Slotty(input));
Slotty* temp = static_cast<Slotty*>(void_slot);
switch(Slotty.type) {
    case Slotty::type_t::Base1:
        /* do sth with */ static_cast<Base1*>(temp.ptr);
        break;
    case Slotty::type_t::Base2:
        /* do sth with */ static_cast<Base2*>(temp.ptr);
        break;
    case Slotty::type_t::Derived:
        /* do sth with */ static_cast<Derived*>(temp.ptr);
        break;
}

OTHER TIPS

If you have complete control over your classes, just create a single virtual root base. Cast to that first before you cast to void *, then cast back to that first. Then you can use dynamic_cast to cast to whatever derived type you want:

struct Root {
    virtual ~Root() {}
};

struct Base1 : virtual public Root { };
struct Base2 : virtual public Root { };
struct Derived1 : public Base1, public Base2 { };
struct Derived2 : public Derived1 { };

int main() {

    Derived1 *d1 = new Derived1;
    Derived2 *d2 = new Derived2;

    void *vp = static_cast<Root *>(d1);
    Derived1 *d11 = dynamic_cast<Derived1 *>(static_cast<Root *>(vp));
    vp = static_cast<Root *>(d2);
    Derived2 *d22 = dynamic_cast<Derived2 *>(static_cast<Root *>(vp));

    delete d1;
    delete d2;
}

EDIT: Apparently the classes must be polymorphic and you must use dynamic_cast, so put a trivial virtual destructor in Root.

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