Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call base virtual method by pointer to function from derived class

I need to call the base method A::foo() from derived class by pointer.

#include <iostream>
struct A{
    virtual void foo() { std::cout << "A::foo()" << std::endl; }
};

struct B:A{
    virtual void foo() { std::cout << "B::foo()" << std::endl; }
    void callBase(void (A::*f)()){
        (this->*f)();
    }
};

int main(){
    B* p=new B();
    p->callBase(&A::foo);
}

This code output "B::foo". Is it possible to call A::foo() by pointer to method?

like image 977
Egor Dudyak Avatar asked Dec 26 '17 06:12

Egor Dudyak


2 Answers

Well, you can do something similar using some tricks with overwriting the value of this. You probably should never try to do that though, vtable pointers aren't meant to be modified by hand.

To do what you described, we need to have the pointer to A's vtable. Our object p has only pointer to B's vtable, so we need to store second pointer in a field within A's constructor.

Here is the code:

#include <iostream>
struct A{
    virtual void foo() { std::cout << "A::foo()" << std::endl; }
    int *a_vtable_ptr;
    // First, save value of A's vtable pointer in a separate variable.
    A() { a_vtable_ptr = *(int**)this; }
};

struct B:A{
    virtual void foo() { std::cout << "B::foo()" << std::endl; }
    void callBase(void (A::*f)()){
        int *my_vtable_ptr = *(int**)this;
        // Then modify vtable pointer of given object to one that corresponds to class A.
        *(int**)this = a_vtable_ptr;
        (this->*f)(); // Call the method as usual.
        // Restore the original vtable pointer.
        *(int**)this = my_vtable_ptr;
    }
};

// Function main() is not modified.
int main(){
    B* p=new B();
    void (A::*f)() = &A::foo;
    p->callBase(f);
}

Output:

A::foo()

Process finished with exit code 0
like image 183
Radek Avatar answered Nov 16 '22 20:11

Radek


Virtual methods are designed to implement polymorphism and pointers to virtual methods supports their polymorphic behavior. But you given the possibility to call the base method by explicitly calling p->A::foo().

So if you want to call base method by pointer, you should make it non-virtual (as @PasserBy mentioned in comments).

Code example:

struct A {
    virtual void foo() { std::cout << "A::foo()" << std::endl; }
    void bar() { std::cout << "A::bar()" << std::endl; }
    void callBase(void (A::*f)()) { (this->*f)(); }
};

struct B : A {
    virtual void foo() { std::cout << "B::foo()" << std::endl; }
    void bar() { std::cout << "B::bar()" << std::endl; }
};

int main()
{
    A* p = new B();
    p->foo();
    p->bar();
    p->callBase(&A::foo);
    p->callBase(&A::bar);
    p->A::foo();
    p->A::bar();
}

Output:

B::foo()
A::bar()
B::foo()
A::bar()
A::foo()
A::bar()
like image 40
0xmono Avatar answered Nov 16 '22 20:11

0xmono