www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - How to convert a member function pointer to a normal function pointer

reply smithfox <ssm.fox gmail.com> writes:
How to convert a member function pointer to a normal function pointer?
Just like Object Pascal Language's Classes.MakeObjectInstance(xxx);

I think it is a classic problem when we try to write object-oriented codes
based on an unalterable C framework(such as window gui sdk);

Thanks
Apr 29 2007
next sibling parent smithfox <ssm.fox gmail.com> writes:
Up
Apr 29 2007
prev sibling next sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"smithfox" <ssm.fox gmail.com> wrote in message 
news:f12959$1ufe$1 digitalmars.com...
 I think it is a classic problem when we try to write object-oriented codes 
 based on an unalterable C framework(such as window gui sdk);
I know a lot of Windows API callbacks allow you to pass a void* as a context pointer which is passed to your callback every time it's called back. What you can do is create a shim function which is extern(Windows) and takes the void* context pointer, then creates a delegate which does the calling, like so: class A { void foo() { writefln("foo"); } } extern(Windows) void callback(void* ctx) { void delegate() dg; dg.funcptr = &A.foo; dg.ptr = ctx; dg(); } void main() { A a = new A(); someCallbackAPIFunc(&callback, cast(void*)a); } This assumes that someCallbackAPIFunc takes the address of a callback function and a void pointer as the context. Then, when your callback function is called, you just set up your delegate to use A.foo as the function and the context as the pointer (which is your instance of A), and then you call the delegate.
Apr 30 2007
prev sibling next sibling parent Russell Lewis <webmaster villagersonline.com> writes:
smithfox wrote:
 How to convert a member function pointer to a normal function pointer?
 Just like Object Pascal Language's Classes.MakeObjectInstance(xxx);
 
 I think it is a classic problem when we try to write object-oriented codes
based on an unalterable C framework(such as window gui sdk);
I think that you need to create a wrapper function, just like in C++. D's only advantage here is that you can use a function literal, if that happens to make your code easier to read.
Apr 30 2007
prev sibling next sibling parent "d" <d nomail.com> writes:
 How to convert a member function pointer to a normal function pointer?
 Just like Object Pascal Language's Classes.MakeObjectInstance(xxx);
Okay. To understand what the OP wants, you need to understand how Delphi works. Right, Delphi's Object Pascal differentiates between function pointers and method pointers. So: type TWndMethod = procedure(var Message: TMessage) of object; // a method pointer TWndFunc = procedure(var Message: TMessage); //a function pointer The OP also mentions "Classes.MakeObjectInstance(xxx);... " Well, in my version of Delphi, this is in the Forms unit (a unit is like a module - i.e. a source file or I guess block of code.) So Classes.Xxxx or Forms.Xxx is a bit like a namespace (but not really) though in practice, it doesn't work a lot like that in Delphi for Win32. Anyway, back to the story. So MakeObjectInstance takes a TWndMethod, which is a non static Method pointer, and then converts it to a pointer to a standard Win32 WinProc ala: function StdWndProc(Window: HWND; Message, WParam: Longint; LParam: Longint): Longint; stdcall; All the MakeObjectInstance call really does is make a stub that can be called that "magically" puts in the hidden "self" param. It only works for the type TWndMethod. What this allows the programmer to do is create an Class that has a method that gets all Windows messages sent to the handle the user sets up when thety pass the pointer to the WinProc to the CreateWindow API call. Nothing more spectacular really. Okay, it also fills out he TMessage instance, but all that really contains if the Message, LParam, WParam etc from the standard WinProc call. It is probably possible to take the Delphi code and translate the technique to D - though legally that would probably be against the VCL sourcecode license... M
May 03 2007
prev sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
smithfox wrote:
 How to convert a member function pointer to a normal function pointer?
 Just like Object Pascal Language's Classes.MakeObjectInstance(xxx);
 
 I think it is a classic problem when we try to write object-oriented codes
based on an unalterable C framework(such as window gui sdk);
 
 Thanks
