Sign in to follow this  
Makaan

c++ brutal cast = safe?

Recommended Posts

Consider the following scenario: class myClass {... void method(); }; typedef void (*proc)(void *); ... myClass object; void *ptr = &object;// cast object to void * proc foo = &myClass::method; // cast class method to type 'proc' (brutal) and now if i do 'foo(ptr);' the 'method' from 'object' will be called. this is equivalent with object.method(); In visual studio 2008 this is allowed and it works with no problems as far as i tested this.Is this absolutely safe with any class type and on other compilers? Thanks Raxvan.

Share this post


Link to post
Share on other sites
Quote:
Original post by Makaan
In visual studio 2008 this is allowed and it works with no problems as far as i tested this.Is this absolutely safe with any class type and on other compilers?
That it works at all is a small miracle, and purely an implementation artefact. This behaviour is neither standard, nor portable, and there are probably many situations where it won't work even under your compiler.

If you actually want to do something like this, you can use member function pointers, or better yet, std::mem_fun or boost::bind.

Share this post


Link to post
Share on other sites
Quote:
Original post by Makaan
Consider the following scenario:

class myClass {... void method(); };

typedef void (*proc)(void *);
...

myClass object;
void *ptr = &object;// cast object to void *
proc foo = &myClass::method; // cast class method to type 'proc' (brutal)

The line proc foo = &myClass::method shouldn't compile. You are missing a (proc*) in the above line?

In any case, yes, the fact that it works is a complete accident and implementation detail of your particular compiler.

An easy way around this is:

class myClass {
// ...
void method();
static void method_func( myClass* self ) { self->method(); }
// ...
};

myClass object;
void* ptr = &object; // implicitly get a void pointer to object.
proc foo = myClass::method_func; // these are type-compatible... probably


As mentioned in the above link, calling conventions _could_ vary between a static member function and a raw C interface that you might end up passing proc to.

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
Quote:
Original post by Makaan
In visual studio 2008 this is allowed and it works with no problems as far as i tested this.Is this absolutely safe with any class type and on other compilers?
That it works at all is a small miracle, and purely an implementation artefact. This behaviour is neither standard, nor portable, and there are probably many situations where it won't work even under your compiler.

If you actually want to do something like this, you can use member function pointers, or better yet, std::mem_fun or boost::bind.


thanks for the links, i will try to rethink my design.

