Oleg Shirokikh
Oleg Shirokikh

Reputation: 3565

C++: Access violation when casting void* to base class pointer

Given a base class B, such as:

    class B
    {
    public:
        virtual ~B() = default;
    public:
        virtual int f() const = 0;
    };

and a number of derived classes Ai: public B (i=1,..,N), implementing f(), I receive a void* definitely holding one of the derived Ai classes from the external program - to execute the f() method.

It's possible to create an entry point for each possible derived type, and it will work fine:

// for each derived class Ai
void executeF(void* aPtr, int* result)
{
    auto aObjPtr = static_cast<Ai*>(aPtr);
    *result = aObjPtr->f();
}

However, it should be possible to achieve the same result with single function only, such as:

void executeF(void* aPtr, int* result)
{
    auto bObjPtr = static_cast<B*>(aPtr); // works
    *result = bObjPtr->f(); // Access violation
}

The case succeeds in the above, but the execution of f() fails with "Access violation" in MSVC 2013.

Is there something wrong with the above function? And if so, is there a way to achieve the task with a single function?

I've read some materials, which claim that one has to cast void* only to the particular class, which it holds (also suggested in the comment below). However, this code compiles and executes fine: http://ideone.com/e0Lr6v

Some more context about how everything is being called:

I can't provide the entire code here because it's too long but in summary.. The function executeF, constructors for objects Ai and everything in the library that defines and operates on objects A, B are provided as exported functions that operate on void* types only. Just FYI, this library is being compiled and build with MSVC 2013.

Another side (the wrapper for R language) is compiled and built with g++ - it loads the above library dynamically, exports needed function and calls it. The only thing that is available on this side is the void* holding objects Ai - it just sends requests to create objects, calls their methods, frees them.

For example (schematically), create an object of type A1:

// "objects" library
void createA1(void** aObj)
{
    *a1Obj = new A1();
}

// caller library
auto const createA1func = (int(__CC *)(void**)) GetProcAddress(getDLL(), "CreateA1");
void* a1Obj = NULL;
createAFunc(a1Obj);
// ... return the a1Obj to the external environemnt to keep it around 

Then, having a1Obj around, do some job with it:

// caller library
auto const executeFfunc = (int(__CC *)(void*, int*)) GetProcAddress(getDLL(), "executeF");
int res(0);
executeFfunc(a1Obj, &res);

So if I write a separate function for each type Ai on both sides, everything works OK. But it'd be significantly less boilerplate code if I can use the base class here somehow.

Upvotes: 1

Views: 1318

Answers (5)

Remy Lebeau
Remy Lebeau

Reputation: 596387

When Ai derives from B, a pointer to the Ai portion of an object is (usually) not pointing at the same memory address as a pointer to the B portion of the same object (especially if B has data fields in it). Accessing Ai via a B* pointer usually involves pointer fixups, VMT lookups, etc, things that have to be taking into account by the particular compiler being used. That is why you can't simply cast an Ai* pointer to a void* to a B* and expect everything to work correctly. The B* pointer is not a valid B* pointer, it is actually an Ai* pointer that has been reinterpretted as a B*, and that simply does not work legally.

To ensure things stay lined up correctly, you must either:

  • cast Ai* to void*, and then void* to Ai*. Which is what you are trying to avoid.

  • cast Ai* to B* first, and then B* to void*, and then void* to B* (and then optionally B* to Ai* via dynamic_cast if you need to access non-virtual members of Ai).

So, in order for this to work the way you are wanting, do the following when constructing your objects:

void createA1(void** aObj)
{
    *aObj = static_cast<B*>(new A1());
}

void createA2(void** aObj)
{
    *aObj = static_cast<B*>(new A2());
}

And so on. This ensures that all pointers passed to executeF() are proper B* pointers, and only then can executeF() safely type-cast its received void* pointer to B* and use polymorphism to access whichever derived class it is actually pointing at:

void executeF(void* aPtr, int* result)
{
    B* bObjPtr = static_cast<B*>(aPtr);
    *result = bObjPtr->f(); // works
}

