Log in

View Full Version : member function pointers


feisty2
15th April 2016, 18:14
so I been told (non-static) member function pointers are not exactly "pointers", these abnormal things are hard to use, hard to understand, fatter than actual pointers, and should be avoided if possible.. there're just also hell too many restrictions with them like you're not allowed to reinterpret_cast them to integers and, etc..

but anyway, f**k it, I hate to be bounded by any kind of messy and insanely complicated "rule" like this cuz rules are made to break.

and I thought, I should find a way to cast these bitches to C style function pointers, and things actually worked...
"union" stepped in when "reinterpret_cast" failed


#include <iostream>
#include <cstdlib>
using namespace std;

class _base {
public:
double val = 0.;
};

class a :virtual public _base {
public:
virtual void func() {
val = val + 1.;
cout << val << endl;
}
};

class b :virtual public _base {
public:
virtual void func() {
val = val + 2.;
cout << val << endl;
}
};

class c :public a, public b {
public:
void func() override {
val = val + 3.;
cout << val << endl;
}
};

void main() {
a *obj_a = new a;
b *obj_b = new b;
c *obj_c = new c;

union {
void(a::*fsrc_a)();
void(b::*fsrc_b)();
void(c::*fsrc_c)();
void(*fdst_a)(a *);
void(*fdst_b)(b *);
void(*fdst_c)(c *);
} tmp;

tmp.fsrc_a = &a::func;
void(*func_a)(a *) = tmp.fdst_a;
func_a(obj_a);
func_a(obj_c);
cout << reinterpret_cast<uint64_t>(func_a) << endl;

tmp.fsrc_b = &b::func;
void(*func_b)(b *) = tmp.fdst_b;
func_b(obj_b);
func_b(obj_c);
cout << reinterpret_cast<uint64_t>(func_b) << endl;

tmp.fsrc_c = &c::func;
//tmp.fsrc_c = &a::func; //also works
//tmp.fsrc_c = &b::func; //works, likewise
void(*func_c)(c *) = tmp.fdst_c;
func_c(obj_c);
cout << reinterpret_cast<uint64_t>(func_c) << endl;

delete obj_a;
delete obj_b;
delete obj_c;

system("pause");
}

http://i.imgur.com/imIXl3G.png
everything worked just as expected, like literally..
and now, f**k yeah, I can do whatever the hell I want with them cuz they have been cast to C function pointers
no warning, no error, no unexpected result...
all seemed a little bit too easy, like, I get this feeling like there's gotta be more than this...
Did I miss something?
and I actually wonder what exactly was in the 140697794319020 mem address.. if it was a function, which function exactly was it?

LoRd_MuldeR
15th April 2016, 18:25
Non-static methods don't use standard "cdecl" calling convention. They use the "thiscall" calling convention, because the member function has an implicit "this" parameter - the pointer to the instance (object) on which the method is invoked.

This also explains why a pointer to a member function is a different "type" than a normal function pointer. Actually, a pointer to a member function of class A also is a different "type" than a pointer to a member function of class B.

Also you cannot just "call" a pointer to a member function, like you can do with a normal function pointer. You have to invoke a pointer to a member function on a specific object instance. And that object must match the pointers type, of course!

See also:
https://isocpp.org/wiki/faq/pointers-to-members

feisty2
16th April 2016, 14:39
Non-static methods don't use standard "cdecl" calling convention. They use the "thiscall" calling convention, because the member function has an implicit "this" parameter - the pointer to the instance (object) on which the method is invoked.

This also explains why a pointer to a member function is a different "type" than a normal function pointer. Actually, a pointer to a member function of class A also is a different "type" than a pointer to a member function of class B.

Also you cannot just "call" a pointer to a member function, like you can do with a normal function pointer. You have to invoke a pointer to a member function on a specific object instance. And that object must match the pointers type, of course!

See also:
https://isocpp.org/wiki/faq/pointers-to-members

but for some mysterious reason
my attempt of casting member function to regular function worked..
I think it was the virtual function table at the 140697794319020 address as the polymorphism was there still even after been cast to non-class function..

LoRd_MuldeR
16th April 2016, 17:12
Casting a pointer to an incompatible type and then trying to deference such an "invalid" pointer results in undefined behavior.

And "undefined behavior" is what the name implies:

May just work as you wish, may crash (or produce weird result) right now, may appear to work 9 out 10 of ten times but crash (or produce weird result) the 10th time, may work or not based on the phase of the moon ;)

And of course "undefined behavior" means that you may get totally different outcome on different platforms or different compilers or different compiler settings or different compiler versions or... <you name it>.

(Just don't do things that are obviously asking for trouble, even though C/C++ doesn't protect you from doing such silly things)

foxyshadis
23rd April 2016, 02:10
Come back and try that with x86; it'll crash immediately. x64 works fine because there is no cdecl or thiscall in x64 anymore, just ms64call (or whatever, gcc calls it ms_abi). The callee will just automatically assume that rcx is "this" no matter what you actually pass in; if you pass an actual this, it'll work fine. If any arguments have non-trivial contructors or destructors, it'll fail. In Linux it'll fail, 32- or 64-bit. And if you actually use the vtable in any interesting way, well, just kiss it all goodbye. You basically found the one and only platform this trick actually works on.