It it not a miracle at all the fact that this works, because as far as i know for any c++ implementation uses the same design for OOP(i don't know if there are any exceptions). Any class method is actualy a normal function that has the first argument a pointer to the object (the hidden 'this' keyword) . I can't think of a problem if i manually pass that pointer into the stack and the call the method, the function will simply get fooled.


Edit:
NotAYakk you are right the line proc foo = &myClass::method; will not compile ,
it is just to represent my idea, i fount a way to force the compiler to make that brutal casting.

The static method is iteresting. thanks

Share this post


Link to post
Share on other sites

class Foo {
public:
virtual void method() = 0;
};

class Bar {
public:
virtual void method() {}
};

class Baz: public Bar, public Foo {
public:
virtual void method() { printf("Baz!\n"); }
};
typedef void(Foo::*FooMethod)();
typedef void(Bar::*BarMethod)();
typedef void(Baz::*BazMethod)();

int main(int, char**) {
printf("%d %d %d == %d?\n", (int)sizeof(FooMethod), (int)sizeof(BarMethod), (int)sizeof(BazMethod), sizeof(void*));

return 0;
}

What does the above print out on your system? (not compiled, just tossed out there).

IIRC, not all methods pointers sizeof(void*), even on relatively standard compilers.

Share this post


Link to post
Share on other sites
Quote:
Original post by Makaan
It it not a miracle at all the fact that this works, because as far as i know for any c++ implementation uses the same design for OOP(i don't know if there are any exceptions). Any class method is actualy a normal function that has the first argument a pointer to the object (the hidden 'this' keyword) . I can't think of a problem if i manually pass that pointer into the stack and the call the method, the function will simply get fooled.
Except for the fact that that isn't guaranteed to work on any compiler. You're relying on implementation details which you have no control over and, quite frankly, know nothing about (as they might vary from system to system). Hacking your code up so that it relies on such implementation details is not standard, is not defined, and is not safe.

Share this post


Link to post
Share on other sites
> Any class method is actualy a normal function that has the first
> argument a pointer to the object (the hidden 'this' keyword) .
There are actually two problems with that:
1. depending on the compiler/ABI, member function pointer can be represented differently in memory; member function pointers can be as large as 16 bytes on some compilers.
2. depending on the compiler/ABI, calling convention for regular and member functions may differ; for example, MSVC defaults to thiscall for member functions (this is passed in ecx register, arguments are passed via stack).

Share this post


Link to post
Share on other sites
Quote:
Original post by Makaan
NotAYakk you are right the line proc foo = &myClass::method; will not compile ,
it is just to represent my idea, i fount a way to force the compiler to make that brutal casting.

How did you get that to compile? MSVC seems to reject all the obvious approaches.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Quote:
Original post by Makaan
NotAYakk you are right the line proc foo = &myClass::method; will not compile ,
it is just to represent my idea, i fount a way to force the compiler to make that brutal casting.

How did you get that to compile? MSVC seems to reject all the obvious approaches.


typedef void (*SimpleProc)(void *);

template< class T >
Callback(T * MyObject,void (T:: *proc)())
{
obj= (void *)MyObject;

void (T:: *aux)() = proc;
void * adr = ((void *)&aux);
foo = *((SimpleProc *)adr); //uber brutal cast
}
and voila , 'foo(obj)' :)

Share this post


Link to post
Share on other sites
A portable way of doing this is something similar to:
template < class T, void (T::*proc)() >
static void foo(void * ptr) {
T * instance = (T*)ptr;
(instance->*proc)();
}

typedef void (*fp)(void *);

int main(int argc, char **argv)
{
fp f = &foo<Foo, &Foo::bar>;

Foo test;
f(&test);
}

Share this post


Link to post
Share on other sites
You didn't actually do a lot of testing before you decided that it "works" did you? I can get MSVC to crash on that code without even trying.

#include <string>
#include <iostream>

typedef void (*SimpleProc)(void *);

template< class T >
void Callback(T * MyObject,void (T:: *proc)()) {
void * obj= (void *)MyObject;

void (T:: *aux)() = proc;
void * adr = ((void *)&aux);
SimpleProc foo = *((SimpleProc *)adr); //uber brutal cast
foo(obj);
};

struct Foo {
std::string data;
void print(void) {
std::cout << data << std::endl;
}
};

int main(int, char **) {
Foo f = { "Fred" };

f.print(); // works fine
Callback(&f, &Foo::print); // First-chance exception at 0x63fe8dcb (msvcp90d.dll)
// in temp.exe: 0xC0000005: Access violation reading location 0x2ee90000.

return 0;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
You didn't actually do a lot of testing before you decided that it "works" did you? I can get MSVC to crash on that code without even trying.
*** Source Snippet Removed ***


Well this just proves that it is not safe at all to do that :) ,
I tested the std::string inside that and it doesnt work, my tests were extremely similar but the Foo class only included int and float values, for that the code seems to work as far as i saw.

Share this post


Link to post
Share on other sites
Quote:
Original post by Makaan
Quote:
Original post by SiCrane
You didn't actually do a lot of testing before you decided that it "works" did you? I can get MSVC to crash on that code without even trying.
I tested the std::string inside that and it doesnt work, my tests were extremely similar but the Foo class only included int and float values, for that the code seems to work as far as i saw.
You didn't mention that you were testing with POD (plain old data) types. It probably works far more often on POD types than types with constructors, etc. and it is almost guaranteed not to work on types with inheritance/virtual functions.

Amusingly enough, even SiCrane's example using std::string works fine on g++ 4.0.1 on an Intel Mac.

Share this post


Link to post
Share on other sites
Actually, it doesn't even work for POD types on MSVC. Changing data from a string to an int and initializing it with 50 produces the output "50" and "39145" on one run.

Share this post


Link to post
Share on other sites
There are ways to do what the OP suggests. All that is obviously a bit slippery (although very convenient).

The complexity behind the internals of member function pointers is quite surprising, especially when considering multiple virtual inheritance. It's much more than simply a function with a hidden 'this' parameter.

Share this post


Link to post
Share on other sites
Quote:
Original post by Makaan
Any class method is actualy a normal function that has the first argument a pointer to the object (the hidden 'this' keyword). I can't think of a problem if i manually pass that pointer into the stack and the call the method, the function will simply get fooled.


Check the section labeled "Implementations of Member Function Pointers". While in theory it'd be quite possible to build a C++ compiler where what you say is true, in practice compilers don't actually do what you'd hope. There's lots of extra offsetting and general bit fiddlery going on.

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey

Check the section labeled "Implementations of Member Function Pointers". While in theory it'd be quite possible to build a C++ compiler where what you say is true, in practice compilers don't actually do what you'd hope. There's lots of extra offsetting and general bit fiddlery going on.


Thanks , but i have abandoned this solution for my problem and went for something 100% safe.

Quote:
Original post by SiCrane
Actually, it doesn't even work for POD types on MSVC. Changing data from a string to an int and initializing it with 50 produces the output "50" and "39145" on one run.


i don't know how but i can say that it works on my computer , i used it for a couple of minutes until i realized it can't be safe , and it worked flawlessly , not once but many times over.

Share this post


Link to post
Share on other sites
Quote:
Original post by Makaan
Consider the following scenario:

class myClass {... void method(); };

typedef void (*proc)(void *);
...

myClass object;
void *ptr = &object;// cast object to void *
proc foo = &myClass::method; // cast class method to type 'proc' (brutal)


and now if i do
'foo(ptr);' the 'method' from 'object' will be called.
this is equivalent with object.method();


This is incredibly not-working. The type of &myClass::method is void(myClass::*)(), which is not only fundamentally different from void(*)(void*) because of being a member function pointer (member function pointers and free function pointers are entirely incompatible), but doesn't even have the same parameter list. Or were you hoping that 'this' would somehow magically become the void* parameter?

You could do:


void wrapper(void* thisIsAMyClassPointerTrustMe) {
((MyClass*)(thisIsAMyClassPointerTrustMe))->method();
}

proc foo = wrapper;


But why would you want to throw away type safety like that?

Use boost::function. Making things work sanely in this area involves heavy lifting. You want to use real tools.

Share this post


Link to post
Share on other sites
I like the thread title, though...


void* p = brutal_cast<void*> (somePointer);


Not only that it changes the type, it also pummels the pointer a bit before returning! I'd buy it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this