Update: Alternatively, especially when dealing with multiple derived classes that each have multiple base classes that may or may not all be shared, another option would be to simply wrap the Ai objects in a struct that has an extra field to indicate the object type. Then your create...() functions can return void* pointers to that struct instead of to the Ai objects directly, and the execute...() functions can first cast the void* to that struct, look at its type field, and cast the object pointers accordingly:

enum AType
{
    a1, a2 /*, ... */
};

class B
{
public:
    virtual ~B() = default;
    virtual int f() = 0;
};

class Bx
{
public:
    virtual ~B() = default;
    virtual int x() = 0;
};

class By
{
public:
    virtual ~B() = default;
    virtual int y() = 0;
};

// ...

class A1 : public B, public Bx
{
public:
    int f() override { return 1; }
    int x() override { return 1; }
};

class A2 : public B, public By
{
public:
    int f() override { return 2; }
    int y() override { return 2; }
};

// ...

struct objDesc
{
    AType type;
    void *obj;
};

void createA1(void** aObj)
{
    objDesc *desc = new objDesc;
    desc->type = a1;
    desc->obj = new A1();
    *aObj = desc;
}

void createA2(void** aObj)
{
    objDesc *desc = new objDesc;
    desc->type = a2;
    desc->obj = new A2();
    *aObj = desc;
}

// ...

void destroyObj(void* aObj)
{
    objDesc *desc = static_cast<objDesc*>(aObj);
    switch (desc->type)
    {
        case a1:
            delete static_cast<A1*>(desc->obj);
            break;

        case a2:
            delete static_cast<A2*>(desc->obj);
            break;

        //..
    }

    delete desc;
}

//...

void executeF(void* aPtr, int* result)
{
    objDesc *desc = static_cast<objDesc*>(aPtr);
    B* bObjPtr = nullptr;

    switch (desc->type)
    {
        case a1:
            bObjPtr = static_cast<A1*>(desc->obj);
            break;

        case a2:
            bObjPtr = static_cast<A2*>(desc->obj);
            break;

        // other classes that implement B ...
    }

    if (bObjPtr)
        *result = bObjPtr->f();
}

void executeX(void* aPtr, int* result)
{
    objDesc *desc = static_cast<objDesc*>(aPtr);
    Bx* bObjPtr = nullptr;

    switch (desc->type)
    {
        case a1:
            bObjPtr = static_cast<A1*>(desc->obj);
            break;

        // other classes that implement Bx ...
    }

    if (bObjPtr)
        *result = bObjPtr->x();
}

void executeY(void* aPtr, int* result)
{
    objDesc *desc = static_cast<objDesc*>(aPtr);
    By* bObjPtr = nullptr;

    switch (desc->type)
    {
        case a2:
            bObjPtr = static_cast<A2*>(desc->obj);
            break;

        // other classes that implement By ...
    }

    if (bObjPtr)
        *result = bObjPtr->y();
}

// ...

It is not ideal or flexible, but it will work within the restrictions you have on the other side.

Otherwise, you can replace the struct with a new base class that all other classes must derive from, then you can make use of dynamic_cast as needed:

class Base
{
public:
    virtual ~Base() = default;
};

class Bf
{
public:
    virtual ~Bf() = default;
    virtual int f() = 0;
};

class Bx
{
public:
    virtual ~Bx() = default;
    virtual int x() = 0;
};

class By
{
public:
    virtual ~By() = default;
    virtual int y() = 0;
};

class Bz
{
public:
    virtual ~Bz() = default;
    virtual int z() = 0;
};

class A1 : public Base, public Bf, public Bx
{
public:
    int f() override { return 1; }
    int x() override { return 1; }
};

class A2 : public Base, public Bf, public By
{
public:
    int f() override { return 2; }
    int y() override { return 2; }
};

class A3 : public Base, public Bz
{
public:
    int z() override { return 3; }
};

// ...

void createA1(void** aObj)
{
    *aObj = static_cast<Base*>(new A1());
}

void createA2(void** aObj)
{
    *aObj = static_cast<Base*>(new A2());
}

