-4

When manually calling the second() function I get a segmentation error. I think the problem is that along the way the program loses info dealing with where the first() function is in the memory for the constructed D class when moving the adress forward with +1 in int (*pfunSecond)(int) = (int (*)(int)) *(((void***)pb)[0] + 1);. I haven't had luck fixing it so far.

#include <iostream>

class B{
public:
  virtual int __cdecl first()=0;
  virtual int __cdecl second(int)=0;
};

class D: public B{
public:
  virtual int __cdecl first(){return 42;}
  virtual int __cdecl second(int x){return first()+x;}
  
};

void myFunc(B* pb){
  int (*pfunFirst)() = (int (*)()) *(((void***)pb)[0]);
  int (*pfunSecond)(int) = (int (*)(int)) *(((void***)pb)[0] + 1);

  std::cout << "Call first(): " << pfunFirst() << std::endl; 
  std::cout << "Call second(): " << pfunSecond(20) << std::endl; 
}

int main(void){
  myFunc(new D()); 
  return 0;
}
  • 3
    Here's a fix - just call `second` via `pb->second()`. Whatever you're trying to do here looks to be heavily depending on implementation details of your specific compiler. Unless you really want to dig into that (assembly code, etc), just don't do this. – TheUndeadFish Mar 14 '21 at 03:05
  • 1
    You are causing Undefined Behaviour by attempting to access the v-table at all which is an implementation detail and not accessible in standard C++. There is no correct or safe answer to this question, but if you want a quick possible explanation for the behaviour you observe, you should mention your compiler, its version, and how you are running it. – alter_igel Mar 14 '21 at 03:26

2 Answers2

2

Your problems are:

  1. Everything you are doing with the vtable is undefined behaviour. But I suppose you know that, and are willing to accept it.

I assume your layout of the virtual function table is correct; lacking a specific compiler I have no choice. The unary * seems iffy, but I don't know how vtables are laid out on your system.

  1. You are missing the implicit this pointer on your fake member function pointers.

  2. You dropped the calling convention.

so:

 int (__cdecl *pfunFirst)(B*) = (int (__cdecl *)(B*)) *(((void***)pb)[0]);
 int (__cdecl *pfunSecond)(B*,int) = (int (__cdecl *)(B*,int)) *(((void***)pb)[0] + 1);

Then do pfunFirst(b).

But my guess is your vtable layout is actually akin to

using cdecl_entry=void(__cdecl*)(void*);

struct vtable_t{
  cdecl_entry functions[1];
};
struct has_vtable{
  vtable_t const* vtable;
};
template<class R,class...Args>using cdecl_func=R(__cdecl *)(Args...);
template<class Base, class R, class...Args>
cdecl_func<R,Base*,Args...> get_vtable_entry(Base const* b, std::size_t n){
  return reinterpret_cast<cdecl_func<R,Base*,Args...>(reinterpret_cast<has_vtable const*>(b)->vtable->functions[n]);
}

this remains UB, but at least doesn't contain void***s

auto pfunFirst = get_vtable_entry<Base, int>(pb,0);
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
-1

Without looking too closely, you are calling these member functions (virtual) through non-member function pointers. The functions are being called with garbage this pointers.

Steve
  • 1,760
  • 10
  • 18