Thread overview | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 29, 2005 Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
I'm trying to register member function of one of my class as expat handler. Of course expat functions are taking pointer to a function and are declared in extern(C) scope. I understand that member functions are in fact normal functions invisibly taking "this" argument and so is this possible to do such thing in D? If answer is "yes": how and "WOW I love this language"; if answer is "no": what would be the best workaround. For clarifying this is what I'm trying to do: extern(C){ void XML_SetElementHandler(XML_Parser p, XML_StartElementHandler start, XML_EndElementHandler end); typedef void function(void *userData, XML_Char *name, XML_Char **atts) XML_StartElementHandler; typedef void function(void *userData, XML_Char *name) XML_EndElementHandler; } class Foo{ this(){ _parser = XML_ParserCreate(null); XML_SetElementHandler(_parser, cast(XML_StartElementHandler)&this.startHandle, cast(XML_EndElementHandler)&this.endHandle); } extern(C){ void startHandle(void *userData,XML_Char *name,XML_Char **atts){ printf("started: %sn",name); } void endHandle(void *userData,XML_Char *name){ printf("ended: %sn",name); } } I get: e2ir: cannot cast from void delegate(void*userData,char*name,char**atts) to void(C *)(void*userData,char*name,char**atts) e2ir: cannot cast from void delegate(void*userData,char*name) to void(C * (void*userData,char*name) Regards, -- Dawid Ciężarkiewicz | arael jid: arael@fov.pl |
January 29, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dawid Ciężarkiewicz | I think you can do that by making the class members static ... - Kris In article <cteolm$12va$1@digitaldaemon.com>, Dawid =?ISO-8859-2?Q?Ci=EA=BFarkiewicz?= says... > >I'm trying to register member function of one of my class as expat handler. Of course expat functions are taking pointer to a function and are declared in extern(C) scope. I understand that member functions are in fact normal functions invisibly taking "this" argument and so is this possible to do such thing in D? If answer is "yes": how and "WOW I love this language"; if answer is "no": what would be the best workaround. > >For clarifying this is what I'm trying to do: > >extern(C){ > > void XML_SetElementHandler(XML_Parser p, XML_StartElementHandler start, > XML_EndElementHandler end); > > typedef void function(void *userData, XML_Char *name, XML_Char **atts) > XML_StartElementHandler; > > typedef void function(void *userData, XML_Char *name) > XML_EndElementHandler; >} > >class Foo{ > > this(){ > _parser = XML_ParserCreate(null); > XML_SetElementHandler(_parser, > cast(XML_StartElementHandler)&this.startHandle, > cast(XML_EndElementHandler)&this.endHandle); > } > >extern(C){ > void startHandle(void *userData,XML_Char *name,XML_Char **atts){ > printf("started: %sn",name); > } > void endHandle(void *userData,XML_Char *name){ > printf("ended: %sn",name); > } > >} > >I get: >e2ir: cannot cast from void delegate(void*userData,char*name,char**atts) to >void(C *)(void*userData,char*name,char**atts) >e2ir: cannot cast from void delegate(void*userData,char*name) to void(C * >(void*userData,char*name) > >Regards, >-- >Dawid Ciê¿arkiewicz | arael >jid: arael@fov.pl |
January 29, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dawid Ciężarkiewicz | These are the culprit lines from your post: > cast(XML_StartElementHandler)&this.startHandle, > cast(XML_EndElementHandler)&this.endHandle); I understand what you're trying to do, as I thought the same thing myself: since D passes 'this' as the first argument to a method, why not pass along my class's methods to Expat since they happen to match the same spec. This would work amazingly well in C++ but D doesn't like it (obviously). There's a lot more going on in those lines than meets the eye: 1) D converts "&this.startHandle" into a *delegate*, which contains a reference to 'this' as well as the address of "startHandle". 2) D does not provide a means for casting a delegate into a function, so the cast to XML_StartElementHandler fails miserably. A more complete explaination of the underpinnings of delegates is here: http://www.prowiki.org/wiki4d/wiki.cgi?DocComments/Function Now what you're asking for is a hack to crack the function pointer out of a delegate. I don't advocate this in the slightest, since it's probing around a grey area that may or may not be changed in the D ABI. > import std.stdio; > > struct DelegateStruct { > void *ptr; > void *func; > }; > > class Test{ > extern(C) int myMethod(int a){ > writefln("A is %d",a); > return a+1; > } > } > > extern(C) typedef int delegate(int) TestDelegate; > extern(C) typedef int function(void*,int) TestFunctionHandle; > > int testFunction(void* thisPtr,TestFunctionHandle handle,int a){ > return handle(thisPtr,a); > } > > void main(){ > Test t = new Test(); > writefln("calling directly"); > writefln("result is: %d",t.myMethod(42)); > TestDelegate del = &t.myMethod; > DelegateStruct ds = *cast(DelegateStruct*)cast(void**)&del; > TestFunctionHandle handle = cast(TestFunctionHandle)ds.func; > writefln("calling indirectly"); > writefln("result is: %d",testFunction(cast(void*)t,handle,42)); > } I'm sure there's probably a way to use a template and/or mixin to make this much more clean. The secret here is the liberal use of "extern(C)". The default D linkage likes to throw the last argument around as EAX, which happens to be "this" for object.method calls (at least for DMD, I don't know what GDC is up to). - EricAnderton at yahoo |
January 29, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kris | Kris wrote: > I think you can do that by making the class members static ... Well, right but this way I lose information stored in "this" and I don't know on which object I work. I was trying to write multithreaded server application that communicates with xml. Each connection has to be separately parsed. I need tons of parser - each knowing almost nothing about other. -- Dawid Ciężarkiewicz | arael jid: arael@fov.pl |
January 29, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to pragma | pragma wrote: > I'm sure there's probably a way to use a template and/or mixin to make this much more clean. > > The secret here is the liberal use of "extern(C)". The default D linkage likes to throw the last argument around as EAX, which happens to be "this" for object.method calls (at least for DMD, I don't know what GDC is up to). This is nice hack, but I was thinking about something more portable, stable etc. As I understand whole hack is set on fact that EAX isn't changed and stores "this" pointer, right? Can I really depend on this in multithreaded environment using external C callback? I mean - I will register such function as handler, then many things will happen (EAX will definitely change) and then expat will call my handler and I don't think it will set EAX to something usable. If I understand this will end with random "this" and segfault. I'm not sure if I understood you so sorry if I'm jabbering. -- Dawid Ciężarkiewicz | arael jid: arael@fov.pl |
January 29, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dawid Ciężarkiewicz | Dawid Ciężarkiewicz wrote: > I'm trying to register member function of one of my class as expat handler. Of course expat functions are taking pointer to a function and are declared in extern(C) scope. I understand that member functions are in fact normal functions invisibly taking "this" argument and so is this possible to do such thing in D? If answer is "yes": how and "WOW I love this language"; if answer is "no": what would be the best workaround. I've found my personal solution. Because every class instance that need his own parser is new thread I register static member function as handler that use "getThis()" to run good instance of member function. Like this: static void StartHandler(void *userData,XML_Char *name,XML_Char **atts){ (cast(ConnectionHandler)getThis()).startHandle(userData,name,atts); } -- Dawid Ciężarkiewicz | arael jid: arael@fov.pl |
January 29, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dawid Ciężarkiewicz | In article <ctfqta$287m$1@digitaldaemon.com>, Dawid =?ISO-8859-2?Q?Ci=EA=BFarkiewicz?= says... > >pragma wrote: > >> I'm sure there's probably a way to use a template and/or mixin to make this much more clean. >> >> The secret here is the liberal use of "extern(C)". The default D linkage likes to throw the last argument around as EAX, which happens to be "this" for object.method calls (at least for DMD, I don't know what GDC is up to). > >This is nice hack, but I was thinking about something more portable, stable etc. As I understand whole hack is set on fact that EAX isn't changed and stores "this" pointer, right? As long as you use "extern(C)" in all the places illustrated, D will compile those methods/functions as 'stdcall' which is the C standard (no EAX mojo). If you try removing "extern(C)", so the compiler uses the D linkage instead, it segfaults all over the place. A hack is a hack. I don't advocate using it unless it gives you a sound advantage that can't be met otherwise. If it passes muster with testing on all your target platforms, then techically its sound code. I for one never code with a hack unless it achives a requirement, like better performance or a reduced memory footprint; and even then its a last resort. Plus I document the heck out of it (flagging it with "//HACK:") to warn other developers of what, why and how. - EricAnderton at yahoo |
January 30, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dawid Ciężarkiewicz | Hi, Dawid, Why not to use something simple as below? (It's just a scetch, never compiled it) class MyHandler { void startHandler(XML_Char *name,XML_Char **atts) { printf("started: %sn",name); } void endHandler(XML_Char *name) { printf("ended: %sn",name); } extern(C) static void _startHandler(void *userData,XML_Char *name,XML_Char **atts) { (cast(MyHandler)(userData)).startHandler(name,atts); } extern(C) static void _endHandler(void *userData,XML_Char *name) { (cast(MyHandler)(userData)).endHandler(name); } void setup(XML_Parser p) { XML_SetUserData(p, cast(void*) this); XML_SetElementHandler(p,_startHandle,_endHandle); } } You can wrap it into template if you want to use it in multiple places. |
January 30, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dawid Ciężarkiewicz | Dawid Ciężarkiewicz wrote: > I'm trying to register member function of one of my class as expat handler. > Of course expat functions are taking pointer to a function and are declared > in extern(C) scope. I understand that member functions are in fact normal > functions invisibly taking "this" argument and so is this possible to do > such thing in D? If answer is "yes": how and "WOW I love this language"; if > answer is "no": what would be the best workaround. > > For clarifying this is what I'm trying to do: > > extern(C){ > > void XML_SetElementHandler(XML_Parser p, XML_StartElementHandler start, > XML_EndElementHandler end); > > typedef void function(void *userData, XML_Char *name, XML_Char **atts) > XML_StartElementHandler; > > typedef void function(void *userData, XML_Char *name) > XML_EndElementHandler; > } > > class Foo{ > > this(){ > _parser = XML_ParserCreate(null); > XML_SetElementHandler(_parser, > cast(XML_StartElementHandler)&this.startHandle, > cast(XML_EndElementHandler)&this.endHandle); > } > > extern(C){ > void startHandle(void *userData,XML_Char *name,XML_Char **atts){ > printf("started: %sn",name); > } > void endHandle(void *userData,XML_Char *name){ > printf("ended: %sn",name); > } > > } You need to create an extern(C) wrapper for the class function, and store the 'this' pointer in a struct, which you use as the userdata. Here's the bare bones of an example, with some nice translation of C strings to D strings thrown in, to boot: // override this class for your own uses... class ExpatParser { void startHandler(XML_char[] name,XML_Char[][] attrs) { return; } void endHandler (XML_char[] name) { return; } this() { _parser = XML_ParserCreate(null); ExpatParserUserData *ptr = new ExpatParserUserData[1]; ptr.ref = this; XML_SetUserData(_parser, cast(void*)ptr); XML_SetElementHandler(_parser, &startHandler_c, &endHandler_c); }; }; struct ExpatParserUserData { ExpatParser ref; }; extern(C) void startHandler_c(void *userdata_void, XML_Char *name_c, XML_Char **attrs_c) { ExpatParserUserData *userdata = cast(ExpatParserUserData*)userdata_void; XML_Char[] name = name_c[0..strlen(name_c)]; XML_Char[][] attrs; for( ; *attrs_c != null; attrs_c++) attrs ~= (*attrs_c)[0..strlen(*attrs_c)]; userdata.ref.startHandler(name,attrs); } you can write your own endHandler_c() the same way... |
January 30, 2005 Re: Passing member function pointer to external C functions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Fedoniouk | Andrew Fedoniouk wrote: > Hi, Dawid, > > Why not to use something simple as below? Brilinat! I didn't know that user that is working like this. Thank you. This is very good answer to my problem. -- Dawid Ciężarkiewicz | arael jid: arael@fov.pl |
Copyright © 1999-2021 by the D Language Foundation