c++ brutal cast = safe?

Started by
18 comments, last by Schrompf 14 years, 10 months ago
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);}
Advertisement
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;}
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.
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.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

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.
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.
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.
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.
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.
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.
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.

This topic is closed to new replies.

Advertisement