As Jarrett mentioned, most C apis should allow you to pass a void* or something to your callback, which you can use to construct a delegate. Since you didn't actually mention any specific functions, I can't really tell if that's the case here. Here's a snippet I used for my expat SAX parser code, which had to set up extern(C) callbacks that called into object delegates. If you have a member function called "expat_StartElement", you would construct a regular old C callback function pointer that calls that member using "&xcb!(expat_StartElement)".
 /**
  * This template will be used to create handler functions.
  *
  * The reason we need this is that expat expects a regular extern(C)
  * function that is passed a single void* argument.  Sadly, all we
  * have are delegates that don't quite work like that.
  *
  * This template will basically wrap a particular argument list, and
  * use the user-data void* as the object reference.
  */
 private
 extern(C)
 ReturnType!(T) xcb(alias T)(
         void* data,
         ParameterTypeTuple!(T) args)
 {
     static const name = eval!(membername((&T).stringof));

     auto reader = cast(ExpatReader)data;
     static if( is( ReturnType!(T) == void ) )
         mixin("reader."~name~"(args);");
     else
         return mixin("reader."~name~"(args)");
 }

 private
 template eval(char[] strT)
 {
     const eval = strT;
 }

 private
 char[] membername(char[] str)
 {
     auto sppos = ctfind(str, ' ');
     auto prpos = ctfind(str, '(');
     if( prpos == -1 )
         prpos = str.length;
     return str[sppos+1..prpos];
 }

 private
 int ctfind(char[] str, dchar ch)
 {
     foreach( i, c ; str )
         if( c == ch )
             return i;
     return -1;
 }
Obviously, you need to know what methods you want to hook up at compile time :) If, for some reason, the callback doesn't let you pass a pointer to it, or you need to rig this up at compile time, then things get a little tricker. If you don't mind writing *severely* non-portable code, you can always generate a machine code shim at runtime. If you grab a copy of the NASM manual, that will tell you the byte encoding for most of the x86 instructions, and you can fill in any holes with a copy of the Pentium 4 manual. Then, all you need to do is generate machine code to put the delegate's ptr into EAX, then call the delegate; things get messy if you need to take function arguments. :P Here's a very simple example I got working today. Of course, if there's *any* other way of doing this, I'd go with that first, since runtime-generated code can be an absolute bastard to debug. ;) ubyte[] gen_wrapper(void delegate() dg) { scope buf = new MemoryStream; scope ass = new X86Assembler(buf); with( ass ) { // dg.ptr *MUST* go into EAX. dg.funcptr can go wherever you // like, just so long as you can use it with a call instruction. mov(Reg32.EAX, cast(size_t)dg.ptr); mov(Reg32.EDX, cast(size_t)dg.funcptr); call(Reg32.EDX); ret(); // This really is redundant, but it makes me feel better :) int_(3); } return buf.data.dup; } void main() { void hello() { writefln("Hello, world!"); } auto hello_dg = &hello; // ArrayEx is just a wrapper around VirtualAlloc/VirtualFree. I'm // using those to make sure the memory I use is set to allow both // writing and execution. ArrayEx!(ubyte) buf; scope(exit) delete buf; { auto tbuf = gen_wrapper(hello_dg); buf = new ArrayEx!(ubyte)(tbuf, Protection.ReadWriteExecute); delete tbuf; } writefln("Bytecode:"); for( size_t i=0; i<buf.length; i++ ) { if( i%16 != 0 ) writef(" "); writef("%02x", buf[i]); if( (i+1)%16 == 0 ) writef("\n"); } writefln(""); auto hello_fn = cast(void function())buf.ptr; writefln("Before hello_dg()"); hello_dg(); writefln("Between hello_dg() and hello_fn()"); hello_fn(); writefln("After hello_fn()"); } Outputs: Bytecode: b8 00 00 00 00 ba ec 24 40 00 ff d2 c3 cd 03 Before hello_dg() Hello, world! Between hello_dg() and hello_fn() Hello, world! After hello_fn() Hope that this was, if not at least helpful, a little interesting :) -- Daniel "compilers are for sissies" -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
May 03 2007
parent "d" <d nomail.com> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message
news:f1cva7$2jc2$1 digitalmars.com...
 As Jarrett mentioned, most C apis should allow you to pass a void* or
 something to your callback, which you can use to construct a delegate.
 Since you didn't actually mention any specific functions, I can't really
 tell if that's the case here.
Don't think it is - at least not if OP really wants MakeObjectInstance, which is specific to the Win32 API WndProc definition... see my lengthy reply above.
May 03 2007