5
 class A : public X;
 class B : public virtual A;
 class C : public virtual A;
 class D1 : public B, public C;
 class D2 : public B, public C;

 void *p1 = new D1; //after storing the pointers,
 void *p2 = new D2; //there will be no exact type info.

 A *pA1 = (A *) p1; // Cast 1
 A *pA2 = (A *) p2;
 X *pX1 = (X *) p1; // Cast 2
 X *pX2 = (X *) p2;

Are those (Cast 1 and Cast 2) casts correct? (except Undefined behavior.) How should i cast them into base classes without exact class type info?

Edit 1:

Please read the question before you mark it as duplicate or answer it. Base class is virtual. It is dreaded diamond problem!

Please don't focus on C-Cast. I know what static/dynamic/reinterpret_cast are.

Edit 2:

void *s are coming from a DWORD_PTR CTreeCtrl::GetItemData(), so it is why i'm using void *. As i'm having void *'s, i have no information about the exact type of classes.

Before dreaded diamond problem, there were only X, A, B, C-Classes and no virtual interitance, so it was possible to cast them into X, because it was the common base class, which comes first in the derived class list (I know this is undefined behavior, but it works).

Solution 1: (Easiest and the correct solution)

Before storing D1 and D2 adresses in void *-Pointers, casting them into common base class. After that, down casting works and also this is not an undefined behavior.

void *p1 = static_cast<X *>(new D1); //or cast to other common base class 'A'
//p2 is the same...

X *pX1 = (X *) p1; //static/reinterpret_cast is better.
//then down cast attempts are possible.
dynamic_cast<D1 *>(pX1); //returns valid pointer
dynamic_cast<D2 *>(pX1); //returns nullptr

Solution 2: (Wrapper class)

Storing a void pointer to wrapper class which holds a pointer to base class X or A is also possible. Like boost::any

Test 1: (Before virtual inheritance)

If there is no virtual inheritance. We have only X, A, B, C-classes.

B b;
C c;

void *pB = &b;
void *pC = &c;

X *pX1 = (X *) pB;
X *pX2 = (X *) pC;

assert(&b == dynamic_cast<B *>(pX1)); // OK
assert(&c == dynamic_cast<C *>(pX2)); // OK

Test 2: (After virtual inheritance) D1 d1; D2 d2;

void *pD1 = &d1;
void *pD2 = &d2;

X *pX1 = (X *) pD1;
X *pX2 = (X *) pD2;
A *pA1 = (A *) pD1;
A *pA2 = (A *) pD2;
B *pB1 = (B *) pD1;
B *pB2 = (B *) pD2;
C *pC1 = (C *) pD1;
C *pC2 = (C *) pD2;

assert(&d1 == dynamic_cast<D1 *>(pB1)); //OK
assert(&d2 == dynamic_cast<D2 *>(pB2)); //OK
assert(&d1 == dynamic_cast<D1 *>(pC1)); //Fails
assert(&d2 == dynamic_cast<D2 *>(pC2)); //Fails

assert(&d1 == dynamic_cast<D1 *>(pX1)); //Access violation by casting
assert(&d2 == dynamic_cast<D2 *>(pX2)); //Access violation by casting
assert(&d1 == dynamic_cast<D1 *>(pA1)); //Access violation by casting
assert(&d2 == dynamic_cast<D2 *>(pA2)); //Access violation by casting

