c++ virtual table - right

Started by
16 comments, last by lavi 21 years, 10 months ago
quote:It works because of the rules I just explained. They go, no matter what compiler you work with

Not mine.
chris@ent:~$ g++ gottlieb.cpp gottlieb.cpp:6: warning: all member functions in class `TestA'' are privategottlieb.cpp: In function `int main()'':gottlieb.cpp:19: ANSI C++ forbids casting between pointers to functions and objectsgottlieb.cpp:26: ANSI C++ forbids casting between pointers to functions and objectschris@ent:~$ 

I don''t really understand why anyone would exploit a compiler to do something forbidden by the language.
Advertisement
And there''s no mandatory implementation of vtables. You could potentially have hardware-accelerated vtables on an embedded platform that are indexed by something other than an address offset.
Here's the code to a program I wrote to test this. It compiled and ran under C++ Builder 6 with ANSI compliance mode (only ANSI C++) in Windows ME.


      #include <conio.h>#include <iostreams.h>using namespace std;class TestA{public: char* Value; virtual void Test()};void TestA::Test(){char strToDisplay[100];strcopy(&strToDisplay,Value);strcat(&strToDisplay," is ATest's TestA::Value field.");cout << &strToDisplay << endl;};class TestB: public TestA{public: void Test();};void TestB::Test(){char strToDisplay[100];strcopy(&strToDisplay,Value);strcat(&strToDisplay," is ATest's TestB::Value field.");cout << &strToDisplay << endl;};typedef void(*PTestFunc)(void* This);int main(){//Call TestA::Test() from the vtable.TestA* ATest = new TestA; ATest->Value = "*((void**)this) == pVtable";void* pVTable = *((void**)ATest);PTestFunc Test = *((void**)pVTable); /*Only if a correct this pointer is passed will Test() be able   to access the field Value*/Test((void*)ATest);delete ATest;//Call TestB::Test() from the vtable.ATest = new TestB; ATest->Value = "ATest->Test() == Test(ATest)";void* pVTableB = *((void**)ATest);Test = *((void**)pVTableB);Test((void*)ATest);delete ATest;Test = NULL;pVTable = NULL;pVTableB = NULL;return 0;};      


There it is. Try to get it to compile on another compiler, then test what I said. A standard implementation of vtables is neccessary because if there wasn't one how would objects from two different compilers call each other's virtual methods? This program shows how to access the vtable of a class and call methods from it.

void Signature(void* Pointer)
{
PObject(Pointer)->ShowMessage("Why do we need so many pointers?");
};

[edited by - Eli Gottlieb on June 6, 2002 3:48:41 PM]
void Signature(void* Pointer){PObject(Pointer)->ShowMessage("Why do we need so many pointers?");};
quote:Original post by Eli Gottlieb
A standard implementation of vtables is neccessary because if there wasn't one how would objects from two different compilers call each other's virtual methods?

They can't. Try it. This is why inter-language or inter-compiler libraries expose their methods as extern "C", because C calling convention is standard at the byte level. The C++ name mangling alone will surely differ between compilers, not to mention vtable implementations.

Section 10.8c of the ARM (sorry, don't have the spec) offers two completely different ways to implement vtables. On top of that, they're just suggestions.

C++ is a behavioral specification, not an implementation specification. You cannot write an implementation spec without having to write one for every platform that chooses to adopt it. Cross-platform source is one of the founding tenets of C and C++. Depending on implementation details in your code is a sure-fire way to make it non-portable. Depending on implementation details to "make" programs across compilers or platforms portable is obviously a contradiction in terms.

COM does specify a binary-level protocol, so that's a solution if this is a problem.

{edit: A solution, not THE solution. Wow, the hubris of me..}

[edited by - Stoffel on June 6, 2002 4:28:21 PM]
quote:Original post by Eli Gottlieb
A standard implementation of vtables is neccessary because if there wasn''t one how would objects from two different compilers call each other''s virtual methods?

heehe, THEY DONT!
I think you can tell Builder and Delphi to emit obj files that work with MSVC, but that type of static interoperablity isn''t part of the C++ spec.

That''s the big deal about COM - it''s a binary spec for the vtable layout - that let''s mutliple OO languages talk to each as well as different OO C++ compilers. Mixin RPC, yield DCOM, and you have have the staging ground for the creation of CORBA as a knee-jerk, omg-we-have-to-do something to attack this COM phenemon.


Anyway, there is a portable way to do what you want, lavi. Use a method pointer.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Anyone able to explain how to USE a method pointer?

void Signature(void* Pointer)
{
PObject(Pointer)->ShowMessage("Why do we need so many pointers?");
};
void Signature(void* Pointer){PObject(Pointer)->ShowMessage("Why do we need so many pointers?");};
Well, here:

      struct A {    int EatMe(void *p) { delete p; return 0; }    int Dummy() { return 0; }};// typedef for readabilitytypedef int (A::*fnptr)(void *);main() {    A a;    fnptr p = &A::EatMe;    int x = a.*p(0);    // or without typedef:    int (A::*q)() = &A::Dummy;    int y = a.*q();    // or call by pointer:    B *b = &a    int z = b->*q();}      



[edited by - IndirectX on June 7, 2002 2:28:08 PM]
---visit #directxdev on afternet <- not just for directx, despite the name
Oh. OK, I tried my proram with a Unix compiler and a Macintosh compiler. On both it didn''t work. But I did find it''s possible to hand-implement a lookup table for virtual methods in just the way I wrote. Sorry. I did notice that somehow though, OBJ files I built on Unix could have their class''s virtual methods called from Macintosh.

void Signature(void* Pointer)
{
PObject(Pointer)->ShowMessage("Why do we need so many pointers?");
};
void Signature(void* Pointer){PObject(Pointer)->ShowMessage("Why do we need so many pointers?");};

This topic is closed to new replies.

Advertisement