473,836 Members | 1,457 Online
Bytes | Software Development & Data Engineering Community
+ 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->QueryInterfa ce ((void **) &pINetB);
pINetB->NB();

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

return 0;
}

Jul 24 '08 #1
4 1632
Hi,

ha**********@gm ail.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->QueryInterfa ce ((void **) &pINetB);
You directly assigned a pointer to Network to a pointer to INetworkB.
Marcel
Jul 24 '08 #2
ha**********@gm ail.com wrote in news:54675b4d-190f-472f-83b9-
5e**********@i2 4g2000prf.googl egroups.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<I NetworkA *>
(this); return *ppOut == 0; }
int QueryInterface2 (void **ppOut) { *ppOut = dyanamic_cast<I NetworkB *>
(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(v oid **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<I NetworkA *>
(this); return *ppOut == 0; }

int QueryInterface2 (void **ppOut) { *ppOut = dyanamic_cast<I NetworkB *>
(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->QueryInterfa ce ((void **) &pINetA);
pINetA->NA();

/* Get the INetworkB interface using QueryInterface2 () */
pINetA->QueryInterface 2 ((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**********@gm ail.com wrote in news:d1a75289-561f-4e38-8b31-
de**********@a1 g2000hsb.google groups.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::s tring, 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::mak e_pair("INetwor kA", static_cast
<INetworkA *>(this)));
m_InterfaceMap. insert(std::mak e_pair("INetwor kB", static_cast
<INetworkB *>(this)));
}

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

int QueryInterface( std::string const & key, void **ppOut)
{
int found = 0;
InterfaceMap::i terator 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->QueryInterfa ce ("INetworkA" , (void **) &pINetA);
pINetA->NA();

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

INetworkA * pINetA2;

pINetB->QueryInterfa ce ("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<INe tworkA *>(this);
m_InterfaceMap[INetworkB] = static_cast<INe tworkB *>(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
2371
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 can refresh the page a million times and each time it only sends 1 email. But its the first time the page is loaded (like I open a new IE browser and go to the script), it sends 2 emails (duplicates). The entire script is below: <?php $to...
2
2001
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 backup, I click on OK but no progress blocks show up in the window showing you the status of the backup. The completion window pops up saying that the DB has been backed up. OK--Fine, maybe the backup is really quick. Then, through Explorer, I look...
1
2108
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 content of an untyped DataSet. This is to be done in the "DropDown" Event (since the dataset is empty at program start). If I go with this:
0
1163
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 content of an untyped DataSet. This is to be done in the "DropDown" Event (since the dataset is empty at program start). If I go with this:
6
1417
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 is just gone. One second I am typing in the IDE and the next I am in whatever application was behind it on the desktop and my keystroke are going to that active application. There is nothing in the application or security logs to indicate...
82
5399
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 > emacs. I've gotten a book on lisp, and I must say lisp is the ugliest > looking language syntax wise. What is up with this: (defun(foo()). (DEFUN FOO () NIL) > What were the lisp authors thinking? Why did Stallman use lisp in
2
1173
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: "puzzlecracker" <ironsel2...@gmail.com> - Find messages by this author
11
13735
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, there's an "Edit" link. So the page itself looks something like this: <HTML><HEAD><TITLE>blah</TITLE></HEAD><BODY> <!-- TEXT STARTS HERE --> <H1>Hello World!</H1> <P>More stuff here...</P>
3
5493
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 @dTest='2001-1-1 1:1:1:997' SELECT @dTest
0
1836
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 code SHOULD do: - read new (=updated) licensetext from file $license_path then - read and modify recursively all files from $current_dir, replacing old
0
9825
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, people are often confused as to whether an ONU can Work As a Router. In this blog post, weíll explore What is ONU, What Is Router, ONU & Routerís main usage, and What is the difference between ONU and Router. Letís take a closer look ! Part I. Meaning of...
0
9671
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 effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10846
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, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10551
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 tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
10595
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 Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
6979
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 then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5650
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 last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5828
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4458
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system

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.