Archived

This topic is now archived and is closed to further replies.

c++ virtual table - right

This topic is 5667 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

class A { public: virtual void Test() {cout<<"A\n";} }; class Bublic A { public: virtual void Test() {cout<<"B\n";} }; int main() { A* pa = new B; pa->Test(); return 0; } Here I''m using 2 jumps :first to the virtual table and then to the right function (Test() for class B). Question: How can I call the function directly : How can I access the virtual table by myself take the function address from there and call the function ?? Thank you.

Share this post


Link to post
Share on other sites
"How can I call the function directly :
How can I access the virtual table by myself take the function address from there
and call the function ??"

That would be a clear example of anti-C++. Inline the function if the speed is a concern.

Forever trusting who we are
And nothing else matters
- Metallica

Share this post


Link to post
Share on other sites
simple: take the function pointer and you have it, virtual without the keyword is something like:

void TestA(){cout<<"A\n";};
void TestB(){cout<<"B\n";};

class A{
void (*Test)();
public:
A(void (*func_ptr)()= TestA) : Test(func_ptr) {};
};

class B : public A{
B() : A(TestB) {};
};

and you can simply call the func (only if it is const) via A::Test(), and non const ? maybe A::Test((A*)0) ? dont know, never tried ...


T2k


[edited by - T2k on June 4, 2002 8:08:37 AM]

Share this post


Link to post
Share on other sites
Here is an example of a HACK that calls a private member function of a class, circumventing the protection mechanism by using the á priori knowledge about the binary representation of classes for VC++ .
Side note: I did not write this code.

#include <iostream>
#include <iomanip>
using namespace std;
class CA
{
protected:
virtual void f1()
{
cout << "CA::f1()" << endl;
}
};
class CB : public CA
{
protected:
virtual void f1()
{
cout << "CA::f2()" << endl;
}
};
union INTANDPTR
{
int i;
void (CA::*f1p)();
};
int main(int argc, char* argv[])
{
CA * pCB = new CB;
//pCB->f1();
void (CA::*f1p)();
int ** iObject = ((int**)pCB);
int * pVtbl = (iObject[0]);
int * pVtblEntry = &pVtbl[0];

INTANDPTR iptr;
iptr.i = pVtblEntry[0];
f1p = iptr.f1p;
(pCB->*f1p)();
return 0;
}


Forever trusting who we are
And nothing else matters
- Metallica

Share this post


Link to post
Share on other sites
That is a sick hack indeed. Why do this at all? The overhead for a virtual function call is not significant enough to warrant that kind of, er..., goo. Not to mention it''s absolutely non-portable to other compilers and might not even port to other versions of VC.

Share this post


Link to post
Share on other sites
quote:
Original post by Kippesoep
That is a sick hack indeed. Why do this at all? The overhead for a virtual function call is not significant enough to warrant that kind of, er..., goo. Not to mention it''s absolutely non-portable to other compilers and might not even port to other versions of VC.




Yes, I fully agree, that is an ugly hack. I stated that pretty clearly, as you can see in my prev. post. The only reason why I saved it is that I found it somehow interesting - you know, if something is ugly enough it begins to become beautifull .

The only use I see for it is that writing/reading things like that may enhance your understanding of classes and the representation a compiler might use for them.

Forever trusting who we are
And nothing else matters
- Metallica

Share this post


Link to post
Share on other sites
Here''s how to find an class''s vtable. First, you create an object of the class. Then you cast that object''s this pointer to a void**. Then dereference once. Viola! A pointer to the class''s vtable! I''ve tried this method and it works as long as you have the objects real this pointer. All objects store a pointer to their vtable as the first member of the object in memory. But the compiler makes it seem like that pointer isn''t there. Also, if you ever bother to look through your compiler''s docs you''ll find that a method is just a regular function that takes an implicit first parameter, the this pointer. Hence the following code works:


  
class TestA
{
virtual void Test() {cout<<"Is TestA\n";};
};

class TestB: public TestA
{
virtual void Test() {cout<<"Is TestB\n";};
};

typedef void(*PTestFunc)(void* This);

int main()
{
//Call TestA::Test() from vtable.

TestA* ATest = new TestA;
void* pVtblA = *((void**)ATest);
PTestFunc pTest = reinterpret_cast<PTestFunc>(*((void**)pVtblA));
//Prints "Is TestA".

pTest(ATest);
delete ATest;
//Call TestB::Test() from vtable.

ATest = new TestB;
void* pVtblB = *((void**)ATest);
pTest = reinterpret_cast<PTestFunc>(*((void**)pVtblB));
//Prints "Is TestB".

pTest(ATest);
delete ATest;
};



It works because of the rules I just explained. They go, no matter what compiler you work with. I know because it works with my classes made in C++ Builder AND with COM objects that were undoubtedly made in Visual C++. Also, the vtable of a class stays instantiated even if there are no objects of that class. I hope you can put this knowledge to good use.

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

Share this post


Link to post
Share on other sites
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 private
gottlieb.cpp: In function `int main()'':
gottlieb.cpp:19: ANSI C++ forbids casting between pointers to functions and objects
gottlieb.cpp:26: ANSI C++ forbids casting between pointers to functions and objects
chris@ent:~$

I don''t really understand why anyone would exploit a compiler to do something forbidden by the language.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Well, here:

      
struct A {
int EatMe(void *p) { delete p; return 0; }
int Dummy() { return 0; }
};
// typedef for readability

typedef 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]

Share this post


Link to post
Share on other sites
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?");
};

Share this post


Link to post
Share on other sites