473,470 Members | 1,920 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

Weird V-table issue

In the example below, a single object (Network) is implementing 2
interfaces (INetworkA and INetworkB). Both of these interfaces derive
from the IBase interface (similar to the IUnknown interface of the COM
world). Network object also implements the QueryInterface methods from
IBase interface.

In the main function, I am doing a QueryInterface on the INetworkA
interface. It returns me the pointer to the second interface
INetworkB. On this pointer, if I perform a function of the INetworkB,
the control is jumping into one of INetworkA's functions.

Can anyone tell me why this is happening? The problem goes away if I
am doing a cast when I am returning the interface pointer. But I am
still not convinced that this is the correct behavior by the compiler.
How does casting affect a V-table of a object pointer?

The below program compiles and runs fine on a GNU C++ compiler.
#include <stdio.h>

/* Base Interface */
class IBase
{
public:
virtual int F1() = 0;
virtual int QueryInterface (void **ppOut) = 0;
virtual int QueryInterface2 (void **ppOut) = 0;
};

/* Some interface */
class INetworkA: public IBase
{
public:
virtual int NA() = 0;
virtual int QueryInterface (void **ppOut) = 0;
};

/* Another interface */
class INetworkB: public IBase
{
public:
virtual int NB() = 0;
};

/* This object implements both interfaces */
class Network : public INetworkA,
public INetworkB
{
public:
int F1() { printf("Network::F1()\n"); }
int NA() { printf("Network::NA()\n"); }
int NB() { printf("Network::NB()\n"); }
int QueryInterface (void **ppOut) {*ppOut = this; return 0;}
int QueryInterface2 (void **ppOut) {*ppOut = (INetworkB *) this;
return 0;}
};
int main()
{
Network *netObj = new Network();
INetworkA *pINetA = netObj;
INetworkB *pINetB = netObj;

pINetA->NA();
pINetB->NB();

/* Weirdness happens here */
/* Get the INetworkB interface using QueryInterface() with no
casting */
pINetA->QueryInterface ((void **) &pINetB);
pINetB->NB();

/* Get the INetworkB interface using QueryInterface2() which does
casting*/
pINetA->QueryInterface2 ((void **) &pINetB);
pINetB->NB();

return 0;
}

Jul 24 '08 #1
4 1608
Hi,

ha**********@gmail.com schrieb:
Can anyone tell me why this is happening? The problem goes away if I
am doing a cast when I am returning the interface pointer. But I am
still not convinced that this is the correct behavior by the compiler.
How does casting affect a V-table of a object pointer?
you are happily to void* and then to another pointer type. This is invalid.

The below program compiles and runs fine on a GNU C++ compiler.
Don't use C-style reinterpret casts.
Don't use void* in C++ programs (except for wrapping C library functions).

And while we are at it do not use char* in C++ programs (You did not do
that in the example, of course). But const char* is fine.

If you follow this advices you won't run into that invalid code without
a compiler warning or error.

