• We’re currently investigating an issue related to the forum theme and styling that is impacting page layout and visual formatting. The problem has been identified, and we are actively working on a resolution. There is no impact to user data or functionality, this is strictly a front-end display issue. We’ll post an update once the fix has been deployed. Thanks for your patience while we get this sorted.

Anyone familiar with COM?

TecHNooB

Diamond Member
Cliffs: Why does pab == pac? (see code)

I'm reading a vanilla COM book about how to implement basic COM in C++. The trick is basically encapsulated in this piece of code (I think).

The class hierarchy is basically a diamond (A top, B left, C right, D bottom) and uses non virtual inheritance. Somehow, with this code, you get a hybrid between virtual and nonvirtual inheritance behavior but I don't know how/why.

Basically, you start by making a D object and a pointer to D. Because the inheritance is nonvirtual, there are two copies of A and you have to select which A you want (B's A or C's A). So if you want to get a pointer to A, you have to first cast to B or C and then to A.

Somehow, // #1 (see comment in code) always casts the pointer to the same A. What is happening in memory/compiler land that causes this to happen?

Note: Some includes are missing.
Code:
#include "stdafx.h"
#include <iostream>

using namespace std;

struct A
{
	virtual void Afoo() = 0;
};

struct B : A 
{
	virtual void Bfoo() = 0;
};

struct C : A 
{
	virtual void Cfoo() = 0;
};