I think by a virtual inheritance, first virtual base B-class will be at first in memory. Tested only with VS2015

  • 2
    Do not use parethetical casting in C++. Pick the appropriate [explicit type conversion](http://en.cppreference.com/w/cpp/language/explicit_cast) and use that instead. (Note that the first conversion listed is the parethetical cast, and it does *different things* depending on context. Pick which other one you really mean and use that instead.) – cdhowie Mar 14 '17 at 16:16
  • 1
    *"Please read the question before you mark it as duplicate or answer. base class is virtual."* The answer to both questions is the same. Whether virtual inheritance is used has nothing to do with how you get from a void-pointer back to an object-pointer. You *must* cast back to the *exact* type of the object pointed to by the void pointer before you do any other casting. **Any other cast is undefined behavior**, I believe when the pointer produced by the invalid cast is dereferenced. (This is why templates are a thing; use them instead of void pointers.) – cdhowie Mar 14 '17 at 16:28
  • 2
    So, to answer your question directly, all of the casts you have presented will invoke UB when you dereference `pA1`, `pA2`, `pX1`, or `pX2`, because you did not cast through `D1*` or `D2*` (as appropriate) -- exactly the same as in the linked duplicate. – cdhowie Mar 14 '17 at 16:31
  • 2
    *"Before dreaded diamond problem ... it was possible to cast them into `X`"* -- Not from a `void*`. In case I have not made it perfectly clear, **it is undefined behavior to cast from a void pointer to *any pointer type except* for the exact type of pointer that was cast *to* the void pointer.** *"There is no way to cast to base classes, without knowing about exact class type?"* Nobody said that. What we are saying is that there is no way to cast *from a void pointer* without knowing the type of pointer that was cast to `void*`. – cdhowie Mar 14 '17 at 16:48
  • 1
    *"i have no information about the exact type of classes"* -- Then it sounds like you *should* be storing some kind of variant type, such as `boost::any`. From such a type, you can [attempt to extract the value](http://www.boost.org/doc/libs/1_61_0/doc/html/boost/any_cast_idp6057536.html) and this will simply fail (returning `nullptr`) if you don't specify the correct type when extracting. This way, you can try several types until you find the correct one. Better would be to wrap the pointer in some object that *does* convey type information to your program so you don't have to try many types. – cdhowie Mar 14 '17 at 16:53
  • @cdhowie thank you for informations. i think i was not clear to explain it. i know it is undefined behavior. Before the changes, we knew that base class pointer adress was same as derived. what i want to know is, has it Class-A already same adress as Derived. This was my question. For a 21 years old software, i don't want to change desing. I only want to know, is it always same as before. – Yusuf R. Karagöz Mar 14 '17 at 16:56
  • @cdhowie yes, after casting to base class, we are attempting to cast derived classes with `dynamic_cast`. A wrapper class is a good idea. Wrapper class can hold a base class pointer, and our tree control can hold `void *`s to this wrapper class. i think `boost::any` is that wrapper, i am true? – Yusuf R. Karagöz Mar 14 '17 at 17:04
  • 1
    Yes, `boost::any` can easily be your wrapper. There's no real way you can test for `D1*` being equal to the corresponding `A*` in all cases due to the way virtual inheritance works. AIUI, it could actually be different for each class that virtually derives `A`, directly or indirectly, so they could be equal for some objects and not others *within the same program execution.* – cdhowie Mar 14 '17 at 17:06
  • Side note, by introducing the wrapper, you will have *two* objects you need to clean up; you will have to destroy the wrapped object as appropriate, and the wrapper object, too. – cdhowie Mar 14 '17 at 17:33
  • @cdhowie it is clear, that wrapper also must be destroyed :). Thanks for the side note. the idea of wrapper class is enough. Anyway i will test for the hierarchy of virtual inheritence in memory. But i will implement the wrapper, although i must implement more. – Yusuf R. Karagöz Mar 14 '17 at 17:52
  • 1
    Voting to reopen as the "duplicate" does not address the virtual base class problem in any way, and the only answer on this question does not address the issue of the two derived classes `D1` and `D2`. This question definitely deserves a better answer. – cmaster - reinstate monica Mar 15 '17 at 09:20

1 Answers1

1

In c++ it is discouraged to use void*, on another hand, it is better practice to use static_cast<T>() and dyanmic_cast<T>().

In other words, the correct way would be to do:

A* pA1 = dynamic_cast<A*>(new D1);

In this case, the compiler can check the relation between A and D1

If you have to handle void*, it is better to use static_cast<T>() to reconvert to the original type, and then to the base class.

void* pD1 = new D1;
A* pA1 = dynamic_cast<A*>(static_cast<D1*>(pD1));

The compiler will have no other choice than trusting that you will give the correct class...

dynamic_cast a void* does not work.

For more information on c++ casts: check this question

Community
  • 1
  • 1
jraynal
  • 507
  • 3
  • 10
  • 1
    To nip it in the bud, using `dynamic_cast<>` to cast from a `void*` to a base type pointer [doesn't work either](http://stackoverflow.com/a/4131138/501250). (It's not mentioned in your answer, but it's perhaps slightly hinted at.) – cdhowie Mar 14 '17 at 16:20
  • 1
    I took into account your comments, I didn't realize that it was confusing... also, I really want to encourage people to not use `void*`in c++ if they can. If my answer is confusing, I will be happy to remove it though... – jraynal Mar 14 '17 at 16:25