int QueryInterface (void **ppOut) {*ppOut = this; return 0;}
int QueryInterface2 (void **ppOut) {*ppOut = (INetworkB *) this;
Furthermore these functions are completely superflous, since every class
pointer to an instance of Network is implicitely convertable to a
pointer to all of it's public base classes.

If you want to restrict the interface availibility by some runtime
conditions, derive protected from the interface and write a strongly
typed QueryInterface function that returns NULL or throws an exception
in case of an error.

And even more, when the same interface is implemented at different
levels of the class hierarchy, you should always derive virtual from
the interface. This gives the Java like behavior.

/* Weirdness happens here */
/* Get the INetworkB interface using QueryInterface() with no
casting */
pINetA->QueryInterface ((void **) &pINetB);
You directly assigned a pointer to Network to a pointer to INetworkB.
Marcel
Jul 24 '08 #2
ha**********@gmail.com wrote in news:54675b4d-190f-472f-83b9-
5e**********@i24g2000prf.googlegroups.com:

By an large you don't need this kind of complexity in C++, but assuming for
the moment that you did....
>
The below program compiles and runs fine on a GNU C++ compiler.
#include <stdio.h>

/* Base Interface */
class IBase
{
public:
virtual int F1() = 0;
virtual int QueryInterface (void **ppOut) = 0;
virtual int QueryInterface2 (void **ppOut) = 0;
};

/* Some interface */
class INetworkA: public IBase
{
public:
virtual int NA() = 0;
virtual int QueryInterface (void **ppOut) = 0;
You don't really need to mention this again. It will be inherited from
IBase.
};

/* Another interface */
class INetworkB: public IBase
{
public:
virtual int NB() = 0;
};

/* This object implements both interfaces */
class Network : public INetworkA,
public INetworkB
{
public:
int F1() { printf("Network::F1()\n"); }
int NA() { printf("Network::NA()\n"); }
int NB() { printf("Network::NB()\n"); }
int QueryInterface (void **ppOut) {*ppOut = this; return 0;}
this will reflect the type of the interface used to call the function. So,
you will always return the same interface which which you called the
QueryInterface() method.
int QueryInterface2 (void **ppOut) {*ppOut = (INetworkB *) this;
return 0;}
For both this method and the previous, you should use dynamic_cast to cast
the pointer to the appropriate type. dynamic_cast will return 0 if the
interface isn't supported,so you can have your return value actually mean
something as well. So, you might have:

int QueryInterface(void **ppOut) { *ppOut = dyanamic_cast<INetworkA *>
(this); return *ppOut == 0; }
int QueryInterface2(void **ppOut) { *ppOut = dyanamic_cast<INetworkB *>
(this); return *ppOut == 0; }
};

I haven't actually compiled the above, but it should work.

The way you have this implemented you don't really accomplish much. It is
much easier to just use static_cast or dynamic_cast directly to get the
interface you want. If the idea as to restrict access to interfaces, then
you need to use private inheritance instead of public and provide methods
to get you going. For example,

class IBase
{ public:
virtual int QueryInteface(void **ppOut) = 0;
virtual int QueryInterface2(void **ppOut) = 0;
};

class INetworkA
{
public:
virtual int NA() = 0;
virtual int QueryInterface2(void **ppOut) = 0;
};

class INetworkB
{
public:
virtual int NB() = 0;
virtual int QueryInterface(void **ppOut) = 0;
};

class Network : public IBase, private INetworkA, private INetworkB
{
public:
int F1() { printf("Network::F1()\n"); }
int QueryInterface(void **ppOut) { *ppOut = dyanamic_cast<INetworkA *>
(this); return *ppOut == 0; }

int QueryInterface2(void **ppOut) { *ppOut = dyanamic_cast<INetworkB *>
(this); return *ppOut == 0; }
private:
int NA() { printf("Network::NA()\n"); }
int NB() { printf("Network::NB()\n"); }
};
int main()
{
Network *netObj = new Network();
// INetworkA *pINetA = netObj; now fails
// INetworkB *pINetB = netObj; now fails

// pINetA->NA();
// pINetB->NB();

INetworkA * pINetA;
INetworkB * pINetB;

/* Get the INetworkA interface using QueryInterface() */
netObj->QueryInterface ((void **) &pINetA);
pINetA->NA();

/* Get the INetworkB interface using QueryInterface2() */
pINetA->QueryInterface2 ((void **) &pINetB);
pINetB->NB();

// Now complete the circuit
INetworkA * pNetA2;
pINetB->QueryInterface((void **) &pINetA2);
pINetA2->NA();

return 0;
}

The above restricts the interfaces so that you have to use one of your
QueryInterfacex() methods and allows movement back and forth via those
methods. The only thing that IBase does for you is makes both the
QueryInterface() methods available from the Network object. I don't know
if that is what you were after, but I hope it helps some anyway.

joe
Jul 25 '08 #3
Thanks for all the replies. I do understand that the standard way of
doing things is to use virtual inheritance and dynamic casting.
However, I would like to clarify that I am working on an embedded
environment (and due to some other reasons which I do not want to
detail here), and I am restricted to using non-virtual inheritance and
no dynamic casting.

Further clarification on QueryInterface: I would like to define a
generic method which all the other interfaces inherit. All the
implementation objects implement the QueryInterface() method to return
the interfaces they they implement. In such a scenario, the out-
parameter of the QueryInterface has to be a void **. Is there any
better method of accomplishing what I am trying to do.

Thanks in advance,
Harsh
Jul 28 '08 #4
ha**********@gmail.com wrote in news:d1a75289-561f-4e38-8b31-
de**********@a1g2000hsb.googlegroups.com:
Thanks for all the replies. I do understand that the standard way of
doing things is to use virtual inheritance and dynamic casting.
However, I would like to clarify that I am working on an embedded
environment (and due to some other reasons which I do not want to
detail here), and I am restricted to using non-virtual inheritance and
no dynamic casting.

Further clarification on QueryInterface: I would like to define a
generic method which all the other interfaces inherit. All the
implementation objects implement the QueryInterface() method to return
the interfaces they they implement. In such a scenario, the out-
parameter of the QueryInterface has to be a void **. Is there any
better method of accomplishing what I am trying to do.

Thanks in advance,
Harsh
I think the problem is that someone somewhere needs to know the layout
of the virtual tables in the actual class that you are using. That is
what dynamic_cast brings to the table. I suppose you can keep your own
table of pointers to the various interfaces and provide a means to
access it from every interface. Then you could avoid the use of
dynamic_cast. Maybe someone else will have a more clever idea for you
though. The following seems to work:

#include <stdio.h>
#include <string>
#include <map>

class IBase
{ public:
virtual int QueryInterface(std::string const & key , void **ppOut) =
0;
protected:
typedef std::map<std::string, void *InterfaceMap;
};

class INetworkA
{
public:
virtual int NA() = 0;
virtual int QueryInterface(std::string const & key, void **ppOut) =
0;
};

class INetworkB
{
public:
virtual int NB() = 0;
virtual int QueryInterface(std::string const & key, void **ppOut) =
0;
};

class Network : public IBase, private INetworkA, private INetworkB
{
public:

Network()
{
m_InterfaceMap.insert(std::make_pair("INetworkA", static_cast
<INetworkA *>(this)));
m_InterfaceMap.insert(std::make_pair("INetworkB", static_cast
<INetworkB *>(this)));
}

int F1() { printf("Network::F1()\n"); }

int QueryInterface(std::string const & key, void **ppOut)
{
int found = 0;
InterfaceMap::iterator itFind = m_InterfaceMap.find(key);
if (itFind != m_InterfaceMap.end()) {
*ppOut = itFind->second;
found = 1;
}
return found;
}

private:
int NA() { printf("Network::NA()\n"); return 0;}
int NB() { printf("Network::NB()\n"); return 0;}
InterfaceMap m_InterfaceMap;
};

int main()
{
Network *netObj = new Network();

INetworkA * pINetA;
INetworkB * pINetB;
netObj->QueryInterface ("INetworkA", (void **) &pINetA);
pINetA->NA();

pINetA->QueryInterface ("INetworkB", (void **) &pINetB);
pINetB->NB();

INetworkA * pINetA2;

pINetB->QueryInterface ("INetworkA", (void **) &pINetA2);
pINetA2->NA();

return 0;
}
Obviously, you don't need to use a string as a key, and you can use any
sort of lookup structure you want. This is for illustrative purposes
only. :) In fact, if you have a small fixed number of interfaces, I
would consider an array of void * s as the InterfaceMap and an enum
value as the key. Such as:

enum Interfaces
{
INetworkA,
INetworkB,
InterfaceCount
};

....

class Network : public IBase, private INetworkA, private INetworkB
{
void * m_InterfaceMap[InterfaceCount];
public:
Network() {
m_InterfaceMap[INetworkA] = static_cast<INetworkA *>(this);
m_InterfaceMap[INetworkB] = static_cast<INetworkB *>(this);
// zero out any empty slots
}

int QueryInterface(Interfaces i, void **ppOut) { *ppOut =
m_InterfaceMap[i]; return *ppOut != 0; }

.....

HTH

joe

Jul 29 '08 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Gabriel Afana | last post by:
I have a simple php script to just send email.....When I first load the script in a browser...it sends 2 emails. Why?? The weird thing is then when I refresh the page, it send only one email...I...
2
by: jwbeaty | last post by:
Here's a weird one. I'm running SQL Server 7 and when I run a backup something weird happens. When I perform the backup via Enterprise Manager by right clicking on the database I want to...
1
by: Kaneda | last post by:
Hello everyone! I have some weird(?) problems, and I am not quite sure if there are due to my errors or maybe a limitation in the .Net framework. I have a ComboBox I need to fill with the...
0
by: Kaneda | last post by:
Hello everyone! I have some weird(?) problems, and I am not quite sure if there are due to my errors or maybe a limitation in the .Net framework. I have a ComboBox I need to fill with the...
6
by: Unicorn | last post by:
I can only say this is a weird problem! I will be sitting there typing away in the code window, when the whole IDE just vanishes without a trace. I get no error messages and no indication, it...
82
by: nobody | last post by:
Howdy, Mike! mikecoxlinux@yahoo.com (Mike Cox) wrote in message news:<3d6111f1.0402271647.c20aea3@posting.google.com>... > I'm a C++ programmer, and have to use lisp because I want to use >...
2
by: puzzlecracker | last post by:
Fixed font - Proportional font string initilization WHY? All 2 messages in topic - view as tree puzzlecracker Jan 29, 10:47 pm show options Newsgroups: comp.lang.c From:...
11
by: Les Paul | last post by:
I'm trying to design an HTML page that can edit itself. In essence, it's just like a Wiki page, but my own very simple version. It's a page full of plain old HTML content, and then at the bottom,...
3
by: aling | last post by:
Execute following T-SQL within Queary Analyzer of SQL Server 2000: ======================================= DECLARE @dTest DATETIME SET @dTest='2001-1-1 1:1:1:991' SELECT @dTest SET...
0
by: P Pulkkinen | last post by:
Dear all, sorry, i know this code is far little too long to debug here, but there is really annoying logical error. If someone debugs this, I really offer warm virtual handshake. What this...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
1
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
muto222
php
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.