class D : public B, public C
{
public:
	virtual void Afoo(){ cout << "AFOO" << endl; }
	virtual void Bfoo(){ cout << "BFOO" << endl; }
	virtual void Cfoo(){ cout << "CFOO" << endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{	
	D *pd = new D();

	// #1
	A *pab = static_cast<B*>((void*)pd);
	A *pac = static_cast<C*>((void*)pd);

	cout << "pab = " << pab << endl;
	cout << "pac = " << pac << endl;

	B *pb = pd;
	C *pc = pd;

	pb->Bfoo();
	pc->Cfoo();

	cout << "pb = " << pb << endl;
	cout << "pc = " << pc << endl;

	// #2
	A *pab2 = pb;
	A *pac2 = pc;

	cout << "pab2 = " << pab2 << endl;
	cout << "pac2 = " << pac2 << endl;

	D *pd2 = new D();

	A *pab3 = static_cast<B*>((void*)pd2);
	A *pac3 = static_cast<C*>((void*)pd2);

	cout << "pab3 = " << pab3 << endl;
	cout << "pac3 = " << pac3 << endl;

	B *IB = dynamic_cast<B*>(pab);
	C *IC = dynamic_cast<C*>(pac);
	
	IB->Bfoo();
	IC->Cfoo();

	return 0;
}
 
Last edited:
It's been a long time since I dealt with the behavior of a hierarchy like that in C++... but I seem to remember something about the compiler optimizing away the additional instance of A in that case.
 
The way I understand it. Your inheritance structure is basically a V. There is an independent A at the top of both B and C and each combined into D. Unless you declare the class as virtual it will retain independent copies of that class in the hierarchy. To get the diamond you would have to declare the class hierarchy as so:

public class A
public class B : virtual public class A
public class C : virtual public class A
public class D : public class B, public class C

As for the static_cast failures, classes are just ordered objects in memory. Static_cast is very limited in the types of casts it can perform and once an ambiguity is introduced it pretty much just takes the first offset it finds in the class map. It could be right or wrong and could end up pointing into an area of memory that isn't actually the desired class. The only time to use static_cast for this type of cast is if you have only one relationship that you know will not cause these problems.

Dynamic_cast will do run time checks on the class and use tables to find if the class exists and at what offset the class is to the current pointer.

I don't think it's good to assign a pointer of a different type to a different cast.

Code:
[COLOR="Red"]A[/COLOR] *pab = static_cast<[COLOR="Red"]B[/COLOR]*>((void*)pd);

Did the compiler accept that cast?
 
The way I understand it. Your inheritance structure is basically a V. There is an independent A at the top of both B and C and each combined into D. Unless you declare the class as virtual it will retain independent copies of that class in the hierarchy. To get the diamond you would have to declare the class hierarchy as so:

public class A
public class B : virtual public class A
public class C : virtual public class A
public class D : public class B, public class C

As for the static_cast failures, classes are just ordered objects in memory. Static_cast is very limited in the types of casts it can perform and once an ambiguity is introduced it pretty much just takes the first offset it finds in the class map. It could be right or wrong and could end up pointing into an area of memory that isn't actually the desired class. The only time to use static_cast for this type of cast is if you have only one relationship that you know will not cause these problems.

Dynamic_cast will do run time checks on the class and use tables to find if the class exists and at what offset the class is to the current pointer.

I don't think it's good to assign a pointer of a different type to a different cast.

Code:
[COLOR="Red"]A[/COLOR] *pab = static_cast<[COLOR="Red"]B[/COLOR]*>((void*)pd);

Did the compiler accept that cast?

This is most likely just semantics. The hierarchy is a diamond, but you get 2 As or 1 A depending on whether you inherit A using virtual or nonvirtual.

As for the pointer, the casting is implicit and seems to be popular when upcasting.

You can compile the code yourself, but you might need a standard library or two.
 
I modified your example with classes to show the tall V vs the diamond. Notice which object gets touched in the constructors.

Code:
// classtest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include <iostream>


using namespace std;

/* Nonvirtual Tall V

A   A
|   |
B   C
 \ /
  D

*/


class A {
public:
	basic_string <char> strA;
	A() { strA+="A "; };
	virtual ~A() {};
};


class B : public A 
{
public:
	basic_string <char> strB;
	B() { A::strA+="B ";
	strB="strB is here"; };
	virtual ~B() {};
};

class C : public A 
{
public:
	C() { A::strA+="C "; };
	virtual ~C() {};
};

class D : public B, public C
{
public:
	D() { A::strA+="D "; };
	virtual ~D() {};
};

/* Virtual Diamond

  A
 / \
B   C
 \ /
  D

*/

class AV {
public:
	basic_string <char> strA;
	AV() { strA+="AV "; };
	virtual ~AV() {};
};


class BV : virtual public AV 
{
public:
	BV() { AV::strA+="BV "; };
	virtual ~BV() {};
};

class CV : virtual public AV 
{
public:
	CV() { AV::strA+="CV "; };
	virtual ~CV() {};
};

class DV : public BV, public CV
{
public:
	DV() { AV::strA+="DV "; };
	virtual ~DV() {};
};

int _tmain(int argc, _TCHAR* argv[])
{	
	D *pD = new D();

	A *pA = static_cast<B*>(pD);
	cout << "A *pA = static_cast<B*>(pD)" << endl << "Constructor = " << pA->strA << endl;

	B *pB = static_cast<B*>(pD);
	C *pC = static_cast<C*>(pD);

	pA = static_cast<A*>(pB);
	cout << "pA = static_cast<A*>(pB)" << endl << "Constructor = " << pA->strA << endl;
	pA = static_cast<A*>(pC);
	cout << "pA = static_cast<A*>(pC)" << endl << "Constructor = " << pA->strA << endl;

	DV *pDV = new DV();

	// which allows this
	AV *pAV = static_cast<AV*>(pDV);
	cout << "pAV = static_cast<AV*>(pDV)" << endl << "Constructor = " << pAV->strA << endl;

	BV *pBV = static_cast<BV*>(pDV);
	CV *pCV = static_cast<CV*>(pDV);

	pAV = static_cast<AV*>(pBV);
	cout << "pAV = static_cast<AV*>(pBV)" << endl << "Constructor = " << pAV->strA << endl;
	pAV = static_cast<AV*>(pCV);
	cout << "pAV = static_cast<AV*>(pCV)" << endl << "Constructor = " << pAV->strA << endl;

	pB = static_cast<B*>(pA); // I don't think static cast can walk up
	pB = dynamic_cast<B*>(pA); // this will work
	if(pB)
		cout << pB->strB << endl;
	pA = new A();
	pB = static_cast<B*>(pA); // this is bad there is no B in A
	pB = dynamic_cast<B*>(pA); // this is good pB will be null
	if(pB)
		cout << pB->strB << endl; // this should not execute
	delete pA;
	delete pDV;
	delete pD;

	return 0;
}

Code:
A *pA = static_cast<B*>(pD)
Constructor = A B D 
pA = static_cast<A*>(pB)
Constructor = A B D 
pA = static_cast<A*>(pC)
Constructor = A C 
pAV = static_cast<AV*>(pDV)
Constructor = AV BV CV DV 
pAV = static_cast<AV*>(pBV)
Constructor = AV BV CV DV 
pAV = static_cast<AV*>(pCV)
Constructor = AV BV CV DV 
strB is here
 
I'm reading a vanilla COM book about how to implement basic COM in C++. The trick is basically encapsulated in this piece of code (I think).

The example you gave has nothing really to do with COM. It doesn't implement a COM interface, and virtual inheritance is a standard part of C++. You shouldn't need to use virtual inheritance to implement a basic COM interface in C++.
 
The example you gave has nothing really to do with COM. It doesn't implement a COM interface, and virtual inheritance is a standard part of C++. You shouldn't need to use virtual inheritance to implement a basic COM interface in C++.

You're right, no virtual inheritance is used here.
 
Why are you C casting to void* before using static_cast? You should never do that. That's what's causing the A* to be the same.
 
Why are you C casting to void* before using static_cast? You should never do that. That's what's causing the A* to be the same.

The COM model seems to want the vtable for non virtual inheritance while acting like virtual inheritance.
 
Why are you C casting to void* before using static_cast? You should never do that. That's what's causing the A* to be the same.
Nice catch, I was puzzled a bit when I read the first post and before I read other people's responses. At first I thought maybe the compiler optimised it, but that would not explain why this happens only for static_cast.

The COM model seems to want the vtable for non virtual inheritance while acting like virtual inheritance.
Can you quote the source that led you to this conclusion? It's been a long time since I've done anything with COM, but it specifies some basic interfaces that need to be implemented and what the dll must have in it. It doesn't even prescribe a language, so it sounds weird that it requires such language-specific stuff like using non-virtual inheritance and casting stuff to void* before using static_cast to topmost base class...
 
The COM model seems to want the vtable for non virtual inheritance while acting like virtual inheritance.

Which is all handled by QueryInterface. If a client wants to cast your class to an A*, it calls QueryInterface(iidof(A)) and your implementation simply chooses which base 'A' to return. The client is not allowed to use static_cast/dynamic_cast/C-style cast to cast to A*.

Note also that this "virtual inheritance" behavior is only required for IUnknown. For any other interface request QueryInterface() can legally return different pointers on subsequent calls if it wants.

Your casting example returns the same A* because you're forcing the pointer to void* and then falsely telling the compiler that it points to a B*/C* instead of a D*. In this case it happens to point to a B (but not a C), but changing the order of inheritance in D would change that. Call B/C methods on the pointers to see the problem:

static_cast<B*>((void*)pd)->Bfoo(); <- Calls D ::Bfoo()
static_cast<C*>((void*)pd)->Cfoo(); <- Also calls D ::Bfoo() since it doesn't really point to a C.

Look at the addresses returned by static_cast with and without the (void*) if you don't understand what's happening.
 
Which is all handled by QueryInterface. If a client wants to cast your class to an A*, it calls QueryInterface(iidof(A)) and your implementation simply chooses which base 'A' to return. The client is not allowed to use static_cast/dynamic_cast/C-style cast to cast to A*.

Note also that this "virtual inheritance" behavior is only required for IUnknown. For any other interface request QueryInterface() can legally return different pointers on subsequent calls if it wants.

Your casting example returns the same A* because you're forcing the pointer to void* and then falsely telling the compiler that it points to a B*/C* instead of a D*. In this case it happens to point to a B (but not a C), but changing the order of inheritance in D would change that. Call B/C methods on the pointers to see the problem:

static_cast<B*>((void*)pd)->Bfoo(); <- Calls D ::Bfoo()
static_cast<C*>((void*)pd)->Cfoo(); <- Also calls D ::Bfoo() since it doesn't really point to a C.

Look at the addresses returned by static_cast with and without the (void*) if you don't understand what's happening.

I don't really know the nuances of void*. Why does static_cast with void* always default to B* (and how is that tied to the class hierarchy)? Is it because the D* doesn't know it points to B's vtbl that casting to a C* just causes it to 'do nothing' in a sense? In other words, B's vtbl is being interpreted as C's vtbl.
 
Last edited:
Back
Top