2

I have two classes B and D

class B{
public:
  virtual int prva()=0;
  virtual int druga(int)=0;
};

class D: public B{
public:
  virtual int prva(){return 42;}
  virtual int druga(int x){return prva()+x;}
};

My task is to create a function that takes pointer to object B nad prints returning values of methods 'prva' and 'druga' without using their name (accessing through virtual table)

I wrote the following code that successfully prints the returning value of method 'prva', but then fails to do the same for the second method 'druga'

typedef int (*PTRFUN1)(void);
typedef int (*PTRFUN2)(int);

void function (B* var){
    int* vptr =  *(int**)var;
    PTRFUN1 fun1 = (PTRFUN1)vptr[0];
    PTRFUN2 fun2 = (PTRFUN2)vptr[1];
    pprintf("Prva:%d\n", fun1());
    printf("Druga:%d\n", fun2(2));
}

int main(void){
    B *pb = new D();
    function(pb);
}

This code executes printing: "Prva:42"

It fails to call prva() inside of 'druga' and I can't figure out why.

Additionally, if I simply remove call of prva() and let the body just be return x, the method 'druga' will always return "42" or whatever number i let 'prva' return no matter what argument I try to send through fun2()

Any ides what am I doing wrong here, and how should I access methods?

L.A.
  • 25
  • 4
  • Any reason why you are not just calling `B->druga(2)` inside `function` ? – ChrisD Apr 03 '20 at 11:25
  • That's the point of the task, I'm not allowed to use calls prva() and druga(), I should access them through virtual pointer – L.A. Apr 03 '20 at 11:28
  • Given that everything about this is platform dependent (assuming it's even well defined for that platform) you should really specify which OS/platform you are aiming this at. – G.M. Apr 03 '20 at 11:31
  • Isn't this undefined/unspecified behavior? Are you sure you can access class members this way? What happens if the compiler adds padding? – Thomas Sablik Apr 03 '20 at 11:32
  • You will need to know the right offsets into the vtable for this, which is compiler dependent as others have mentioned. – ChrisD Apr 03 '20 at 11:32
  • @L.A. Are you sure that the task isn't to call `D::prva` without mentioning `D` instead? That would make sense. – eerorika Apr 03 '20 at 12:07

2 Answers2

2

It is not possible to access the virtual table in standard C++. In fact, the language doesn't even have a concept of what a virtual table is. Virtual table is a specific (and popular) way to implement dynamic dispatch.

Any ides what am I doing wrong here

When you indirect through vptr, the behaviour of the program is undefined.

Some implementations may have way to access the table in C++, but there is no standard way. If your compiler does not, then only way is to study how the compiler implements it, the write the program to access it in assembly.

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

@eerorika's answer is 100% correct and you should never user anything that causes undefined behaviour.

However if you still want to go this route, you could probably fix your code by passing the B pointer as the first parameter to prva & druga.

Member Functions like prva and druga have an implicit this argument that you need to pass to the function. So something along the lines of:

typedef int (*PTRFUN1)(void* that);
typedef int (*PTRFUN2)(void* that, int);

void function (B* var){
    void** vptr =  *(void***)var;
    PTRFUN1 fun1 = (PTRFUN1)vptr[0];
    PTRFUN2 fun2 = (PTRFUN2)vptr[1];
    printf("Prva:%d\n", fun1(var));
    printf("Druga:%d\n", fun2(var, 2));
}

should work (still compiler-dependent and 100% undefined behaviour)

Here's a Fiddle for it.

Edit:

There is also a way you can invoke the functions without using their name that isn't undefined behavior.
You can use Pointer to Member Functions for this:

typedef int (B::*PTRFUN1)();
typedef int (B::*PTRFUN2)(int x);

void function (B* var){
    PTRFUN1 fun1 = &B::prva;
    PTRFUN2 fun2 = &B::druga;
    printf("Prva:%d\n", (var->*fun1)());
    printf("Druga:%d\n", (var->*fun2)(2));
}

This will not cause UB and still satisfies your requirement of not using the function names to call them.

Here's a Fiddle for the Pointer-to-Member-Function Variant.

Turtlefight
  • 9,420
  • 2
  • 23
  • 40