5

Here is my code.

class IService {
};

class X_Service {
public:
    void service1() {
        std::cout<< "Service1 Running..."<<std::endl;
    }
};


int main() {
    IService service;
    auto func = reinterpret_cast<void (IService::*)()>(&X_Service::service1);
    (service.*(func))();
    return 0;
}

I don't understand how this works. I didn't inherit IService and didn't create a X_Service object but it works. Can someone explain this?

Mahesh Nepal
  • 1,385
  • 11
  • 16
s3ms
  • 79
  • 4

4 Answers4

8

Your confusion probably comes from the misunderstanding that because something compiles and runs without crashing, it "works". Which is not really true.

There are many ways you can break the rules of the language and still write code that compiles and runs. By using reinterpret_cast here and making an invalid cast you have broken the rules of the language, and your program has Undefined Behaviour.

That means it can seem to work, it can crash or it can just do something completely different from what you intended.

In your case it seems to work, but it's still UB and the code is not valid.

super
  • 12,335
  • 2
  • 19
  • 29
  • Thanks for answer. Yes I know this code UB but just I was wonder how can it possible even. Main code not like this. I tried simulation reflection and worked something like this. :) – s3ms Feb 23 '20 at 17:23
3

Under the hood your compiler will turn all these functions into machine code that is basically just jumps to certain addresses in memory and then executing commands stored there.

A member function is just a function that has in addition to local variables and parameters, a piece of memory that stores the address of the class object. That piece of memory holds the address you are accessing when you use the this keyword.

If you call a member function on a wrong object or nullptr, then you basically just make the this pointer point to something invalid.

Your function doesn't access this, which is the reason your program doesn't blow up.

That said, this is still undefined behavior, and anything could happen.

Max Vollmer
  • 8,412
  • 9
  • 28
  • 43
2

So, I had some fun and manipulated the code a bit. This is also an empirical answer. There are a lot of pitfalls that risk stack corruption with this way of doing things, so I changed the code a bit to make it to where stack corruption does not occur but kind of show what it happening.

#include <iostream>

class IService {
public:
    int x;
};

class X_Service {

public:
    int x;
    void service1() {
        this->x = 65;
        std::cout << this->x << std::endl;
    }
};


int main() {
    IService service;
    auto func = reinterpret_cast<void (IService::*)()>(&X_Service::service1);
    (service.*(func))();
    std::cout << service.x << std::endl;
    std::cin.get();
    X_Service derp;
    (derp.service1)();
    std::cout << derp.x << std::endl;


    return 0;
}

So from the outset, auto gave you the power to make a none type safe pointer void (IService::*)()also the instance of the object itself is this-> regardless of what member function of whatever class you are stealth inheriting from. The only issue is that the first variable of the instance is interpreted based on the first variable of the class you are stealth inheriting from, which can lead to stack corruption if the type differs.

Ways to get cool output but inevitably cause stack corruption, you can do the following fun things.

class IService {
public:
    char x;
};

Your IDE will detect stack corruption of your IService object, but getting that output of

65
A

is kind of worth it, but you will see that issues will arise doing this stealth inheritance.

I'm also on an 86x compiler. So basically my variable are all lined up. Say for instance if I add an int y above int x in Iservice, this program would output nonsense. Basically it only works because my classes are binary compatible.

Marcus
  • 71
  • 4
  • hi @Marcus i like to understand this syntax "auto func = reinterpret_cast(&X_Service::service1); " could you please explain what this is really doing? – RaHuL Feb 24 '20 at 05:11
  • @RaHuL To be honest that's above my paygrade. You'd probably need to talk to a compiler guru. How I sort of understand this Undefined Behavior, is that you are effectively creating a void function pointer from one class's namespace to another class's method. Maybe. – Marcus Feb 24 '20 at 06:34
1

When you reinterpret_cast a function or member function pointer to a different type, you are never allowed to call the resulting pointer except if you cast it back to its original type first and call through that.

Violating this rule causes undefined behavior. This means that you loose any language guarantee that the program will behave in any specific way, absent additional guarantees from your specific compiler.

reinterpret_cast is generally dangerous because it completely circumvents the type system. If you use it you need to always verify yourself by looking at the language rules, whether the cast and the way the result will be used is well-defined. reinterpret_cast tells the compiler that you know what you are doing and that you don't want any warning or error, even if the result will be non-sense.

walnut
  • 21,629
  • 4
  • 23
  • 59