void createA3(void** aObj)
{
    *aObj = static_cast<Base*>(new A3());
}

// ...

void destroyObj(void* aObj)
{
    delete static_cast<Base*>(aObj);
}

//...

void executeF(void* aPtr, int* result)
{
    Base *base = static_cast<Base*>(aPtr);
    B* bObjPtr = dynamic_cast<B*>(base);
    if (bObjPtr)
        *result = bObjPtr->f();
}

void executeX(void* aPtr, int* result)
{
    Base *base = static_cast<Base*>(aPtr);
    Bx* bObjPtr = dynamic_cast<Bx*>(base);
    if (bObjPtr)
        *result = bObjPtr->x();
}

void executeY(void* aPtr, int* result)
{
    Base *base = static_cast<Base*>(aPtr);
    By* bObjPtr = dynamic_cast<By*>(base);
    if (bObjPtr)
        *result = bObjPtr->y();
}

void executeZ(void* aPtr, int* result)
{
    Base *base = static_cast<Base*>(aPtr);
    By* bObjPtr = dynamic_cast<Bz*>(base);
    if (bObjPtr)
        *result = bObjPtr->z();
}

//...

Upvotes: 2

David Schwartz
David Schwartz

Reputation: 182769

Imagine if there are two types, Base1 and Base2. Say Base1 contains only one member, an integer. And say Base2 contains only one member, a float.

One would expect that a Base1* will point to the integer and a Base2* will point to the float.

Now, consider:

class Derived : public Base1, public Base2
{
    ...

Now, if we cast a Derived* to a void*, we can get a pointer to the integer in the Base1 or we could get a pointer to the float in the Base2. But we cannot possibly get both.

Thus the expectation that you can convert a Derived* to a void* and then cast it back to a pointer to a base class and get something sensible is asking for the impossible. Converting a pointer to a base class into a pointer to a class it is derived from must sometimes change the value of that pointer.

Upvotes: 0

Oleg Shirokikh
Oleg Shirokikh

Reputation: 3565

I came up with the following working solution, which avoids having 2*N functions on both sides, where N is the number of derived A classes. Instead, it involves 2 functions one on each side. The object library has a switch with N cases which casts the void* to the appropriate class. Note that the g++ side does need to be aware of the enum only and still doesn't know anything about types.

Not sure if it's perfect approach, but looks pretty concise and safe. Still interested in other solutions/comments.

http://ideone.com/enNl3f

enum AType
{
    a1 = 1, a2
};

class B
{
public:
    virtual ~B() = default;
public:
    virtual int f() const = 0;
};

class A1: public B
{
    virtual int f() const override
    {
        return 1;
    }
};
class A2: public B
{
    virtual int f() const override
    {
        return 2;
    }
};

void executeF(void* aPtr, AType aType, int* result)
{
    B* bPtr = nullptr;
    switch(aType)
    {
        case a1:
            bPtr = static_cast<A1*>(aPtr);
            break;
        case a2:
            bPtr = static_cast<A2*>(aPtr);
            break;
        default:
            break;
    }

    if(bPtr)
        *result = bPtr->f();
}

Upvotes: 0

AnT stands with Russia
AnT stands with Russia

Reputation: 320531

The behavior you observe simply means that conversion from Ai * to B * is not purely conceptual, but actually requires a physical change in pointer value. In typical implementations this usally happens when:

  1. Class B is not polymorphic and contains subobjects of non-zero size, while class Ai is polymorphic. (Not your case)
  2. Class Ai has multiple bases and B is just one of them.

My guess would be that you are dealing with the second case in your code.

In such cases it might "work" if you ensure that the base B is the very first base of Ai (but, again, this is heavily implementation-dependent and, obviously, unreliable).

Upvotes: 1

Sergio Basurco
Sergio Basurco

Reputation: 4318

The function executeF, constructors for objects Ai...

That is most likely the issue, you shall not call virtuals in the constructor. It works for Ai because Ai is not calling the virtual method from the vptr table. B however has no such table yet if it's being constructed. See this other SO answer.

Upvotes: -2

Related Questions